Refactored BillEntry and relevant classes.
authorProdromos Gerakios <pgerakios@grnet.gr>
Thu, 23 Aug 2012 13:24:30 +0000 (16:24 +0300)
committerProdromos Gerakios <pgerakios@grnet.gr>
Thu, 23 Aug 2012 13:24:30 +0000 (16:24 +0300)
bill.sh
src/main/scala/gr/grnet/aquarium/actor/message/GetUserBillResponse.scala
src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala
src/main/scala/gr/grnet/aquarium/charging/bill/BillEntry.scala
src/main/scala/gr/grnet/aquarium/connector/rabbitmq/RabbitMQProducer.scala

diff --git a/bill.sh b/bill.sh
index 79eb27a..371b038 100755 (executable)
--- a/bill.sh
+++ b/bill.sh
@@ -59,6 +59,8 @@ RESOURCE1="{
  echo $USERCREATE | rabbitmqadmin -H dev82.dev.grnet.gr -P 55672 -u rabbit -p r@bb1t publish  routing_key=astakos.user exchange=astakos
  echo $ADDCREDITS | rabbitmqadmin -H dev82.dev.grnet.gr -P 55672 -u rabbit -p r@bb1t publish  routing_key=astakos.user exchange=astakos 
  echo $RESOURCE1  | rabbitmqadmin -H dev82.dev.grnet.gr -P 55672 -u rabbit -p r@bb1t publish  routing_key=pithos.resource.diskspace exchange=pithos
