X-Git-Url: https://code.grnet.gr/git/aquarium/blobdiff_plain/01f6cdb8b78ca29ad76cd663ad90d18b511fb59e..c66d56a7b64c619e49ae2bc2b0ddcf9905fbbbd9:/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala?ds=inline diff --git a/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala b/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala index aba9589..474c7cd 100644 --- a/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala +++ b/src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala @@ -37,24 +37,25 @@ package gr.grnet.aquarium.charging.bill import com.ckkloverdos.props.Props import com.ckkloverdos.resource.FileStreamResource -import gr.grnet.aquarium.converter.{CompactJsonTextFormat, StdConverters} +import gr.grnet.aquarium.converter.StdConverters import gr.grnet.aquarium.logic.accounting.dsl.Timeslot import gr.grnet.aquarium.message.avro.{AvroHelpers, MessageHelpers} -import gr.grnet.aquarium.message.avro.gen.{ChargeslotMsg, WalletEntryMsg, UserStateMsg} -import gr.grnet.aquarium.store.memory.MemStoreProvider +import gr.grnet.aquarium.message.avro.gen._ import gr.grnet.aquarium.util.json.JsonSupport -import gr.grnet.aquarium.{Aquarium, ResourceLocator, AquariumBuilder} +import gr.grnet.aquarium.{Real, Aquarium, ResourceLocator, AquariumBuilder} import java.io.File import java.util.concurrent.atomic.AtomicLong import scala.collection.immutable.TreeMap import scala.collection.mutable.ListBuffer +import scala.Some import gr.grnet.aquarium.policy.ResourceType +import java.util /* * @author Prodromos Gerakios */ - +/* case class ChargeEntry(val id:String, val unitPrice:String, val startTime:String, @@ -71,15 +72,15 @@ class EventEntry(val eventType : String, case class ResourceEntry(val resourceName : String, - //val resourceType : String, - //val unitName : String, + val resourceType : String, + val unitName : String, val totalCredits : String, val totalElapsedTime : String, val totalUnits : String, val details : List[EventEntry]) extends JsonSupport { - var unitName = "EMPTY_UNIT_NAME" - var resourceType = "EMPTY_RESOURCE_TYPE" + //var unitName = "EMPTY_UNIT_NAME" + //var resourceType = "EMPTY_RESOURCE_TYPE" } case class ServiceEntry(val serviceName: String, @@ -91,8 +92,6 @@ case class ServiceEntry(val serviceName: String, ) extends JsonSupport {} -abstract class AbstractBillEntry - extends JsonSupport {} class BillEntry(val id:String, val userID : String, @@ -103,10 +102,10 @@ class BillEntry(val id:String, val endTime:String, val bill:List[ServiceEntry] ) - extends AbstractBillEntry {} - + extends JsonSupport {} +*/ -object AbstractBillEntry { +object BillEntryMsg { private[this] val counter = new AtomicLong(0L) private[this] def nextUIDObject() = counter.getAndIncrement @@ -120,25 +119,117 @@ object AbstractBillEntry { val dend = cal.getTime Timeslot(dstart,dend) } */ + private [this] def newChargeEntry(id:String, + unitPrice:String, + startTime:String, + endTime:String, + elapsedTime:String, + units:String, + credits:String) : ChargeEntryMsg = { + val msg = new ChargeEntryMsg + msg.setId(id) + msg.setUnitPrice(unitPrice) + msg.setStartTime(startTime) + msg.setEndTime(endTime) + msg.setElapsedTime(elapsedTime) + msg.setUnits(units) + msg.setCredits(credits) + msg + } + + private[this] def newEventEntry(eventType : String, + details : java.util.List[ChargeEntryMsg]) : EventEntryMsg = { - private[this] def toChargeEntry(c:ChargeslotMsg) : (ChargeEntry,Long,Double) = { + val msg = new EventEntryMsg + msg.setEventType(eventType) + msg.setDetails(details) + msg + } + + private [this] def newResourceEntry( resourceName : String, + resourceType : String, + unitName : String, + totalCredits : String, + totalElapsedTime : String, + totalUnits : String, + details : java.util.List[EventEntryMsg]) : ResourceEntryMsg = { + val msg = new ResourceEntryMsg + msg.setResourceName(resourceName) + msg.setResourceType(resourceType) + msg.setUnitName(unitName) + msg.setTotalCredits(totalCredits) + msg.setTotalElapsedTime(totalElapsedTime) + msg.setTotalUnits(totalUnits) + msg.setDetails(details) + msg + } + + private[this] def newServiceEntry( serviceName: String, + totalCredits : String, + totalElapsedTime : String, + totalUnits:String, + unitName:String, + details: java.util.List[ResourceEntryMsg] + ) : ServiceEntryMsg = { + val msg = new ServiceEntryMsg + msg.setServiceName(serviceName) + msg.setTotalCredits(totalCredits) + msg.setTotalElapsedTime(totalElapsedTime) + msg.setTotalUnits(totalUnits) + msg.setUnitName(unitName) + msg.setDetails(details) + msg + } + + + private[this] def newBillEntry( id:String, + userID : String, + status : String, + remainingCredits:String, + deductedCredits:String, + startTime:String, + endTime:String, + bill: java.util.List[ServiceEntryMsg] + ) : BillEntryMsg = { + val msg = new BillEntryMsg + msg.setId(id) + msg.setUserID(userID) + msg.setStatus(status) + msg.setRemainingCredits(remainingCredits) + msg.setDeductedCredits(deductedCredits) + msg.setStartTime(startTime) + msg.setEndTime(endTime) + msg.setBill(bill) + msg + } + + private[this] def javaList[A](l:A*) : java.util.List[A] = { + val al = new java.util.ArrayList[A]() + l.foreach(al.add(_)) + al + } + + + private[this] def toChargeEntry(c:ChargeslotMsg) : (ChargeEntryMsg,Long,Real) = { val unitPrice = c.getUnitPrice.toString val startTime = c.getStartMillis.toString val endTime = c.getStopMillis.toString val difTime = (c.getStopMillis - c.getStartMillis).toLong - val unitsD = (c.getCreditsToSubtract/c.getUnitPrice) + val unitsD = (Real(c.getCreditsToSubtract)/Real(c.getUnitPrice)) val credits = c.getCreditsToSubtract.toString - (new ChargeEntry(counter.getAndIncrement.toString,unitPrice, + (newChargeEntry(counter.getAndIncrement.toString,unitPrice, startTime,endTime,difTime.toString,unitsD.toString,credits),difTime,unitsD) } - private[this] def toEventEntry(eventType:String,c:ChargeslotMsg) : (EventEntry,Long,Double) = { + + + private[this] def toEventEntry(eventType:String,c:ChargeslotMsg) : (EventEntryMsg,Long,Real) = { val (c1,l1,d1) = toChargeEntry(c) - (new EventEntry(eventType,List(c1)),l1,d1) + (newEventEntry(eventType,javaList(c1)),l1,d1) } - private[this] def toResourceEntry(w:WalletEntryMsg) : ResourceEntry = { + private[this] def toResourceEntry(w:WalletEntryMsg) : ResourceEntryMsg = { assert(w.getSumOfCreditsToSubtract==0.0 || MessageHelpers.chargeslotCountOf(w) > 0) val rcType = w.getResourceType.getName val rcName = rcType match { @@ -148,7 +239,6 @@ object AbstractBillEntry { MessageHelpers.currentResourceEventOf(w).getInstanceID } val rcUnitName = w.getResourceType.getUnit - val eventEntry = new ListBuffer[EventEntry] val credits = w.getSumOfCreditsToSubtract val eventType = //TODO: This is hardcoded; find a better solution rcType match { @@ -176,26 +266,26 @@ object AbstractBillEntry { ///FIXME: val elapsedTime = w.getChargeslots.asScala.foldLeft() //c.getStopMillis - c.getStartMillis var totalElapsedTime = 0L - var totalUnits = 0.0D + var totalUnits = Real.Zero + val eventEntry = new java.util.ArrayList[EventEntryMsg]() //new ListBuffer[EventEntry] for { c <- w.getChargeslots.asScala }{ if(c.getCreditsToSubtract != 0.0) { //Console.err.println("c.creditsToSubtract : " + c.creditsToSubtract) val (e,l,u) = toEventEntry(eventType.toString,c) - eventEntry += e + eventEntry.add(e) //eventEntry += e totalElapsedTime += l totalUnits += u } } //Console.err.println("TOTAL resource event credits: " + credits) - val re = new ResourceEntry(rcName,/*rcType,rcUnitName,*/credits.toString,totalElapsedTime.toString, - totalUnits.toString,eventEntry.toList) - re.unitName = rcUnitName - re.resourceType = rcType - re + /*val re =*/ newResourceEntry(rcName,rcType,rcUnitName,credits.toString,totalElapsedTime.toString, + totalUnits.toString,eventEntry) + //re.unitName = rcUnitName + //re.resourceType = rcType + //re } - private[this] def resourceEntriesAt(t:Timeslot,w:UserStateMsg) : (List[ResourceEntry],Double) = { - val ret = new ListBuffer[ResourceEntry] + private[this] def resourceEntriesAt(t:Timeslot,w:UserStateMsg) : (java.util.List[ResourceEntryMsg],Double) = { var sum = 0.0 //Console.err.println("Wallet entries: " + w.walletEntries) import scala.collection.JavaConverters.asScalaBufferConverter @@ -204,75 +294,95 @@ object AbstractBillEntry { for { i <- walletEntries } Console.err.println("WALLET ENTRY\n%s\nEND WALLET ENTRY".format(i.toJsonString)) Console.err.println("End wallet entries")*/ + val ret = new java.util.ArrayList[ResourceEntryMsg]()//new ListBuffer[ResourceEntry] for { i <- walletEntries} { val referenceTimeslot = MessageHelpers.referenceTimeslotOf(i) if(t.contains(referenceTimeslot) && i.getSumOfCreditsToSubtract.toDouble != 0.0){ /*Console.err.println("i.sumOfCreditsToSubtract : " + i.sumOfCreditsToSubtract)*/ if(i.getSumOfCreditsToSubtract.toDouble > 0.0D) sum += i.getSumOfCreditsToSubtract.toDouble - ret += toResourceEntry(i) + ret.add(toResourceEntry(i)) //ret += toResourceEntry(i) } else { val ijson = AvroHelpers.jsonStringOfSpecificRecord(i) val itimeslot = MessageHelpers.referenceTimeslotOf(i) - Console.err.println("IGNORING WALLET ENTRY : " + ijson + "\n" + - t + " does not contain " + itimeslot + " !!!!") + // Console.err.println("IGNORING WALLET ENTRY : " + ijson + "\n" + + // t + " does not contain " + itimeslot + " !!!!") } } - (ret.toList,sum) + (ret,sum) } - private[this] def aggregateResourceEntries(re:List[ResourceEntry]) : List[ServiceEntry] = { - def addResourceEntries(a:ResourceEntry,b:ResourceEntry) : ResourceEntry = { - assert(a.resourceName == b.resourceName) - val totalCredits = (a.totalCredits.toDouble+b.totalCredits.toDouble).toString - val totalElapsedTime = (a.totalElapsedTime.toLong+b.totalElapsedTime.toLong).toString - val totalUnits = (a.totalUnits.toDouble+b.totalUnits.toDouble).toString - val ab = a.copy(a.resourceName/*,a.resourceType,a.unitName*/,totalCredits,totalElapsedTime,totalUnits, - a.details ::: b.details) - ab.unitName = a.unitName - ab.resourceType = a.resourceType - ab + private[this] def aggregateResourceEntries(re:java.util.List[ResourceEntryMsg]) : java.util.List[ServiceEntryMsg] = { + def addResourceEntries(a:ResourceEntryMsg,b:ResourceEntryMsg) : ResourceEntryMsg = { + assert(a.getResourceName == b.getResourceName) + val totalCredits = (a.getTotalCredits.toDouble+b.getTotalCredits.toDouble).toString + val totalElapsedTime = (a.getTotalElapsedTime.toLong+b.getTotalElapsedTime.toLong).toString + val totalUnits = (a.getTotalUnits.toDouble+b.getTotalUnits.toDouble).toString + val msg = new ResourceEntryMsg + + val details = new java.util.ArrayList[EventEntryMsg](a.getDetails) + details.addAll(b.getDetails) + msg.setResourceName(a.getResourceName) + msg.setResourceType(a.getResourceType) + msg.setUnitName(a.getUnitName) + msg.setTotalCredits(totalCredits) + msg.setTotalElapsedTime(totalElapsedTime) + msg.setTotalUnits(totalUnits) + msg.setDetails(details) + msg + /*/*val ab =*/ a.copy(a.getResourceName,a.getResourceType,a.getUnitName,totalCredits,totalElapsedTime,totalUnits, + {a.getDetails.addAll(b.details); a}) + //ab.unitName = a.unitName + //ab.resourceType = a.resourceType + //ab*/ } - val map0 = re.foldLeft(TreeMap[String,ResourceEntry]()){ (map,r1) => - map.get(r1.resourceName) match { - case None => map + ((r1.resourceName,r1)) - case Some(r0) => (map - r0.resourceName) + - ((r0.resourceName, addResourceEntries(r0,r1))) + import scala.collection.JavaConverters.asScalaBufferConverter + val map0 = re.asScala.foldLeft(TreeMap[String,ResourceEntryMsg]()){ (map,r1) => + map.get(r1.getResourceName) match { + case None => map + ((r1.getResourceName,r1)) + case Some(r0) => (map - r0.getResourceName) + + ((r0.getResourceName, addResourceEntries(r0,r1))) } } - val map1 = map0.foldLeft(TreeMap[String,List[ResourceEntry]]()){ case (map,(_,r1)) => - map.get(r1.resourceType) match { - case None => map + ((r1.resourceType,List(r1))) - case Some(rl) => (map - r1.resourceType) + ((r1.resourceName,r1::rl)) + val map1 = map0.foldLeft(TreeMap[String,List[ResourceEntryMsg]]()){ case (map,(_,r1)) => + map.get(r1.getResourceType) match { + case None => map + ((r1.getResourceType,List(r1))) + case Some(rl) => (map - r1.getResourceType) + ((r1.getResourceType,r1::rl)) } } - map1.foldLeft(List[ServiceEntry]()){ case (ret,(serviceName,resList)) => + import scala.collection.JavaConverters.seqAsJavaListConverter + map1.foldLeft(List[ServiceEntryMsg]()){ case (ret,(serviceName,resList)) => val (totalCredits,totalElapsedTime,totalUnits) = resList.foldLeft((0.0D,0L,0.0D)){ case ((a,b,c),r) => - (a+r.totalCredits.toDouble, - b+r.totalElapsedTime.toLong, - c+r.totalUnits.toDouble + (a+r.getTotalCredits.toDouble, + b+r.getTotalElapsedTime.toLong, + c+r.getTotalUnits.toDouble )} - new ServiceEntry(serviceName,totalCredits.toString, + newServiceEntry(serviceName,totalCredits.toString, totalElapsedTime.toString,totalUnits.toString, - resList.head.unitName,resList) :: ret - } + resList.head.getUnitName,resList.asJava) :: ret + }.asJava } - def addMissingServices(se:List[ServiceEntry],re:Map[String,ResourceType]) : List[ServiceEntry]= - se:::(re -- se.map(_.serviceName).toSet).foldLeft(List[ServiceEntry]()) { case (ret,(name,typ:ResourceType)) => - new ServiceEntry(name,"0.0","0","0.0",typ.unit,List[ResourceEntry]()) :: ret - } + def addMissingServices(se0:java.util.List[ServiceEntryMsg],re:Map[String,ResourceType]) : + java.util.List[ServiceEntryMsg]= { + import scala.collection.JavaConverters.asScalaBufferConverter + val se = se0.asScala.toList + import scala.collection.JavaConverters.seqAsJavaListConverter + (se :::(re -- se.map(_.getServiceName).toSet).foldLeft(List[ServiceEntryMsg]()) { case (ret,(name,typ:ResourceType)) => + newServiceEntry(name,"0.0","0","0.0",typ.unit,new java.util.ArrayList[ResourceEntryMsg]()) :: ret + }).asJava + } def fromWorkingUserState(t0:Timeslot,userID:String,w:Option[UserStateMsg], - resourceTypes:Map[String,ResourceType]) : AbstractBillEntry = { + resourceTypes:Map[String,ResourceType]) : BillEntryMsg = { val t = t0.roundMilliseconds /* we do not care about milliseconds */ //Console.err.println("Timeslot: " + t0) //Console.err.println("After rounding timeslot: " + t) val ret = w match { case None => - val allMissing = addMissingServices(Nil,resourceTypes) - new BillEntry(counter.getAndIncrement.toString, + val allMissing = addMissingServices(new util.ArrayList[ServiceEntryMsg](),resourceTypes) + newBillEntry(counter.getAndIncrement.toString, userID,"processing", "0.0", "0.0", @@ -280,11 +390,11 @@ object AbstractBillEntry { allMissing) case Some(w) => val wjson = AvroHelpers.jsonStringOfSpecificRecord(w) - Console.err.println("Working user state: %s".format(wjson)) + //Console.err.println("Working user state: %s".format(wjson)) val (rcEntries,rcEntriesCredits) = resourceEntriesAt(t,w) val resList0 = aggregateResourceEntries(rcEntries) val resList1 = addMissingServices(resList0,resourceTypes) - new BillEntry(counter.getAndIncrement.toString, + newBillEntry(counter.getAndIncrement.toString, userID,"ok", w.getTotalCredits.toString, rcEntriesCredits.toString, @@ -292,7 +402,7 @@ object AbstractBillEntry { t.to.getTime.toString, resList1) } - //Console.err.println("JSON: " + ret.toJsonString) + //Console.err.println("JSON: " + AvroHelpers.jsonStringOfSpecificRecord(ret)) ret }