+
+#read
 MAINCLASS=gr.grnet.aquarium.charging.bill.BillEntry
 /home/pgerakios/jdk1.6.0_33/bin/java -Dfile.encoding=UTF-8 -classpath /home/pgerakios/jdk1.6.0_33/jre/lib/rt.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/resources.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/jce.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/plugin.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/deploy.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/jsse.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/management-agent.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/javaws.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/charsets.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/ext/sunpkcs11.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/ext/localedata.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/ext/dnsns.jar:/home/pgerakios/jdk1.6.0_33/jre/lib/ext/sunjce_provider.jar:/home/pgerakios/aquarium/target/classes:/home/pgerakios/.m2/repository/org/scala-lang/scala-library/2.9.1/scala-library-2.9.1.jar:/home/pgerakios/.m2/repository/org/slf4j/slf4j-api/1.6.1/slf4j-api-1.6.1.jar:/home/pgerakios/.m2/repository/ch/qos/logback/logback-classic/0.9.29/logback-classic-0.9.29.jar:/home/pgerakios/.m2/repository/ch/qos/logback/logback-core/0.9.29/logback-core-0.9.29.jar:/home/pgerakios/.m2/repository/org/scala-lang/scala-compiler/2.9.1/scala-compiler-2.9.1.jar:/home/pgerakios/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.0.2/jackson-core-2.0.2.jar:/home/pgerakios/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.0.2/jackson-databind-2.0.2.jar:/home/pgerakios/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.0.2/jackson-annotations-2.0.2.jar:/home/pgerakios/.m2/repository/com/fasterxml/jackson/module/jackson-module-scala/2.0.2/jackson-module-scala-2.0.2.jar:/home/pgerakios/.m2/repository/org/scalastuff/scalabeans/0.2/scalabeans-0.2.jar:/home/pgerakios/.m2/repository/com/thoughtworks/paranamer/paranamer/2.3/paranamer-2.3.jar:/home/pgerakios/.m2/repository/com/google/guava/guava/12.0/guava-12.0.jar:/home/pgerakios/.m2/repository/net/liftweb/lift-json_2.9.1/2.4/lift-json_2.9.1-2.4.jar:/home/pgerakios/.m2/repository/org/scala-lang/scalap/2.9.1/scalap-2.9.1.jar:/home/pgerakios/.m2/repository/net/liftweb/lift-json-ext_2.9.1/2.4/lift-json-ext_2.9.1-2.4.jar:/home/pgerakios/.m2/repository/commons-codec/commons-codec/1.4/commons-codec-1.4.jar:/home/pgerakios/.m2/repository/net/liftweb/lift-common_2.9.1/2.4/lift-common_2.9.1-2.4.jar:/home/pgerakios/.m2/repository/org/yaml/snakeyaml/1.9/snakeyaml-1.9.jar:/home/pgerakios/.m2/repository/com/kenai/crontab-parser/crontab-parser/1.0.1/crontab-parser-1.0.1.jar:/home/pgerakios/.m2/repository/com/rabbitmq/amqp-client/2.8.4/amqp-client-2.8.4.jar:/home/pgerakios/.m2/repository/com/ckkloverdos/jbootstrap/3.0.0/jbootstrap-3.0.0.jar:/home/pgerakios/.m2/repository/com/ckkloverdos/streamresource/0.5.1/streamresource-0.5.1.jar:/home/pgerakios/.m2/repository/com/ckkloverdos/maybe/0.5.0/maybe-0.5.0.jar:/home/pgerakios/.m2/repository/com/ckkloverdos/sysprop/0.5.1/sysprop-0.5.1.jar:/home/pgerakios/.m2/repository/com/ckkloverdos/converter/0.5.0/converter-0.5.0.jar:/home/pgerakios/.m2/repository/com/ckkloverdos/typedkey/0.5.0/typedkey-0.5.0.jar:/home/pgerakios/.m2/repository/com/thoughtworks/xstream/xstream/1.4.1/xstream-1.4.1.jar:/home/pgerakios/.m2/repository/xmlpull/xmlpull/1.1.3.1/xmlpull-1.1.3.1.jar:/home/pgerakios/.m2/repository/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar:/home/pgerakios/.m2/repository/org/mongodb/mongo-java-driver/2.7.2/mongo-java-driver-2.7.2.jar:/home/pgerakios/.m2/repository/com/typesafe/akka/akka-actor/2.0.2/akka-actor-2.0.2.jar:/home/pgerakios/.m2/repository/com/typesafe/akka/akka-remote/2.0.2/akka-remote-2.0.2.jar:/home/pgerakios/.m2/repository/io/netty/netty/3.3.0.Final/netty-3.3.0.Final.jar:/home/pgerakios/.m2/repository/com/google/protobuf/protobuf-java/2.4.1/protobuf-java-2.4.1.jar:/home/pgerakios/.m2/repository/net/debasishg/sjson_2.9.1/0.15/sjson_2.9.1-0.15.jar:/home/pgerakios/.m2/repository/net/databinder/dispatch-json_2.9.1/0.8.5/dispatch-json_2.9.1-0.8.5.jar:/home/pgerakios/.m2/repository/org/apache/httpcomponents/httpclient/4.1/httpclient-4.1.jar:/home/pgerakios/.m2/repository/org/apache/httpcomponents/httpcore/4.1/httpcore-4.1.jar:/home/pgerakios/.m2/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar:/home/pgerakios/.m2/repository/org/objenesis/objenesis/1.2/objenesis-1.2.jar:/home/pgerakios/.m2/repository/commons-io/commons-io/1.4/commons-io-1.4.jar:/home/pgerakios/.m2/repository/voldemort/store/compress/h2-lzf/1.0/h2-lzf-1.0.jar:/home/pgerakios/.m2/repository/com/typesafe/akka/akka-slf4j/2.0.2/akka-slf4j-2.0.2.jar:/home/pgerakios/.m2/repository/com/twitter/finagle-core_2.9.1/4.0.2/finagle-core_2.9.1-4.0.2.jar:/home/pgerakios/.m2/repository/com/twitter/util-core_2.9.1/4.0.1/util-core_2.9.1-4.0.1.jar:/home/pgerakios/.m2/repository/com/twitter/util-collection_2.9.1/4.0.1/util-collection_2.9.1-4.0.1.jar:/home/pgerakios/.m2/repository/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar:/home/pgerakios/.m2/repository/com/twitter/util-hashing_2.9.1/4.0.1/util-hashing_2.9.1-4.0.1.jar:/home/pgerakios/.m2/repository/com/twitter/finagle-http_2.9.1/4.0.2/finagle-http_2.9.1-4.0.2.jar:/home/pgerakios/.m2/repository/com/twitter/util-codec_2.9.1/4.0.1/util-codec_2.9.1-4.0.1.jar:/home/pgerakios/.m2/repository/com/twitter/util-logging_2.9.1/4.0.1/util-logging_2.9.1-4.0.1.jar:/home/pgerakios/.m2/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar:/home/pgerakios/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar:/home/pgerakios/.m2/repository/joda-time/joda-time/2.0/joda-time-2.0.jar:/home/pgerakios/.m2/repository/org/joda/joda-convert/1.1/joda-convert-1.1.jar:/home/pgerakios/.m2/repository/org/quartz-scheduler/quartz/2.1.5/quartz-2.1.5.jar:/home/pgerakios/.m2/repository/c3p0/c3p0/0.9.1.1/c3p0-0.9.1.1.jar:/home/pgerakios/.m2/repository/org/quartz-scheduler/quartz-oracle/2.1.5/quartz-oracle-2.1.5.jar:/home/pgerakios/.m2/repository/org/quartz-scheduler/quartz-weblogic/2.1.5/quartz-weblogic-2.1.5.jar:/home/pgerakios/.m2/repository/org/quartz-scheduler/quartz-jboss/2.1.5/quartz-jboss-2.1.5.jar:/home/pgerakios/idea-IC-117.418/lib/idea_rt.jar $MAINCLASS & 
 PID=$!
index d18cc7d..f6129be 100644 (file)
@@ -36,7 +36,7 @@
 package gr.grnet.aquarium.actor.message
 
 import gr.grnet.aquarium.AquariumInternalError
-import gr.grnet.aquarium.charging.bill.BillEntry
+import gr.grnet.aquarium.charging.bill.{AbstractBillEntry, BillEntry}
 
 /**
  *
@@ -59,4 +59,4 @@ case class GetUserBillResponse(
   }
 }
 
-case class GetUserBillResponseData(userID: String, billEntry: BillEntry)
\ No newline at end of file
+case class GetUserBillResponseData(userID: String, billEntry: AbstractBillEntry)
\ No newline at end of file
index 476f3ce..f969671 100644 (file)
@@ -71,7 +71,7 @@ import scala.Some
 import scala.Right
 import gr.grnet.aquarium.policy.StdUserAgreement
 import gr.grnet.aquarium.charging.state.UserStateBootstrap
-import gr.grnet.aquarium.charging.bill.BillEntry
+import gr.grnet.aquarium.charging.bill.{AbstractBillEntry, BillEntry}
 
 /**
  *
@@ -439,7 +439,7 @@ class UserActor extends ReflectiveRoleableActor {
     try{
       val timeslot = event.timeslot
       val state= if(haveWorkingUserState) Some(this._workingUserState) else None
-      val billEntry = BillEntry.fromWorkingUserState(timeslot,this._userID,state)
+      val billEntry = AbstractBillEntry.fromWorkingUserState(timeslot,this._userID,state)
       val billData = GetUserBillResponseData(this._userID,billEntry)
       sender ! GetUserBillResponse(Right(billData))
     } catch {
index 5de5905..a449e13 100644 (file)
@@ -5,19 +5,23 @@ import gr.grnet.aquarium.util.json.JsonSupport
 import com.ckkloverdos.resource.FileStreamResource
 import java.io.File
 import com.ckkloverdos.props.Props
-import gr.grnet.aquarium.converter.{PrettyJsonTextFormat, StdConverters}
+import gr.grnet.aquarium.converter.{CompactJsonTextFormat, PrettyJsonTextFormat, StdConverters}
 import gr.grnet.aquarium.{Aquarium, ResourceLocator, AquariumBuilder}
 import gr.grnet.aquarium.store.memory.MemStoreProvider
 import gr.grnet.aquarium.converter.StdConverters._
-import scala.Some
+import scala._
 import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
 import java.util.concurrent.atomic.AtomicLong
 import java.util.{Date, Calendar, GregorianCalendar}
 import gr.grnet.aquarium.charging.wallet.WalletEntry
-import collection.parallel.mutable
-import collection.mutable.ListBuffer
+import scala.collection.parallel.mutable
+import scala.collection.mutable.ListBuffer
 import gr.grnet.aquarium.Aquarium.EnvKeys
 import gr.grnet.aquarium.charging.Chargeslot
+import scala.collection.immutable.TreeMap
+import scala.Some
+import gr.grnet.aquarium.charging.Chargeslot
+
 
 /*
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
@@ -59,23 +63,30 @@ import gr.grnet.aquarium.charging.Chargeslot
 * @author Prodromos Gerakios <pgerakios@grnet.gr>
 */
 
-class EventEntry(val id:String,
-                 val unitPrice:String,
-                 val startTime:String,
-                 val endTime:String,
-                 val ellapsedTime:String,
-                 val credits:String) extends JsonSupport {
+case class ChargeEntry(val id:String,
+                       val unitPrice:String,
+                       val startTime:String,
+                       val endTime:String,
+                       val ellapsedTime:String,
+                       val credits:String)
+  extends JsonSupport {}
+
+
+class EventEntry(val eventType : String,
+                 val details   : List[ChargeEntry])
+ extends JsonSupport {}
 
-}
 
-class ResourceEntry(val resourceName : String,
-                    val resourceType : String,
-                    val unitName : String,
-                    val totalCredits : String,
-                    val eventType:String,
-                    val details : List[EventEntry]) extends JsonSupport {
+case class ResourceEntry(val resourceName : String,
+                         val resourceType : String,
+                         val unitName : String,
+                         val totalCredits : String,
+                         val details : List[EventEntry])
+extends JsonSupport {}
 
-}
+
+abstract class AbstractBillEntry
+ extends JsonSupport {}
 
 class BillEntry(val id:String,
                 val userID : String,
@@ -85,11 +96,11 @@ class BillEntry(val id:String,
                 val startTime:String,
                 val endTime:String,
                 val bill:List[ResourceEntry]
-              )  extends JsonSupport {
+              )
+ extends AbstractBillEntry {}
 
-}
 
-object BillEntry {
+object AbstractBillEntry {
 
   private[this] val counter = new AtomicLong(0L)
   private[this] def nextUIDObject() = counter.getAndIncrement
@@ -104,21 +115,28 @@ object BillEntry {
    Timeslot(dstart,dend)
   } */
 
-  private[this] def toEventEntry(c:Chargeslot) : EventEntry = {
+  private[this] def toChargeEntry(c:Chargeslot) : ChargeEntry = {
     val unitPrice = c.unitPrice.toString
     val startTime = c.startMillis.toString
     val endTime   = c.stopMillis.toString
     val difTime   = (c.stopMillis - c.startMillis).toString
     val credits   = c.creditsToSubtract.toString
-    new EventEntry(counter.getAndIncrement.toString,unitPrice,
+    new ChargeEntry(counter.getAndIncrement.toString,unitPrice,
                     startTime,endTime,difTime,credits)
-
   }
 
+  private[this] def toEventEntry(eventType:String,c:Chargeslot) : EventEntry =
+    new EventEntry(eventType,List(toChargeEntry(c)))
+
 
   private[this] def toResourceEntry(w:WalletEntry) : ResourceEntry = {
     assert(w.sumOfCreditsToSubtract==0.0 || w.chargslotCount > 0)
-    val rcName =  w.resource.toString
+    val rcName = w.currentResourceEvent.clientID match {
+            case "pithos" =>
+              w.currentResourceEvent.details("path")
+            case _ =>
+              w.currentResourceEvent.instanceID
+        }
     val rcType =  w.resourceType.name
     val rcUnitName = w.resourceType.unit
     val eventEntry = new ListBuffer[EventEntry]
@@ -147,19 +165,20 @@ object BillEntry {
     for { c <- w.chargeslots }{
       if(c.creditsToSubtract != 0.0) {
         //Console.err.println("c.creditsToSubtract : " + c.creditsToSubtract)
-        eventEntry += toEventEntry(c)
+        eventEntry += toEventEntry(eventType.toString,c)
         //credits += c.creditsToSubtract
       }
     }
     //Console.err.println("TOTAL resource event credits: " + credits)
-    new ResourceEntry(rcName,rcType,rcUnitName,credits.toString,eventType.toString,eventEntry.toList)
+    new ResourceEntry(rcName,rcType,rcUnitName,credits.toString,eventEntry.toList)
   }
 
   private[this] def resourceEntriesAt(t:Timeslot,w:WorkingUserState) : (List[ResourceEntry],Double) = {
     val ret = new ListBuffer[ResourceEntry]
     var sum = 0.0
     //Console.err.println("Wallet entries: " + w.walletEntries)
-    for { i <- w.walletEntries} {
+    val walletEntries = w.walletEntries
+    for { i <- walletEntries} {
       if(t.contains(i.referenceTimeslot) && i.sumOfCreditsToSubtract != 0.0){
         //Console.err.println("i.sumOfCreditsToSubtract : " + i.sumOfCreditsToSubtract)
         sum += i.sumOfCreditsToSubtract
@@ -172,7 +191,22 @@ object BillEntry {
     (ret.toList,sum)
   }
 
-  def fromWorkingUserState(t:Timeslot,userID:String,w:Option[WorkingUserState]) : BillEntry = {
+  private[this] def addResourceEntries(a:ResourceEntry,b:ResourceEntry) : ResourceEntry = {
+    assert(a.resourceName == b.resourceName)
+    val totalCredits = (a.totalCredits.toDouble+b.totalCredits.toDouble).toString
+    a.copy(a.resourceName,a.resourceType,a.unitName,totalCredits,a.details ::: b.details)
+  }
+
+  private[this] def aggregateResourceEntries(re:List[ResourceEntry]) : List[ResourceEntry] =
+    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)))
+      }
+    }.values.toList
+
+  def fromWorkingUserState(t:Timeslot,userID:String,w:Option[WorkingUserState]) : AbstractBillEntry = {
     val ret = w match {
       case None =>
           new BillEntry(counter.getAndIncrement.toString,
@@ -183,19 +217,39 @@ object BillEntry {
                         Nil)
       case Some(w) =>
         val (rcEntries,rcEntriesCredits) = resourceEntriesAt(t,w)
+        val resMap = aggregateResourceEntries(rcEntries)
         new BillEntry(counter.getAndIncrement.toString,
                       userID,"ok",
                       w.totalCredits.toString,
                       rcEntriesCredits.toString,
                       t.from.getTime.toString,t.to.getTime.toString,
-                      rcEntries)
+                      resMap
+                     )
     }
     //Console.err.println("JSON: " +  ret.toJsonString)
     ret
   }
 
-  //
+  val jsonSample = "{\n  \"id\":\"2\",\n  \"userID\":\"loverdos@grnet.gr\",\n  \"status\":\"ok\",\n  \"remainingCredits\":\"3130.0000027777783\",\n  \"deductedCredits\":\"5739.9999944444435\",\n  \"startTime\":\"1341090000000\",\n  \"endTime\":\"1343768399999\",\n  \"bill\":[{\n    \"resourceName\":\"diskspace\",\n    \"resourceType\":\"diskspace\",\n    \"unitName\":\"MB/Hr\",\n    \"totalCredits\":\"2869.9999972222217\",\n    \"eventType\":\"object update@/Papers/GOTO_HARMFUL.PDF\",\n\t    \"details\":[\n\t     {\"totalCredits\":\"2869.9999972222217\",\n\t      \"details\":[{\n\t      \"id\":\"0\",\n\t      \"unitPrice\":\"0.01\",\n\t      \"startTime\":\"1342735200000\",\n\t      \"endTime\":\"1343768399999\",\n\t      \"ellapsedTime\":\"1033199999\",\n\t      \"credits\":\"2869.9999972222217\"\n\t    \t}]\n\t    }\n\t  ]\n  },{\n    \"resourceName\":\"diskspace\",\n    \"resourceType\":\"diskspace\",\n    \"unitName\":\"MB/Hr\",\n    \"totalCredits\":\"2869.9999972222217\",\n    \"eventType\":\"object update@/Papers/GOTO_HARMFUL.PDF\",\n    \"details\":[\t     {\"totalCredits\":\"2869.9999972222217\",\n\t      \"details\":[{\n\t      \"id\":\"0\",\n\t      \"unitPrice\":\"0.01\",\n\t      \"startTime\":\"1342735200000\",\n\t      \"endTime\":\"1343768399999\",\n\t      \"ellapsedTime\":\"1033199999\",\n\t      \"credits\":\"2869.9999972222217\"\n\t    \t}]\n\t    }\n\t]\n  }]\n}"
+
   def main(args: Array[String]) = {
+     val b : BillEntry = StdConverters.AllConverters.convertEx[BillEntry](CompactJsonTextFormat(jsonSample))
+     val l0 = b.bill
+     val l1 = aggregateResourceEntries(l0)
+
+     Console.err.println("Initial resources: ")
+     for{ i <- l0 } Console.err.println("RESOURCE: " + i.toJsonString)
+    Console.err.println("Aggregate resources: ")
+    for{ a <- l1 } {
+      Console.err.println("RESOURCE:  %s\n  %s\nEND RESOURCE".format(a.resourceName,a.toJsonString))
+    }
+
+    val aggr = new BillEntry(b.id,b.userID,b.status,b.remainingCredits,b.deductedCredits,b.startTime,b.endTime,l1)
+    Console.err.println("Aggregate:\n" + aggr.toJsonString)
+  }
+
+  //
+  def main0(args: Array[String]) = {
     //Console.err.println("JSON: " +  (new BillEntry).toJsonString)
     val propsfile = new FileStreamResource(new File("aquarium.properties"))
     var _props: Props = Props(propsfile)(StdConverters.AllConverters).getOr(Props()(StdConverters.AllConverters))
index 04c659e..b591287 100644 (file)
@@ -56,9 +56,9 @@ import collection.mutable
 
 private class RabbitMQProducerActor extends Actor {
   def receive = {
-    case sendMessage:(() => Unit) =>
+    case sendMessage:Function1[_,_] =>
       //Console.err.println("Executing msg ... " + sendMessage.hashCode)
-      sendMessage()
+      sendMessage.asInstanceOf[()=>Unit]()
     case x  : AnyRef     =>
       //Console.err.println("Dammit  ..." + x.getClass.getSimpleName)
       ()