Remodeling events
authorChristos KK Loverdos <loverdos@gmail.com>
Thu, 26 Apr 2012 14:43:36 +0000 (17:43 +0300)
committerChristos KK Loverdos <loverdos@gmail.com>
Thu, 26 Apr 2012 14:43:36 +0000 (17:43 +0300)
40 files changed:
src/main/scala/gr/grnet/aquarium/actor/message/service/router/RouterMessage.scala
src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala
src/main/scala/gr/grnet/aquarium/converter/Conversions.scala
src/main/scala/gr/grnet/aquarium/converter/JsonConversions.scala
src/main/scala/gr/grnet/aquarium/converter/StdConverters.scala
src/main/scala/gr/grnet/aquarium/event/EventModel.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/event/ExternalEventModel.scala [moved from src/main/scala/gr/grnet/aquarium/event/AquariumEventModel.scala with 75% similarity]
src/main/scala/gr/grnet/aquarium/event/ExternalEventSkeleton.scala [moved from src/main/scala/gr/grnet/aquarium/event/AquariumEventSkeleton.scala with 97% similarity]
src/main/scala/gr/grnet/aquarium/event/NewWalletEntry.scala
src/main/scala/gr/grnet/aquarium/event/PolicyEntry.scala
src/main/scala/gr/grnet/aquarium/event/ResourceEvent.scala
src/main/scala/gr/grnet/aquarium/event/WalletEntry.scala
src/main/scala/gr/grnet/aquarium/event/im/IMEventModel.scala
src/main/scala/gr/grnet/aquarium/event/im/StdIMEvent.scala
src/main/scala/gr/grnet/aquarium/event/resource/ResourceEventModel.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/event/resource/StdResourceEvent.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/logic/accounting/Accounting.scala
src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLCostPolicy.scala
src/main/scala/gr/grnet/aquarium/service/EventProcessorService.scala
src/main/scala/gr/grnet/aquarium/service/IMEventProcessorService.scala
src/main/scala/gr/grnet/aquarium/service/ResourceEventProcessorService.scala
src/main/scala/gr/grnet/aquarium/simulation/ResourceInstanceSim.scala
src/main/scala/gr/grnet/aquarium/simulation/StdBandwidthInstanceSim.scala
src/main/scala/gr/grnet/aquarium/simulation/StdDiskspaceInstanceSim.scala
src/main/scala/gr/grnet/aquarium/simulation/StdVMTimeInstanceSim.scala
src/main/scala/gr/grnet/aquarium/simulation/UserSim.scala
src/main/scala/gr/grnet/aquarium/store/IMEventStore.scala
src/main/scala/gr/grnet/aquarium/store/LocalFSEventStore.scala
src/main/scala/gr/grnet/aquarium/store/ResourceEventStore.scala
src/main/scala/gr/grnet/aquarium/store/memory/MemStore.scala
src/main/scala/gr/grnet/aquarium/store/memory/package.scala
src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBEventModel.scala
src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBIMEvent.scala
src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBResourceEvent.scala [new file with mode: 0644]
src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBStore.scala
src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala
src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala
src/main/scala/gr/grnet/aquarium/util/RandomEventGenerator.scala
src/test/scala/gr/grnet/aquarium/logic/test/AccountingTest.scala
src/test/scala/gr/grnet/aquarium/store/mongodb/EventStoreTest.scala

index e0053f4..32be7eb 100644 (file)
@@ -40,8 +40,8 @@ package router
 import gr.grnet.aquarium.user.UserState
 import gr.grnet.aquarium.util.json.JsonSupport
 import gr.grnet.aquarium.event.im.IMEventModel
-import gr.grnet.aquarium.event.ResourceEvent
 import gr.grnet.aquarium.converter.{PrettyJsonTextFormat, StdConverters}
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 
 /**
@@ -91,7 +91,7 @@ case class UserResponseGetState(userID: String, state: UserState) extends Router
  *
  * Note that the prefix `Process` means that no reply is created or needed.
  */
-case class ProcessResourceEvent(rcEvent: ResourceEvent) extends RouterMessage
+case class ProcessResourceEvent(rcEvent: ResourceEventModel) extends RouterMessage
 
 /**
  * Dispatcher message that triggers the user event processing pipeline.
index fd502af..fa5e8ae 100644 (file)
@@ -62,7 +62,7 @@ class UserActor extends ReflectiveAquariumActor {
 
   self.lifeCycle = Temporary
 
-  override protected def onThrowable(t: Throwable) = {
+  override protected def onThrowable(t: Throwable, message: AnyRef) = {
     logger.error("Terminating due to: %s".format(t.getMessage), t)
     UserActorCache.invalidate(this._userID)
 
index fb40783..98b5c10 100644 (file)
@@ -48,5 +48,4 @@ object Conversions {
   def jsonSupportToDBObject(jsonSupport: JsonSupport) =
     AllConverters.convertEx[DBObject](jsonSupport)
 
-
 }
index acc3e5d..3a5cc8b 100644 (file)
@@ -38,7 +38,7 @@ package gr.grnet.aquarium.converter
 import net.liftweb.json.JsonAST.JValue
 import net.liftweb.json._
 import ext.JodaTimeSerializers
-import gr.grnet.aquarium.event.AquariumEventSkeleton
+import gr.grnet.aquarium.event.ExternalEventSkeleton
 
 
 /**
@@ -54,7 +54,7 @@ object JsonConversions {
    * The application-wide JSON formats used from the underlying lift-json library.
    */
   implicit val Formats = (DefaultFormats ++ JodaTimeSerializers.all) +
-    FieldSerializer[AquariumEventSkeleton]()
+    FieldSerializer[ExternalEventSkeleton]()
 
   /**
    * Converts a value to JSON AST (Abstract Syntax Tree) by acting a bit intelligently, depending on the actual type
index b006e16..ccc1024 100644 (file)
@@ -108,6 +108,7 @@ object StdConverters {
 
     @scala.throws(classOf[ConverterException])
     def convertEx[T: Type](sourceValue: Any) = {
+      // Generic deserializer from json string to a business logic model
       JsonConversions.jsonToObject[T](sourceValue.asInstanceOf[JsonTextFormat].value)(manifest[T], JsonConversions.Formats)
     }
   }
diff --git a/src/main/scala/gr/grnet/aquarium/event/EventModel.scala b/src/main/scala/gr/grnet/aquarium/event/EventModel.scala
new file mode 100644 (file)
index 0000000..dfd30f5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.event
+
+/**
+ * Basic properties for all events.
+ * An event represents some state change, where state is specific to the use-case.
+ *
+ *@author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+trait EventModel {
+  /**
+   * The unique event id. The responsibility for the id generation is to the event generator.
+   */
+  def id: String
+
+  /**
+   * The Unix time of the state change occurrence that this event represents.
+   */
+  def occurredMillis: Long
+
+  /**
+   * The ID given to this event if/when persisted to a store.
+   * The exact type of the id is store-specific.
+   */
+  def storeID: Option[AnyRef] = None
+
+  def eventVersion: String
+
+  /**
+   * An extension point that provides even more properties.
+   */
+  def details: Map[String, String]
+
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long): EventModel
+}
+
+
+object EventModel {
+  trait NamesT {
+    final val id = "id"
+    final val occurredMillis = "occurredMillis"
+    final val storeID = "storeID"
+    final val eventVersion = "eventVersion"
+    final val details = "details"
+  }
+
+  object Names extends NamesT
+}
\ No newline at end of file
@@ -45,44 +45,24 @@ import gr.grnet.aquarium.util.xml.XmlSupport
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
 
-trait AquariumEventModel extends JsonSupport with XmlSupport {
-  /**
-   * The id at the sender side
-   */
-  def id: String
-
-  /**
-   * When it occurred at the sender side
-   */
-  def occurredMillis: Long
-
+trait ExternalEventModel extends EventModel with JsonSupport with XmlSupport {
   /**
    * When it was received by Aquarium
    */
   def receivedMillis: Long
+
   def userID: String
-  def eventVersion: String
-  def details: Map[String, String]
 
-  /**
-   * The ID given to this event if/when persisted to a store.
-   * The exact type of the id is store-specific.
-   */
-  def storeID: Option[AnyRef] = None
 
   def toBytes: Array[Byte] = makeBytes(toJsonString)
 
-  def withReceivedMillis(newReceivedMillis: Long): AquariumEventModel
+  def withReceivedMillis(newReceivedMillis: Long): ExternalEventModel
 }
 
-object AquariumEventModel {
-  trait NamesT {
-    final val id = "id"
-    final val occurredMillis = "occurredMillis"
+object ExternalEventModel {
+  trait NamesT extends EventModel.NamesT {
     final val receivedMillis = "receivedMillis"
     final val userID = "userID"
-    final val eventVersion = "eventVersion"
-    final val details = "details"
   }
 
   object Names extends NamesT
@@ -46,13 +46,13 @@ import util.Loggable
  * @author Georgios Gousios <gousiosg@gmail.com>
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
-abstract class AquariumEventSkeleton(
+abstract class ExternalEventSkeleton(
     _id: String,           // The id at the client side (the sender) TODO: Rename to remoteId or something...
     _occurredMillis: Long, // When it occurred at client side (the sender)
     _receivedMillis: Long, // When it was received by Aquarium
     _eventVersion: String
 )
-  extends AquariumEventModel
+  extends ExternalEventModel
   with    XmlSupport
   with    Loggable {
 
index 8de466e..fbf69c5 100644 (file)
@@ -40,6 +40,7 @@ import gr.grnet.aquarium.logic.accounting.Chargeslot
 import gr.grnet.aquarium.util.date.MutableDateCalc
 import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLResource}
 import converter.{JsonTextFormat, StdConverters}
+import resource.ResourceEventModel
 
 /**
  * The following equation must hold: `newTotalCredits = oldTotalCredits + entryCredits`.
@@ -65,7 +66,7 @@ case class NewWalletEntry(userId: String,
                           referenceTimeslot: Timeslot,
                           yearOfBillingMonth: Int,
                           billingMonth: Int,
-                          resourceEvents: List[ResourceEvent], // current is at the head
+                          resourceEvents: List[ResourceEventModel], // current is at the head
                           chargeslots: List[Chargeslot],
                           resourceDef: DSLResource,
                           isSynthetic: Boolean) {
index a6b78b5..1059483 100644 (file)
@@ -55,7 +55,7 @@ case class PolicyEntry(
     eventVersion: String = "1.0",
     userID: String = "",
     details: Map[String, String] = Map()
- ) extends AquariumEventModel {
+ ) extends ExternalEventModel {
 
   assert(if(validTo != -1) validTo > validFrom else validFrom > 0)
 
@@ -63,6 +63,9 @@ case class PolicyEntry(
 
   def withReceivedMillis(millis: Long) = copy(receivedMillis = millis)
 
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
+    this.copy(details = newDetails, occurredMillis = newOccurredMillis)
+
   def fromToTimeslot = Timeslot(new Date(validFrom), new Date(validTo))
 }
 
index 094b6af..9c52f08 100644 (file)
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium
-package event
-
-import gr.grnet.aquarium.util.makeString
-import gr.grnet.aquarium.logic.accounting.dsl._
-import com.ckkloverdos.maybe.Maybe
-import java.util.Date
-import gr.grnet.aquarium.util.date.MutableDateCalc
-import collection.SeqLike
-import converter.{JsonTextFormat, StdConverters}
+//package gr.grnet.aquarium
+//package event
+//
+//import gr.grnet.aquarium.util.makeString
+//import gr.grnet.aquarium.logic.accounting.dsl._
+//import com.ckkloverdos.maybe.Maybe
+//import java.util.Date
+//import gr.grnet.aquarium.util.date.MutableDateCalc
+//import collection.SeqLike
+//import converter.{JsonTextFormat, StdConverters}
 
 /**
  * Event sent to Aquarium by clients for resource accounting.
@@ -50,152 +50,89 @@ import converter.{JsonTextFormat, StdConverters}
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  * @author Georgios Gousios <gousiosg@gmail.com>.
  */
-case class ResourceEvent(
-    id: String,           // The id at the client side (the sender) TODO: Rename to remoteId or something...
-    occurredMillis: Long, // When it occurred at client side (the sender)
-    receivedMillis: Long, // When it was received by Aquarium
-    userID: String,                    // The user for which this resource is relevant
-    clientID: String,                  // The unique client identifier (usually some hash)
-    resource: String,                  // String representation of the resource type (e.g. "bndup", "vmtime").
-    instanceID: String,                // String representation of the resource instance id
-    eventVersion: String,
-    value: Double,
-    details: Map[String, String])
-extends AquariumEventModel {
-
-  def validate() : Boolean = {
-    !safeResource.isEmpty
-  }
-
-  def safeResource   = if(resource eq null)   "" else resource
-  def safeInstanceId = if(instanceID eq null) "" else instanceID
-
-  def hasResource   = !safeResource.isEmpty
-  def hasInstanceId = !safeInstanceId.isEmpty
-
-  def fullResourceInfo = (safeResource, safeInstanceId)
-
-  def occurredDate = new Date(occurredMillis)
-
-  def occurredDeltaFrom(that: ResourceEvent): Long = {
-    this.occurredMillis - that.occurredMillis
-  }
-
-  def isOccurredWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
-    require(fromMillis <= toMillis, "fromMillis <= toMillis")
-    fromMillis <= occurredMillis && occurredMillis <= toMillis
-  }
-
-  def isOccurredWithinDates(fromDate: Date, toDate: Date): Boolean = {
-    isOccurredWithinMillis(fromDate.getTime, toDate.getTime)
-  }
-
-  def isReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
-    require(fromMillis <= toMillis, "fromMillis <= toMillis")
-    fromMillis <= receivedMillis && receivedMillis <= toMillis
-  }
-  
-  def isReceivedWithinDates(fromDate: Date, toDate: Date): Boolean = {
-    isReceivedWithinMillis(fromDate.getTime, toDate.getTime)
-  }
-
-  def isReceivedWithinDateCalcs(fromDate: MutableDateCalc, toDate: MutableDateCalc): Boolean = {
-    isReceivedWithinMillis(fromDate.getMillis, toDate.getMillis)
-  }
-
-  def isOccurredOrReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
-    isOccurredWithinMillis(fromMillis, toMillis) ||
-    isReceivedWithinMillis(fromMillis, toMillis)
-  }
-
-  def isOccurredOrReceivedWithinDates(fromDate: Date, toDate: Date): Boolean = {
-    isOccurredWithinDates(fromDate, toDate) ||
-    isReceivedWithinDates(fromDate, toDate)
-  }
-  
-  def isOutOfSyncForBillingMonth(yearOfBillingMonth: Int, billingMonth: Int) = {
-    val billingStartDateCalc = new MutableDateCalc(yearOfBillingMonth, billingMonth)
-    val billingStartMillis = billingStartDateCalc.toMillis
-    // NOTE: no need to `copy` the mutable `billingStartDateCalc` here because we use it once
-    val billingStopMillis  = billingStartDateCalc.goEndOfThisMonth.toMillis
-
-    isOutOfSyncForBillingPeriod(billingStartMillis, billingStopMillis)
-  }
-  
-  def isOutOfSyncForBillingPeriod(billingStartMillis: Long, billingStopMillis: Long): Boolean = {
-    isReceivedWithinMillis(billingStartMillis, billingStopMillis) &&
-    (occurredMillis < billingStartMillis || occurredMillis > billingStopMillis)
-  }
-
-  def toDebugString(useOnlyInstanceId: Boolean = false): String = {
-    val instanceInfo = if(useOnlyInstanceId) instanceID else "%s::%s".format(resource, instanceID)
-    val occurredFormatted = new MutableDateCalc(occurredMillis).toYYYYMMDDHHMMSS
-    if(occurredMillis == receivedMillis) {
-      "%sEVENT(%s, [%s], %s, %s, %s, %s, %s)".format(
-        if(isSynthetic) "*" else "",
-        id,
-        occurredFormatted,
-        value,
-        instanceInfo,
-        details,
-        userID,
-        clientID
-      )
-    } else {
-      "%sEVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format(
-        if(isSynthetic) "*" else "",
-        id,
-        occurredFormatted,
-        new MutableDateCalc(receivedMillis),
-        value,
-        instanceInfo,
-        details,
-        userID,
-        clientID
-      )
-    }
-  }
-
-  /**
-   * Return `true` iff this is an event regarding a resource with an
-   * [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]].
-   */
-  def isOnOffEvent(policy: DSLPolicy): Boolean = {
-    policy.findResource(this.resource).map(_.costPolicy) match {
-      case Some(OnOffCostPolicy) ⇒ true
-      case _ ⇒ false
-    }
-  }
-
-  /**
-   * Return `true` iff this is an event regarding a resource with an
-   * [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]] and a
-   * `value` of `"on"`.
-   */
-  def isOnEvent(policy: DSLPolicy): Boolean = {
-    policy.findResource(this.resource) match {
-      case Some(DSLResource(_, _, OnOffCostPolicy, _, _)) ⇒
-        OnOffPolicyResourceState(this.value).isOn
-      case _ ⇒
-        false
-    }
-  }
-
-  /**
-   * Return `true` iff this is an event regarding a resource with an
-   * [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]] and a
-   * `value` of `"off"`.
-   */
-  def isOffEvent(policy: DSLPolicy): Boolean = {
-    policy.findResource(this.resource) match {
-      case Some(DSLResource(_, _, OnOffCostPolicy, _, _)) ⇒
-        OnOffPolicyResourceState(this.value).isOff
-      case _ ⇒
-        false
-    }
-  }
-
-  def withReceivedMillis(millis: Long) = copy(receivedMillis = millis)
+//case class ResourceEvent(
+//    id: String,           // The id at the client side (the sender) TODO: Rename to remoteId or something...
+//    occurredMillis: Long, // When it occurred at client side (the sender)
+//    receivedMillis: Long, // When it was received by Aquarium
+//    userID: String,                    // The user for which this resource is relevant
+//    clientID: String,                  // The unique client identifier (usually some hash)
+//    resource: String,                  // String representation of the resource type (e.g. "bndup", "vmtime").
+//    instanceID: String,                // String representation of the resource instance id
+//    eventVersion: String,
+//    value: Double,
+//    details: Map[String, String])
+//extends ExternalEventModel {
+//
+//  def validate() : Boolean = {
+//    !safeResource.isEmpty
+//  }
+//
+//  def safeResource   = if(resource eq null)   "" else resource
+//  def safeInstanceId = if(instanceID eq null) "" else instanceID
+//
+//  def fullResourceInfo = (safeResource, safeInstanceId)
+//
+//  def occurredDate = new Date(occurredMillis)
+//
+//  def isOccurredWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
+//    require(fromMillis <= toMillis, "fromMillis <= toMillis")
+//    fromMillis <= occurredMillis && occurredMillis <= toMillis
+//  }
+//
+//  def isReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
+//    require(fromMillis <= toMillis, "fromMillis <= toMillis")
+//    fromMillis <= receivedMillis && receivedMillis <= toMillis
+//  }
+//
+//  def isOccurredOrReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
+//    isOccurredWithinMillis(fromMillis, toMillis) ||
+//    isReceivedWithinMillis(fromMillis, toMillis)
+//  }
+//
+//  def isOutOfSyncForBillingMonth(yearOfBillingMonth: Int, billingMonth: Int) = {
+//    val billingStartDateCalc = new MutableDateCalc(yearOfBillingMonth, billingMonth)
+//    val billingStartMillis = billingStartDateCalc.toMillis
+//    // NOTE: no need to `copy` the mutable `billingStartDateCalc` here because we use it once
+//    val billingStopMillis  = billingStartDateCalc.goEndOfThisMonth.toMillis
+//
+//    isOutOfSyncForBillingPeriod(billingStartMillis, billingStopMillis)
+//  }
+//
+//  def isOutOfSyncForBillingPeriod(billingStartMillis: Long, billingStopMillis: Long): Boolean = {
+//    isReceivedWithinMillis(billingStartMillis, billingStopMillis) &&
+//    (occurredMillis < billingStartMillis || occurredMillis > billingStopMillis)
+//  }
+//
+//  def toDebugString(useOnlyInstanceId: Boolean = false): String = {
+//    val instanceInfo = if(useOnlyInstanceId) instanceID else "%s::%s".format(resource, instanceID)
+//    val occurredFormatted = new MutableDateCalc(occurredMillis).toYYYYMMDDHHMMSS
+//    if(occurredMillis == receivedMillis) {
+//      "%sEVENT(%s, [%s], %s, %s, %s, %s, %s)".format(
+//        if(isSynthetic) "*" else "",
+//        id,
+//        occurredFormatted,
+//        value,
+//        instanceInfo,
+//        details,
+//        userID,
+//        clientID
+//      )
+//    } else {
+//      "%sEVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format(
+//        if(isSynthetic) "*" else "",
+//        id,
+//        occurredFormatted,
+//        new MutableDateCalc(receivedMillis),
+//        value,
+//        instanceInfo,
+//        details,
+//        userID,
+//        clientID
+//      )
+//    }
+//  }
+//
+//  def withReceivedMillis(millis: Long) = copy(receivedMillis = millis)
 
   /**
    * Find the cost policy of the resource named in this resource event.
@@ -207,9 +144,9 @@ extends AquariumEventModel {
    * Should the need arises to change the cost policy for a resource, this is a good enough
    * reason to consider creating another type of resource.
    */
-  def findCostPolicyM(defaultPolicy: DSLPolicy): Maybe[DSLCostPolicy] = {
-    defaultPolicy.findResource(this.safeResource).map(_.costPolicy): Maybe[DSLCostPolicy]
-  }
+//  def findCostPolicyM(defaultPolicy: DSLPolicy): Maybe[DSLCostPolicy] = {
+//    defaultPolicy.findResource(this.safeResource).map(_.costPolicy): Maybe[DSLCostPolicy]
+//  }
 
   /**
    * Find the cost policy of the resource named in this resource event.
@@ -221,13 +158,13 @@ extends AquariumEventModel {
    * Should the need arises to change the cost policy for a resource, this is a good enough
    * reason to consider creating another type of resource.
    */
-  def findCostPolicyM(resourcesMap: DSLResourcesMap): Maybe[DSLCostPolicy] = {
-    for {
-      rc <- resourcesMap.findResource(this.safeResource)
-    } yield {
-      rc.costPolicy
-    }
-  }
+//  def findCostPolicyM(resourcesMap: DSLResourcesMap): Maybe[DSLCostPolicy] = {
+//    for {
+//      rc <- resourcesMap.findResource(this.safeResource)
+//    } yield {
+//      rc.costPolicy
+//    }
+//  }
 
   /**
    * `Synthetic` means that Aquarium has manufactured this resource event for some purpose. For example, the implicitly
@@ -235,58 +172,58 @@ extends AquariumEventModel {
    *
    * @return `true` iff this resource event is synthetic.
    */
-  def isSynthetic = {
-    details contains ResourceEvent.JsonNames.details_aquarium_is_synthetic
-  }
-}
+//  def isSynthetic = {
+//    details contains ResourceEvent.JsonNames.details_aquarium_is_synthetic
+//  }
+//}
 
 object ResourceEvent {
-  type ResourceType = String
-  type ResourceIdType = String
-  type FullResourceType = (ResourceType, ResourceIdType)
-  type FullResourceTypeMap = Map[FullResourceType, ResourceEvent]
-  type FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]
-
-  def fromJson(json: String): ResourceEvent = {
-    StdConverters.AllConverters.convertEx[ResourceEvent](JsonTextFormat(json))
-  }
-
-  def fromBytes(bytes: Array[Byte]): ResourceEvent = {
-    StdConverters.AllConverters.convertEx[ResourceEvent](JsonTextFormat(makeString(bytes)))
-  }
-
-  def setAquariumSynthetic(map: Map[String, String]): Map[String, String] = {
-    map.updated(JsonNames.details_aquarium_is_synthetic, "true")
-  }
-
-  def setAquariumSyntheticAndImplicitEnd(map: Map[String, String]): Map[String, String] = {
-    map.
-      updated(JsonNames.details_aquarium_is_synthetic, "true").
-      updated(JsonNames.details_aquarium_is_implicit_end, "true")
-  }
-  
-  def sortByOccurred[S <: Seq[ResourceEvent]](events: S with SeqLike[ResourceEvent, S]): S = {
-    events.sortWith(_.occurredMillis <= _.occurredMillis)
-  }
-
-  object JsonNames {
-    final val _id = "_id"
-    final val id = "id"
-    final val userId = "userId"
-    final val occurredMillis = "occurredMillis"
-    final val receivedMillis = "receivedMillis"
-    final val clientId = "clientId"
-    final val resource = "resource"
-    final val resourceId = "resourceId"
-    final val eventVersion = "eventVersion"
-    final val value = "value"
-    final val details = "details"
+//  type ResourceType = String
+//  type ResourceIdType = String
+//  type FullResourceType = (ResourceType, ResourceIdType)
+//  type FullResourceTypeMap = Map[FullResourceType, ResourceEvent]
+//  type FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]
+
+//  def fromJson(json: String): ResourceEvent = {
+//    StdConverters.AllConverters.convertEx[ResourceEvent](JsonTextFormat(json))
+//  }
+//
+//  def fromBytes(bytes: Array[Byte]): ResourceEvent = {
+//    StdConverters.AllConverters.convertEx[ResourceEvent](JsonTextFormat(makeString(bytes)))
+//  }
+//
+//  def setAquariumSynthetic(map: Map[String, String]): Map[String, String] = {
+//    map.updated(JsonNames.details_aquarium_is_synthetic, "true")
+//  }
+
+//  def setAquariumSyntheticAndImplicitEnd(map: Map[String, String]): Map[String, String] = {
+//    map.
+//      updated(JsonNames.details_aquarium_is_synthetic, "true").
+//      updated(JsonNames.details_aquarium_is_implicit_end, "true")
+//  }
+//
+//  def sortByOccurred[S <: Seq[ResourceEvent]](events: S with SeqLike[ResourceEvent, S]): S = {
+//    events.sortWith(_.occurredMillis <= _.occurredMillis)
+//  }
+//
+//  object JsonNames {
+//    final val _id = "_id"
+//    final val id = "id"
+//    final val userId = "userId"
+//    final val occurredMillis = "occurredMillis"
+//    final val receivedMillis = "receivedMillis"
+//    final val clientId = "clientId"
+//    final val resource = "resource"
+//    final val resourceId = "resourceId"
+//    final val eventVersion = "eventVersion"
+//    final val value = "value"
+//    final val details = "details"
 
     // This is set in the details map to indicate a synthetic resource event (ie not a real one).
     // Examples of synthetic resource events are those that are implicitly generated at the
     // end of the billing period (e.g. `OFF`s).
-    final val details_aquarium_is_synthetic    = "__aquarium_is_synthetic__"
-
-    final val details_aquarium_is_implicit_end = "__aquarium_is_implicit_end__"
-  }
+//    final val details_aquarium_is_synthetic    = "__aquarium_is_synthetic__"
+//
+//    final val details_aquarium_is_implicit_end = "__aquarium_is_implicit_end__"
+//  }
 }
\ No newline at end of file
index 96561f5..a478772 100644 (file)
@@ -63,7 +63,7 @@ case class WalletEntry(
     eventVersion: String = "1.0",
     userID: String = "",
     details: Map[String, String] = Map()
-) extends AquariumEventModel {
+) extends ExternalEventModel {
 
 
   assert(occurredMillis > 0)
@@ -78,6 +78,9 @@ case class WalletEntry(
 
   def withReceivedMillis(millis: Long) = copy(receivedMillis = millis)
 
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
+    this.copy(details = newDetails, occurredMillis = newOccurredMillis)
+
   def occurredDate = new Date(occurredMillis)
   def receivedDate = new Date(receivedMillis)
 }
index 43a88d8..56fbe25 100644 (file)
@@ -35,7 +35,7 @@
 
 package gr.grnet.aquarium.event.im
 
-import gr.grnet.aquarium.event.AquariumEventModel
+import gr.grnet.aquarium.event.ExternalEventModel
 
 /**
  * The model of any event sent from the `Identity Management` (IM) external system.
@@ -45,7 +45,7 @@ import gr.grnet.aquarium.event.AquariumEventModel
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
 
-trait IMEventModel extends AquariumEventModel {
+trait IMEventModel extends ExternalEventModel {
   def clientID: String
 
   def isActive: Boolean
@@ -64,7 +64,7 @@ trait IMEventModel extends AquariumEventModel {
 }
 
 object IMEventModel {
-  trait NamesT extends AquariumEventModel.NamesT {
+  trait NamesT extends ExternalEventModel.NamesT {
     final val clientID = "clientID"
     final val isActive = "isActive"
     final val role = "role"
index dab1058..27ad155 100644 (file)
@@ -35,7 +35,8 @@
 
 package gr.grnet.aquarium.event.im
 
-import gr.grnet.aquarium.event.{AquariumEventModel, AquariumEventSkeleton}
+import gr.grnet.aquarium.util.makeString
+import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
 
 
 /**
@@ -43,28 +44,48 @@ import gr.grnet.aquarium.event.{AquariumEventModel, AquariumEventSkeleton}
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
 
-class StdIMEvent(
-    val id: String, // The id at the sender side
-    val occurredMillis: Long, // When it occurred at the sender side
-    val receivedMillis: Long, // When it was received by Aquarium
-    val userID: String,
-    val clientID: String,
-    val isActive: Boolean,
-    val role: String,
-    val eventVersion: String,
-    val eventType: String,
-    val details: Map[String, String])
-extends AquariumEventModel with IMEventModel {
-  def withReceivedMillis(newReceivedMillis: Long) = new StdIMEvent(
-    id,
-    occurredMillis,
-    newReceivedMillis,
-    userID,
-    clientID,
-    isActive,
-    role,
-    eventVersion,
-    eventType,
-    details
-  )
+case class StdIMEvent(
+    id: String, // The id at the sender side
+    occurredMillis: Long, // When it occurred at the sender side
+    receivedMillis: Long, // When it was received by Aquarium
+    userID: String,
+    clientID: String,
+    isActive: Boolean,
+    role: String,
+    eventVersion: String,
+    eventType: String,
+    details: Map[String, String])
+extends IMEventModel {
+  def withReceivedMillis(newReceivedMillis: Long) =
+    this.copy(receivedMillis = newReceivedMillis)
+
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
+    this.copy(details = newDetails, occurredMillis = newOccurredMillis)
+}
+
+object StdIMEvent {
+  final def fromJsonString(json: String): StdIMEvent = {
+    StdConverters.AllConverters.convertEx[StdIMEvent](JsonTextFormat(json))
+  }
+
+  final def fromJsonBytes(jsonBytes: Array[Byte]): StdIMEvent = {
+    fromJsonString(makeString(jsonBytes))
+  }
+
+  final def fromOther(event: IMEventModel): StdIMEvent = {
+    if(event.isInstanceOf[StdIMEvent]) event.asInstanceOf[StdIMEvent]
+    else new StdIMEvent(
+      event.id,
+      event.occurredMillis,
+      event.receivedMillis,
+      event.userID,
+      event.clientID,
+      event.isActive,
+      event.role,
+      event.eventVersion,
+      event.eventType,
+      event.details
+    )
+  }
+
 }
diff --git a/src/main/scala/gr/grnet/aquarium/event/resource/ResourceEventModel.scala b/src/main/scala/gr/grnet/aquarium/event/resource/ResourceEventModel.scala
new file mode 100644 (file)
index 0000000..3ad3784
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.event
+package resource
+
+import java.util.Date
+import gr.grnet.aquarium.util.date.MutableDateCalc
+
+/**
+ * The model of any resource event.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+trait ResourceEventModel extends ExternalEventModel {
+  /**
+   * Identifies the client that sent this event.
+   */
+  def clientID: String
+
+  /**
+   * String representation of the resource type (e.g. "bndup", "vmtime").
+   */
+  def resource: String
+
+  /**
+   * String representation of the resource instance id
+   */
+  def instanceID: String
+
+
+  /**
+   * The resource value.
+   */
+  def value: Double
+
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long): ResourceEventModel
+
+  def withDetailsAndValue(newDetails: Map[String, String], newValue: Double, newOccurredMillis: Long): ResourceEventModel
+
+  def safeResource   = if(resource eq null)   "" else resource
+  def safeInstanceId = if(instanceID eq null) "" else instanceID
+
+  def fullResourceInfo = (safeResource, safeInstanceId)
+
+  def occurredDate = new Date(occurredMillis)
+
+  def isOccurredWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
+    require(fromMillis <= toMillis, "fromMillis <= toMillis")
+    fromMillis <= occurredMillis && occurredMillis <= toMillis
+  }
+
+  def isReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
+    require(fromMillis <= toMillis, "fromMillis <= toMillis")
+    fromMillis <= receivedMillis && receivedMillis <= toMillis
+  }
+
+  def isOccurredOrReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
+    isOccurredWithinMillis(fromMillis, toMillis) ||
+    isReceivedWithinMillis(fromMillis, toMillis)
+  }
+
+  def isOutOfSyncForBillingMonth(yearOfBillingMonth: Int, billingMonth: Int) = {
+    val billingStartDateCalc = new MutableDateCalc(yearOfBillingMonth, billingMonth)
+    val billingStartMillis = billingStartDateCalc.toMillis
+    // NOTE: no need to `copy` the mutable `billingStartDateCalc` here because we use it once
+    val billingStopMillis  = billingStartDateCalc.goEndOfThisMonth.toMillis
+
+    isOutOfSyncForBillingPeriod(billingStartMillis, billingStopMillis)
+  }
+
+  def isOutOfSyncForBillingPeriod(billingStartMillis: Long, billingStopMillis: Long): Boolean = {
+    isReceivedWithinMillis(billingStartMillis, billingStopMillis) &&
+    (occurredMillis < billingStartMillis || occurredMillis > billingStopMillis)
+  }
+
+  def toDebugString(useOnlyInstanceId: Boolean = false): String = {
+    val instanceInfo = if(useOnlyInstanceId) instanceID else "%s::%s".format(resource, instanceID)
+    val occurredFormatted = new MutableDateCalc(occurredMillis).toYYYYMMDDHHMMSS
+    if(occurredMillis == receivedMillis) {
+      "%sEVENT(%s, [%s], %s, %s, %s, %s, %s)".format(
+        if(isSynthetic) "*" else "",
+        id,
+        occurredFormatted,
+        value,
+        instanceInfo,
+        details,
+        userID,
+        clientID
+      )
+    } else {
+      "%sEVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format(
+        if(isSynthetic) "*" else "",
+        id,
+        occurredFormatted,
+        new MutableDateCalc(receivedMillis),
+        value,
+        instanceInfo,
+        details,
+        userID,
+        clientID
+      )
+    }
+  }
+
+  /**
+   * `Synthetic` means that Aquarium has manufactured this resource event for some purpose. For example, the implicitly
+   * issued resource events at the end a a billing period.
+   *
+   * @return `true` iff this resource event is synthetic.
+   */
+  def isSynthetic = {
+    details contains ResourceEventModel.Names.details_aquarium_is_synthetic
+  }
+
+}
+
+object ResourceEventModel {
+  type ResourceType = String
+  type ResourceIdType = String
+  type FullResourceType = (ResourceType, ResourceIdType)
+  type FullResourceTypeMap = Map[FullResourceType, ResourceEventModel]
+  type FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEventModel]
+
+  trait NamesT extends ExternalEventModel.NamesT {
+    final val clientID = "clientID"
+    final val resource = "resource"
+    final val instanceID = "instanceID"
+    final val value = "value"
+
+    // This is set in the details map to indicate a synthetic resource event (ie not a real one).
+    // Examples of synthetic resource events are those that are implicitly generated at the
+    // end of the billing period (e.g. `OFF`s).
+    final val details_aquarium_is_synthetic    = "__aquarium_is_synthetic__"
+
+    final val details_aquarium_is_implicit_end = "__aquarium_is_implicit_end__"
+  }
+
+  object Names extends NamesT
+
+  def setAquariumSyntheticAndImplicitEnd(map: Map[String, String]): Map[String, String] = {
+    map.
+      updated(Names.details_aquarium_is_synthetic, "true").
+      updated(Names.details_aquarium_is_implicit_end, "true")
+  }
+
+}
diff --git a/src/main/scala/gr/grnet/aquarium/event/resource/StdResourceEvent.scala b/src/main/scala/gr/grnet/aquarium/event/resource/StdResourceEvent.scala
new file mode 100644 (file)
index 0000000..a350724
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.event
+package resource
+
+import gr.grnet.aquarium.util.makeString
+import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
+
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class StdResourceEvent(
+    id: String,
+    occurredMillis: Long,
+    receivedMillis: Long,
+    userID: String,
+    clientID: String,
+    resource: String,
+    instanceID: String,
+    value: Double,
+    eventVersion: String,
+    details: Map[String, String]
+) extends ResourceEventModel {
+  def withReceivedMillis(newReceivedMillis: Long) =
+    this.copy(receivedMillis = newReceivedMillis)
+
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
+    this.copy(details = newDetails, occurredMillis = newOccurredMillis)
+
+  def withDetailsAndValue(newDetails: Map[String, String], newValue: Double, newOccurredMillis: Long) =
+    this.copy(details = newDetails, value = newValue, occurredMillis = newOccurredMillis)
+}
+
+object StdResourceEvent {
+  final def fromJsonString(json: String): StdResourceEvent = {
+    StdConverters.AllConverters.convertEx[StdResourceEvent](JsonTextFormat(json))
+  }
+
+  final def fromJsonBytes(jsonBytes: Array[Byte]): StdResourceEvent = {
+    fromJsonString(makeString(jsonBytes))
+  }
+
+  final def fromOther(event: ResourceEventModel): StdResourceEvent = {
+    if(event.isInstanceOf[StdResourceEvent]) event.asInstanceOf[StdResourceEvent]
+    else {
+      import event._
+      new StdResourceEvent(
+        id,
+        occurredMillis,
+        receivedMillis,
+        userID,
+        clientID,
+        resource,
+        instanceID,
+        value,
+        event.eventVersion,
+        event.details
+      )
+    }
+}
+}
\ No newline at end of file
index ed42c34..f715200 100644 (file)
@@ -44,8 +44,9 @@ import com.ckkloverdos.maybe.{NoVal, Maybe, Failed, Just}
 import gr.grnet.aquarium.util.{ContextualLogger, CryptoUtils, Loggable}
 import gr.grnet.aquarium.store.PolicyStore
 import gr.grnet.aquarium.AquariumException
-import gr.grnet.aquarium.event.{WalletEntry, ResourceEvent}
+import gr.grnet.aquarium.event.{WalletEntry}
 import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * A timeslot together with the algorithm and unit price that apply for this particular timeslot.
@@ -252,8 +253,8 @@ trait Accounting extends DSLUtils with Loggable {
    * Compute the charge slots generated by a particular resource event.
    *
    */
-  def computeFullChargeslots(previousResourceEventM: Maybe[ResourceEvent],
-                             currentResourceEvent: ResourceEvent,
+  def computeFullChargeslots(previousResourceEventM: Maybe[ResourceEventModel],
+                             currentResourceEvent: ResourceEventModel,
                              oldCredits: Double,
                              oldTotalAmount: Double,
                              newTotalAmount: Double,
@@ -407,7 +408,7 @@ trait Accounting extends DSLUtils with Loggable {
    * @param previousAmount The current state of the resource
    * @param previousOccurred The last time the resource state was updated
    */
-  def chargeEvent(currentResourceEvent: ResourceEvent,
+  def chargeEvent(currentResourceEvent: ResourceEventModel,
                   agreements: SortedMap[Timeslot, String],
                   previousAmount: Double,
                   previousOccurred: Date,
@@ -487,7 +488,7 @@ trait Accounting extends DSLUtils with Loggable {
    *                  resource event boundaries
    * @return A list of wallet entries, one for each
    */
-  def chargeEvent(event: ResourceEvent,
+  def chargeEvent(event: ResourceEventModel,
                   agr: DSLAgreement,
                   previousAmount: Double,
                   previousOccurred: Date,
@@ -499,8 +500,8 @@ trait Accounting extends DSLUtils with Loggable {
     chargeFor.map{x => assert(true,
       Timeslot(previousOccurred, new Date(event.occurredMillis)))}
 
-    if (!event.validate())
-      return Failed(new AccountingException("Event not valid"))
+//    if (!event.validate())
+//      return Failed(new AccountingException("Event not valid"))
 
     val policy = Policy.policy
     val dslResource = policy.findResource(event.resource) match {
index c193466..7a0b78a 100644 (file)
@@ -36,8 +36,8 @@
 package gr.grnet.aquarium.logic.accounting.dsl
 
 import com.ckkloverdos.maybe.{NoVal, Failed, Just, Maybe}
-import gr.grnet.aquarium.event.ResourceEvent
 import gr.grnet.aquarium.AquariumException
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * A cost policy indicates how charging for a resource will be done
@@ -112,7 +112,7 @@ abstract class DSLCostPolicy(val name: String, val vars: Set[DSLCostPolicyVar])
    * in which case it is ignored.
    *
    * @param oldAmount the old accumulating amount
-   * @param newEventValue the value contained in a newly arrived [[gr.grnet.aquarium.event.ResourceEvent]]
+   * @param newEventValue the value contained in a newly arrived [[gr.grnet.aquarium.event.resource.ResourceEventModel]]
    * @return
    */
   def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double): Double
@@ -168,13 +168,13 @@ abstract class DSLCostPolicy(val name: String, val vars: Set[DSLCostPolicyVar])
    */
   def supportsImplicitEvents: Boolean
 
-  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEvent): Boolean
+  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel): Boolean
 
   @throws(classOf[Exception])
-  def constructImplicitEndEventFor(resourceEvent: ResourceEvent, newOccurredMillis: Long): ResourceEvent
+  def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long): ResourceEventModel
 
   @throws(classOf[Exception])
-  def constructImplicitStartEventFor(resourceEvent: ResourceEvent): ResourceEvent
+  def constructImplicitStartEventFor(resourceEvent: ResourceEventModel): ResourceEventModel
 }
 
 object DSLCostPolicyNames {
@@ -247,13 +247,13 @@ case object OnceCostPolicy
 
   def supportsImplicitEvents = false
 
-  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEvent) = false
+  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = false
 
-  def constructImplicitEndEventFor(resourceEvent: ResourceEvent, occurredMillis: Long) = {
+  def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, occurredMillis: Long) = {
     throw new AquariumException("constructImplicitEndEventFor() Not compliant with %s".format(this))
   }
 
-  def constructImplicitStartEventFor(resourceEvent: ResourceEvent) = {
+  def constructImplicitStartEventFor(resourceEvent: ResourceEventModel) = {
     throw new AquariumException("constructImplicitStartEventFor() Not compliant with %s".format(this))
   }
 }
@@ -293,25 +293,20 @@ case object ContinuousCostPolicy
     true
   }
 
-  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEvent) = {
+  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
     true
   }
 
-  def constructImplicitEndEventFor(resourceEvent: ResourceEvent, newOccurredMillis: Long) = {
+  def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
     assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
 
     val details = resourceEvent.details
-    val newDetails = ResourceEvent.setAquariumSyntheticAndImplicitEnd(details)
-    val newValue   = resourceEvent.value
+    val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
 
-    resourceEvent.copy(
-      occurredMillis = newOccurredMillis,
-      details = newDetails,
-      value = newValue
-    )
+    resourceEvent.withDetails(newDetails, newOccurredMillis)
   }
 
-  def constructImplicitStartEventFor(resourceEvent: ResourceEvent) = {
+  def constructImplicitStartEventFor(resourceEvent: ResourceEventModel) = {
     throw new AquariumException("constructImplicitStartEventFor() Not compliant with %s".format(this))
   }
 }
@@ -396,28 +391,24 @@ case object OnOffCostPolicy
   }
 
 
-  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEvent) = {
+  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
     // If we have ON events with no OFF companions at the end of the billing period,
     // then we must generate implicit OFF events.
     OnOffCostPolicyValues.isONValue(resourceEvent.value)
   }
 
-  def constructImplicitEndEventFor(resourceEvent: ResourceEvent, newOccurredMillis: Long) = {
+  def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
     assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
     assert(OnOffCostPolicyValues.isONValue(resourceEvent.value))
 
     val details = resourceEvent.details
-    val newDetails = ResourceEvent.setAquariumSyntheticAndImplicitEnd(details)
+    val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
     val newValue   = OnOffCostPolicyValues.OFF
 
-    resourceEvent.copy(
-      occurredMillis = newOccurredMillis,
-      details = newDetails,
-      value = newValue
-    )
+    resourceEvent.withDetailsAndValue(newDetails, newValue, newOccurredMillis)
   }
 
-  def constructImplicitStartEventFor(resourceEvent: ResourceEvent) = {
+  def constructImplicitStartEventFor(resourceEvent: ResourceEventModel) = {
     throw new AquariumException("constructImplicitStartEventFor() Not compliant with %s".format(this))
   }
 }
@@ -466,15 +457,15 @@ case object DiscreteCostPolicy extends DSLCostPolicy(DSLCostPolicyNames.discrete
     false
   }
 
-  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEvent) = {
+  def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
     false
   }
 
-  def constructImplicitEndEventFor(resourceEvent: ResourceEvent, occurredMillis: Long) = {
+  def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, occurredMillis: Long) = {
     throw new AquariumException("constructImplicitEndEventFor() Not compliant with %s".format(this))
   }
 
-  def constructImplicitStartEventFor(resourceEvent: ResourceEvent) = {
+  def constructImplicitStartEventFor(resourceEvent: ResourceEventModel) = {
     throw new AquariumException("constructImplicitStartEventFor() Not compliant with %s".format(this))
   }
 }
index 61d794f..e2a4fcb 100644 (file)
@@ -51,7 +51,7 @@ import java.util.concurrent.{ConcurrentHashMap, ConcurrentSkipListSet}
 import com.ckkloverdos.maybe._
 import gr.grnet.aquarium.util.date.TimeHelpers
 import gr.grnet.aquarium.{AquariumException, Configurator}
-import gr.grnet.aquarium.event.AquariumEventModel
+import gr.grnet.aquarium.event.ExternalEventModel
 
 /**
  * An abstract service that retrieves Aquarium events from a queue,
@@ -63,7 +63,7 @@ import gr.grnet.aquarium.event.AquariumEventModel
  *
  * @author Georgios Gousios <gousiosg@gmail.com>
  */
-abstract class EventProcessorService[E <: AquariumEventModel] extends AkkaAMQP with Loggable with Lifecycle {
+abstract class EventProcessorService[E <: ExternalEventModel] extends AkkaAMQP with Loggable with Lifecycle {
 
   /* Messages exchanged between the persister and the queuereader */
   case class AckData(msgId: String, deliveryTag: Long, queue: ActorRef)
index c153504..d144335 100644 (file)
@@ -43,8 +43,8 @@ import gr.grnet.aquarium.actor.message.service.router.ProcessIMEvent
 import gr.grnet.aquarium.util.date.TimeHelpers
 import gr.grnet.aquarium.util.makeString
 import com.ckkloverdos.maybe._
-import gr.grnet.aquarium.event.im.IMEventModel
 import gr.grnet.aquarium.store.memory.MemStore
+import gr.grnet.aquarium.event.im.{StdIMEvent, IMEventModel}
 
 /**
  * An event processor service for user events coming from the IM system
@@ -54,7 +54,7 @@ import gr.grnet.aquarium.store.memory.MemStore
 class IMEventProcessorService extends EventProcessorService[IMEventModel] {
 
   override def parseJsonBytes(data: Array[Byte]) = {
-    MemStore.createIMEventFromJsonBytes(data)
+    StdIMEvent.fromJsonBytes(data)
   }
 
   override def forward(event: IMEventModel) = {
index 1c3db72..f61cb5b 100644 (file)
@@ -38,10 +38,10 @@ package gr.grnet.aquarium.service
 import gr.grnet.aquarium.actor.RouterRole
 import gr.grnet.aquarium.Configurator.Keys
 import gr.grnet.aquarium.store.LocalFSEventStore
-import com.ckkloverdos.maybe.{Maybe, Just, Failed, NoVal}
+import com.ckkloverdos.maybe.Maybe
 import gr.grnet.aquarium.actor.message.service.router.ProcessResourceEvent
-import gr.grnet.aquarium.event.ResourceEvent
 import gr.grnet.aquarium.util.date.TimeHelpers
+import gr.grnet.aquarium.event.resource.{StdResourceEvent, ResourceEventModel}
 
 
 /**
@@ -49,21 +49,23 @@ import gr.grnet.aquarium.util.date.TimeHelpers
  *
  * @author Georgios Gousios <gousiosg@gmail.com>
  */
-final class ResourceEventProcessorService extends EventProcessorService[ResourceEvent] {
+final class ResourceEventProcessorService extends EventProcessorService[ResourceEventModel] {
 
-  override def parseJsonBytes(data: Array[Byte]) = ResourceEvent.fromBytes(data)
+  override def parseJsonBytes(data: Array[Byte]) = {
+    StdResourceEvent.fromJsonBytes(data)
+  }
 
-  override def forward(event: ResourceEvent): Unit = {
+  override def forward(event: ResourceEventModel): Unit = {
     if(event ne null) {
       val businessLogicDispacther = _configurator.actorProvider.actorForRole(RouterRole)
       businessLogicDispacther ! ProcessResourceEvent(event)
     }
   }
 
-  override def existsInStore(event: ResourceEvent): Boolean =
-    _configurator.resourceEventStore.findResourceEventById(event.id).isJust
+  override def existsInStore(event: ResourceEventModel): Boolean =
+    _configurator.resourceEventStore.findResourceEventById(event.id).isDefined
 
-  override def storeParsedEvent(event: ResourceEvent, initialPayload: Array[Byte]): Unit = {
+  override def storeParsedEvent(event: ResourceEventModel, initialPayload: Array[Byte]): Unit = {
     // 1. Store to local FS for debugging purposes.
     //    BUT be resilient to errors, since this is not critical
     if(_configurator.eventsStoreFolder.isJust) {
@@ -73,7 +75,7 @@ final class ResourceEventProcessorService extends EventProcessorService[Resource
     }
 
     // 2. Store to DB
-    _configurator.resourceEventStore.storeResourceEvent(event)
+    _configurator.resourceEventStore.insertResourceEvent(event)
   }
 
 
index 9da57bb..18a8e35 100644 (file)
@@ -35,7 +35,7 @@
 
 package gr.grnet.aquarium.simulation
 
-import gr.grnet.aquarium.event.ResourceEvent
+import gr.grnet.aquarium.event.resource.StdResourceEvent
 
 
 /**
@@ -57,7 +57,7 @@ class ResourceInstanceSim (val resource: ResourceSim,
                        details: Map[String, String],
                        eventVersion: String = "1.0") = {
 
-    val event = ResourceEvent(
+    val event = StdResourceEvent(
       uidGen.nextUID(),
       occurredMillis,
       receivedMillis,
@@ -65,8 +65,8 @@ class ResourceInstanceSim (val resource: ResourceSim,
       client.clientId,
       resource.name,
       instanceId,
-      eventVersion,
       value,
+      eventVersion,
       details
     )
 
index 532c22d..b82e5f3 100644 (file)
@@ -36,9 +36,7 @@
 package gr.grnet.aquarium.simulation
 
 import java.util.Date
-import com.ckkloverdos.maybe.Maybe
 import gr.grnet.aquarium.store.RecordID
-import gr.grnet.aquarium.event.ResourceEvent
 
 /**
  * A simulator for an instance of the standard `bandwidth` resource.
@@ -54,7 +52,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
   def useBandwidth(occurredDate: Date,
                    megaBytes: Double,
                    details: Map[String, String] = Map(),
-                   eventVersion: String = "1.0"): RecordID = {
+                   eventVersion: String = "1.0") = {
 
     newResourceEvent(
      occurredDate.getTime,
index 55c8555..6c6d1df 100644 (file)
@@ -35,7 +35,6 @@
 
 package gr.grnet.aquarium.simulation
 
-import gr.grnet.aquarium.event.ResourceEvent
 import gr.grnet.aquarium.store.RecordID
 import com.ckkloverdos.maybe.Maybe
 import java.util.Date
@@ -54,7 +53,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
   def consumeMB(occurredDate: Date,
                 megaBytes: Double,
                 details: Map[String, String] = Map(),
-                eventVersion: String = "1.0"): RecordID = {
+                eventVersion: String = "1.0") = {
     newResourceEvent(
       occurredDate.getTime,
       occurredDate.getTime,
@@ -68,7 +67,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
   def freeMB(occurredDate: Date,
              megaBytes: Double,
              details: Map[String, String] = Map(),
-             eventVersion: String = "1.0"): RecordID = {
+             eventVersion: String = "1.0") = {
 
     consumeMB(occurredDate, -megaBytes)
   }
@@ -77,7 +76,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
                           outOfSyncHours: Int,
                           megaBytes: Double,
                           details: Map[String, String] = Map(),
-                          eventVersion: String = "1.0"): RecordID = {
+                          eventVersion: String = "1.0") = {
 
     val occurredDateCalc = new MutableDateCalc(occurredDate)
     val occurredTime = occurredDateCalc.toMillis
@@ -96,7 +95,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
                        outOfSyncHours: Int,
                        megaBytes: Double,
                        details: Map[String, String] = Map(),
-                       eventVersion: String = "1.0"): RecordID = {
+                       eventVersion: String = "1.0") = {
 
     consumeMB_OutOfSync(occurredDate, outOfSyncHours, -megaBytes)
   }
index c69a30c..75e234e 100644 (file)
@@ -37,10 +37,9 @@ package gr.grnet.aquarium.simulation
 
 import gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicyValues
 import gr.grnet.aquarium.store.RecordID
-import com.ckkloverdos.maybe.Maybe
 import java.util.Date
 import gr.grnet.aquarium.util.date.MutableDateCalc
-import gr.grnet.aquarium.event.ResourceEvent
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * A simulator for an instance of the standard `vmtime` resource.
@@ -56,7 +55,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
 
   def newON(occurredDate: Date,
             details: Map[String, String] = Map(),
-            eventVersion: String = "1.0"): RecordID = {
+            eventVersion: String = "1.0") = {
     newResourceEvent(
       occurredDate.getTime,
       occurredDate.getTime,
@@ -68,7 +67,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
 
   def newOFF(occurredDate: Date,
              details: Map[String, String] = Map(),
-             eventVersion: String = "1.0"): RecordID = {
+             eventVersion: String = "1.0") = {
     newResourceEvent(
       occurredDate.getTime,
       occurredDate.getTime,
@@ -78,7 +77,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
     )
   }
 
-  def newONOFF(occurredDateForON: Date, totalVMTimeInHours: Int): (RecordID, RecordID) = {
+  def newONOFF(occurredDateForON: Date, totalVMTimeInHours: Int): (ResourceEventModel, ResourceEventModel) = {
     val onID = newON(occurredDateForON)
     val offDate = new MutableDateCalc(occurredDateForON).goPlusHours(totalVMTimeInHours).toDate
     val offID = newOFF(offDate)
@@ -89,7 +88,7 @@ extends ResourceInstanceSim(resource, instanceId, owner, client) {
   def newOFF_OutOfSync(occuredDate: Date,
                        outOfSyncHours: Int,
                        details: Map[String, String] = Map(),
-                       eventVersion: String = "1.0"): RecordID = {
+                       eventVersion: String = "1.0") = {
 
     val occurredDateCalc = new MutableDateCalc(occuredDate)
     val occurredTime = occurredDateCalc.toMillis
index 6cfdb48..6355e74 100644 (file)
@@ -37,7 +37,7 @@ package gr.grnet.aquarium.simulation
 import java.util.Date
 import gr.grnet.aquarium.store.RecordID
 import math.Ordering
-import gr.grnet.aquarium.event.ResourceEvent
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * A simulator for a user.
@@ -50,17 +50,17 @@ case class UserSim(userId: String, userCreationDate: Date, aquarium: AquariumSim
   def resourceEventStore = aquarium.resourceEventStore
 
   private[simulation]
-  def _addResourceEvent(resourceEvent: ResourceEvent): RecordID = {
-    resourceEventStore.storeResourceEvent(resourceEvent)
+  def _addResourceEvent(resourceEvent: ResourceEventModel): ResourceEventModel = {
+    resourceEventStore.insertResourceEvent(resourceEvent)
   }
 
-  def myResourceEvents: List[ResourceEvent] = {
+  def myResourceEvents: List[ResourceEventModel] = {
     resourceEventStore.findResourceEventsByUserId(userId)(None)
   }
 
-  def myResourceEventsByReceivedDate: List[ResourceEvent] = {
-    myResourceEvents.sorted(new Ordering[ResourceEvent] {
-      def compare(x: ResourceEvent, y: ResourceEvent) = {
+  def myResourceEventsByReceivedDate: List[ResourceEventModel] = {
+    myResourceEvents.sorted(new Ordering[ResourceEventModel] {
+      def compare(x: ResourceEventModel, y: ResourceEventModel) = {
         if(x.receivedMillis < y.receivedMillis)
           -1
         else if(x.receivedMillis == y.receivedMillis)
@@ -71,9 +71,9 @@ case class UserSim(userId: String, userCreationDate: Date, aquarium: AquariumSim
     })
   }
 
-  def myResourceEventsByOccurredDate: List[ResourceEvent] = {
-    myResourceEvents.sorted(new Ordering[ResourceEvent] {
-      def compare(x: ResourceEvent, y: ResourceEvent) = {
+  def myResourceEventsByOccurredDate: List[ResourceEventModel] = {
+    myResourceEvents.sorted(new Ordering[ResourceEventModel] {
+      def compare(x: ResourceEventModel, y: ResourceEventModel) = {
         if(x.occurredMillis < y.occurredMillis)
           -1
         else if(x.occurredMillis == y.occurredMillis)
index 609d808..8dd4e80 100644 (file)
@@ -35,9 +35,9 @@
 
 package gr.grnet.aquarium.store
 
-import com.ckkloverdos.maybe.Maybe
 import gr.grnet.aquarium.event.im.IMEventModel
 import gr.grnet.aquarium.util.makeString
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * Store for external user events
@@ -47,8 +47,7 @@ import gr.grnet.aquarium.util.makeString
  */
 trait IMEventStore {
   type IMEvent <: IMEventModel
-
-  def isLocalIMEvent(event: IMEventModel): Boolean
+  type ResourceEvent <: ResourceEventModel
 
   def createIMEventFromJson(json: String): IMEvent
 
@@ -58,8 +57,6 @@ trait IMEventStore {
 
   def createIMEventFromOther(event: IMEventModel): IMEvent
 
-//  def storeUnparsed(json: String): Maybe[RecordID]
-
   /**
    * Insert a new event into the store.
    */
@@ -68,10 +65,5 @@ trait IMEventStore {
   /**
    * Find a user event by event ID
    */
-  def findIMEventById(id: String): Maybe[IMEvent]
-
-  /**
-   * Find all user events by user ID
-   */
-  def findIMEventsByUserId(userId: String): List[IMEvent]
+  def findIMEventById(id: String): Option[IMEvent]
 }
\ No newline at end of file
index 5e7c032..aa196d5 100644 (file)
@@ -39,8 +39,8 @@ import gr.grnet.aquarium.Configurator
 import java.io.{FileOutputStream, File}
 import gr.grnet.aquarium.util.{Loggable, stringOfStackTrace}
 import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
-import gr.grnet.aquarium.event.ResourceEvent
 import gr.grnet.aquarium.event.im.IMEventModel
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * This is used whenever the property `events.store.folder` is setup in aquarium configuration.
@@ -112,7 +112,7 @@ object LocalFSEventStore extends Loggable {
     }
   }
 
-  def storeResourceEvent(mc: Configurator, event: ResourceEvent, initialPayload: Array[Byte]): Unit = {
+  def storeResourceEvent(mc: Configurator, event: ResourceEventModel, initialPayload: Array[Byte]): Unit = {
     require(event ne null, "Resource event must be not null")
 
     for(root <- mc.eventsStoreFolder) {
index 2d00e20..f263dd9 100644 (file)
@@ -36,8 +36,8 @@
 package gr.grnet.aquarium.store
 
 import com.ckkloverdos.maybe.Maybe
-import gr.grnet.aquarium.event.ResourceEvent
 import gr.grnet.aquarium.AquariumException
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * An abstraction for Aquarium `ResourceEvent` stores.
@@ -46,13 +46,17 @@ import gr.grnet.aquarium.AquariumException
  * @author Georgios Gousios <gousiosg@gmail.com>.
  */
 trait ResourceEventStore {
+  type ResourceEvent <: ResourceEventModel
+
+  def createResourceEventFromOther(event: ResourceEventModel): ResourceEvent
+
   def clearResourceEvents(): Unit = {
     throw new AquariumException("Unsupported operation")
   }
 
-  def storeResourceEvent(event: ResourceEvent): RecordID
+  def insertResourceEvent(event: ResourceEventModel): ResourceEvent
 
-  def findResourceEventById(id: String): Maybe[ResourceEvent]
+  def findResourceEventById(id: String): Option[ResourceEvent]
 
   def findResourceEventsByUserId(userId: String)(sortWith: Option[(ResourceEvent, ResourceEvent) => Boolean]): List[ResourceEvent]
 
index 7da8cd0..2300ee3 100644 (file)
@@ -45,11 +45,12 @@ import java.util.concurrent.ConcurrentHashMap
 import gr.grnet.aquarium.user.UserState
 import gr.grnet.aquarium.uid.ConcurrentVMLocalUIDGenerator
 import gr.grnet.aquarium.{AquariumException, Configurable}
-import gr.grnet.aquarium.event.{WalletEntry, ResourceEvent, PolicyEntry}
+import gr.grnet.aquarium.event.{WalletEntry, PolicyEntry}
 import gr.grnet.aquarium.converter.JsonTextFormat
 import gr.grnet.aquarium.util._
 import gr.grnet.aquarium.event.im.{StdIMEvent, IMEventModel}
 import org.bson.types.ObjectId
+import gr.grnet.aquarium.event.resource.{StdResourceEvent, ResourceEventModel}
 
 /**
  * An implementation of various stores that persists data in memory.
@@ -67,6 +68,7 @@ class MemStore extends UserStateStore
   with StoreProvider {
 
   override type IMEvent = MemIMEvent
+  override type ResourceEvent = MemResourceEvent
 
   private[this] val idGen = new ConcurrentVMLocalUIDGenerator(1000)
   
@@ -202,21 +204,37 @@ class MemStore extends UserStateStore
   //- WalletEntryStore
 
   //+ ResourceEventStore
+  def createResourceEventFromOther(event: ResourceEventModel): ResourceEvent = {
+    if(event.isInstanceOf[MemResourceEvent]) event.asInstanceOf[MemResourceEvent]
+    else {
+      import event._
+      new StdResourceEvent(
+        id,
+        occurredMillis,
+        receivedMillis,
+        userID,
+        clientID,
+        resource,
+        instanceID,
+        value,
+        eventVersion,
+        details
+      ): MemResourceEvent
+    }
+  }
 
   override def clearResourceEvents() = {
     _resourceEvents = Nil
   }
 
-  def storeResourceEvent(event: ResourceEvent) = {
-    _resourceEvents ::= event
-    RecordID(event.id)
+  def insertResourceEvent(event: ResourceEventModel) = {
+    val localEvent = createResourceEventFromOther(event)
+    _resourceEvents ::= localEvent
+    localEvent
   }
 
   def findResourceEventById(id: String) = {
-    _resourceEvents.find(ev ⇒ ev.id == id) match {
-      case Some(ev) ⇒ Just(ev)
-      case None     ⇒ NoVal
-    }
+    _resourceEvents.find(ev ⇒ ev.id == id)
   }
 
   def findResourceEventsByUserId(userId: String)
@@ -282,28 +300,20 @@ class MemStore extends UserStateStore
 
   //+ IMEventStore
   def createIMEventFromJson(json: String) = {
-    MemStore.createIMEventFromJson(json)
-  }
-
-  def isLocalIMEvent(event: IMEventModel) = {
-    MemStore.isLocalIMEvent(event)
+    StdIMEvent.fromJsonString(json)
   }
 
   def createIMEventFromOther(event: IMEventModel) = {
-    MemStore.createIMEventFromOther(event)
+    StdIMEvent.fromOther(event)
   }
 
-  def storeUnparsed(json: String) = throw new AquariumException("Not implemented")
-
   def insertIMEvent(event: IMEventModel) = {
     val localEvent = createIMEventFromOther(event)
     imEventById += (event.id -> localEvent)
     localEvent
   }
 
-  def findIMEventById(id: String) = Maybe{imEventById.getOrElse(id, null)}
-
-  def findIMEventsByUserId(userId: String) = imEventById.valuesIterator.filter{v => v.userID == userId}.toList
+  def findIMEventById(id: String) = imEventById.get(id)
   //- IMEventStore
 
   def loadPolicyEntriesAfter(after: Long) =
@@ -328,31 +338,6 @@ class MemStore extends UserStateStore
 }
 
 object MemStore {
-  def createIMEventFromOther(event: IMEventModel) = {
-    if(event.isInstanceOf[MemIMEvent]) event.asInstanceOf[MemIMEvent]
-    else new StdIMEvent(
-      event.id,
-      event.occurredMillis,
-      event.receivedMillis,
-      event.userID,
-      event.clientID,
-      event.isActive,
-      event.role,
-      event.eventVersion,
-      event.eventType,
-      event.details
-    ): MemIMEvent
-  }
-
-  final def createIMEventFromJson(json: String) = {
-    import gr.grnet.aquarium.converter.StdConverters.AllConverters
-    AllConverters.convertEx[MemIMEvent](JsonTextFormat(json))
-  }
-
-  final def createIMEventFromJsonBytes(jsonBytes: Array[Byte]) = {
-    createIMEventFromJson(makeString(jsonBytes))
-  }
-
   final def isLocalIMEvent(event: IMEventModel) = event match {
     case _: MemIMEvent ⇒ true
     case _ ⇒ false
index 376146d..65aeaf4 100644 (file)
@@ -36,7 +36,9 @@
 package gr.grnet.aquarium.store
 
 import gr.grnet.aquarium.event.im.StdIMEvent
+import gr.grnet.aquarium.event.resource.StdResourceEvent
 
 package object memory {
   type MemIMEvent = StdIMEvent
+  type MemResourceEvent = StdResourceEvent
 }
index 0c57a14..fa4ddfa 100644 (file)
 package gr.grnet.aquarium.store.mongodb
 
 import org.bson.types.ObjectId
-import gr.grnet.aquarium.event.AquariumEventModel
+import gr.grnet.aquarium.event.ExternalEventModel
 
 /**
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
 
-trait MongoDBEventModel extends AquariumEventModel {
+trait MongoDBEventModel extends ExternalEventModel {
   def _id: ObjectId
 
   override def storeID: Option[ObjectId] = Option(_id)
index d447d37..e3d274b 100644 (file)
@@ -39,7 +39,9 @@ import org.bson.types.ObjectId
 import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
 import gr.grnet.aquarium.util._
 import gr.grnet.aquarium.event.im.{IMEventModel, StdIMEvent}
-import gr.grnet.aquarium.event.AquariumEventModel
+import gr.grnet.aquarium.event.ExternalEventModel
+import com.mongodb.DBObject
+import com.mongodb.util.JSON
 
 
 /**
@@ -59,21 +61,29 @@ case class MongoDBIMEvent(
    eventType: String,
    details: Map[String, String],
    _id: ObjectId
- ) extends AquariumEventModel with IMEventModel with MongoDBEventModel {
+) extends IMEventModel with MongoDBEventModel {
 
-  override def withReceivedMillis(newReceivedMillis: Long) = this.copy(receivedMillis = newReceivedMillis)
+  def withReceivedMillis(newReceivedMillis: Long) =
+    this.copy(receivedMillis = newReceivedMillis)
+
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
+    this.copy(details = newDetails, occurredMillis = newOccurredMillis)
 }
 
 object MongoDBIMEvent {
-  final def fromJson(json: String): MongoDBIMEvent = {
+  final def fromJsonString(json: String): MongoDBIMEvent = {
     StdConverters.AllConverters.convertEx[MongoDBIMEvent](JsonTextFormat(json))
   }
 
   final def fromJsonBytes(jsonBytes: Array[Byte]): MongoDBIMEvent = {
-    fromJson(makeString(jsonBytes))
+    fromJsonString(makeString(jsonBytes))
+  }
+
+  final def fromDBObject(dbObject: DBObject): MongoDBIMEvent = {
+    fromJsonString(JSON.serialize(dbObject))
   }
 
-  final def fromOther(event: IMEventModel, _id: ObjectId = null): MongoDBIMEvent = {
+  final def fromOther(event: IMEventModel, _id: ObjectId): MongoDBIMEvent = {
     MongoDBIMEvent(
       event.id,
       event.occurredMillis,
diff --git a/src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBResourceEvent.scala b/src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBResourceEvent.scala
new file mode 100644 (file)
index 0000000..c2b7735
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.store.mongodb
+
+import org.bson.types.ObjectId
+import com.mongodb.DBObject
+
+import gr.grnet.aquarium.util.makeString
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+import gr.grnet.aquarium.converter.{StdConverters, JsonTextFormat}
+import com.mongodb.util.JSON
+
+/**
+ * A [[gr.grnet.aquarium.event.resource.ResourceEventModel]] as represented for MongoDB.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class MongoDBResourceEvent(
+  id: String,
+  occurredMillis: Long,
+  receivedMillis: Long,
+  userID: String,
+  clientID: String,
+  resource: String,
+  instanceID: String,
+  value: Double,
+  eventVersion: String,
+  details: Map[String, String],
+  _id: ObjectId
+) extends ResourceEventModel with MongoDBEventModel {
+
+  def withReceivedMillis(newReceivedMillis: Long) =
+    this.copy(receivedMillis = newReceivedMillis)
+
+  def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
+    this.copy(details = newDetails, occurredMillis = newOccurredMillis)
+
+  def withDetailsAndValue(newDetails: Map[String, String], newValue: Double, newOccurredMillis: Long) =
+    this.copy(details = newDetails, value = newValue, occurredMillis = newOccurredMillis)
+}
+
+object MongoDBResourceEvent {
+  final def fromJsonString(json: String): MongoDBResourceEvent = {
+    StdConverters.AllConverters.convertEx[MongoDBResourceEvent](JsonTextFormat(json))
+  }
+
+  final def fromJsonBytes(bytes: Array[Byte]): MongoDBResourceEvent = {
+    fromJsonString(makeString(bytes))
+  }
+
+  final def fromDBObject(dbObject: DBObject): MongoDBResourceEvent = {
+    fromJsonString(JSON.serialize(dbObject))
+  }
+
+
+  final def fromOther(rcEvent: ResourceEventModel, _id: ObjectId): MongoDBResourceEvent = {
+    import rcEvent._
+    MongoDBResourceEvent(
+      id,
+      occurredMillis,
+      receivedMillis,
+      userID,
+      clientID,
+      resource,
+      instanceID,
+      value,
+      eventVersion,
+      details,
+      _id
+    )
+  }
+}
\ No newline at end of file
index e238883..86e73e5 100644 (file)
@@ -37,23 +37,24 @@ package gr.grnet.aquarium.store.mongodb
 
 import com.mongodb.util.JSON
 import gr.grnet.aquarium.user.UserState
-import gr.grnet.aquarium.user.UserState.{JsonNames => UserStateJsonNames}
+import gr.grnet.aquarium.user.UserState.{JsonNames ⇒ UserStateJsonNames}
 import gr.grnet.aquarium.util.json.JsonSupport
 import collection.mutable.ListBuffer
-import gr.grnet.aquarium.event.im.IMEventModel.{Names => IMEventNames}
+import gr.grnet.aquarium.event._
+import gr.grnet.aquarium.event.im.IMEventModel
+import gr.grnet.aquarium.event.im.IMEventModel.{Names ⇒ IMEventNames}
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+import gr.grnet.aquarium.event.resource.ResourceEventModel.{Names ⇒ ResourceEventNames}
 import gr.grnet.aquarium.store._
-import gr.grnet.aquarium.event.ResourceEvent.{JsonNames => ResourceJsonNames}
-import gr.grnet.aquarium.event.WalletEntry.{JsonNames => WalletJsonNames}
-import gr.grnet.aquarium.event.PolicyEntry.{JsonNames => PolicyJsonNames}
+import gr.grnet.aquarium.event.WalletEntry.{JsonNames ⇒ WalletJsonNames}
+import gr.grnet.aquarium.event.PolicyEntry.{JsonNames ⇒ PolicyJsonNames}
 import java.util.Date
 import gr.grnet.aquarium.logic.accounting.Policy
 import com.mongodb._
 import org.bson.types.ObjectId
-import gr.grnet.aquarium.event._
-import com.ckkloverdos.maybe.{NoVal, Maybe}
-import im.IMEventModel
+import com.ckkloverdos.maybe.Maybe
 import gr.grnet.aquarium.util._
-import gr.grnet.aquarium.converter.{Conversions, StdConverters}
+import gr.grnet.aquarium.converter.Conversions
 
 /**
  * Mongodb implementation of the various aquarium stores.
@@ -74,6 +75,7 @@ class MongoDBStore(
   with Loggable {
 
   override type IMEvent = MongoDBIMEvent
+  override type ResourceEvent = MongoDBResourceEvent
 
   private[store] lazy val resourceEvents   = getCollection(MongoDBStore.RESOURCE_EVENTS_COLLECTION)
   private[store] lazy val userStates       = getCollection(MongoDBStore.USER_STATES_COLLECTION)
@@ -91,51 +93,47 @@ class MongoDBStore(
     db.getCollection(name)
   }
 
-  private[this] def _sortByTimestampAsc[A <: AquariumEventModel](one: A, two: A): Boolean = {
+  private[this] def _sortByTimestampAsc[A <: ExternalEventModel](one: A, two: A): Boolean = {
     if (one.occurredMillis > two.occurredMillis) false
     else if (one.occurredMillis < two.occurredMillis) true
     else true
   }
 
-  private[this] def _sortByTimestampDesc[A <: AquariumEventSkeleton](one: A, two: A): Boolean = {
-    if (one.occurredMillis < two.occurredMillis) false
-    else if (one.occurredMillis > two.occurredMillis) true
-    else true
+  //+ResourceEventStore
+  def createResourceEventFromOther(event: ResourceEventModel): ResourceEvent = {
+    MongoDBResourceEvent.fromOther(event, null)
   }
 
-  //+ResourceEventStore
-  def storeResourceEvent(event: ResourceEvent) = {
-    MongoDBStore.storeAny[ResourceEvent](
-      event,
-      resourceEvents,
-      ResourceJsonNames.id,
-      (e) => e.id,
-      MongoDBStore.jsonSupportToDBObject)
+  def insertResourceEvent(event: ResourceEventModel) = {
+    val localEvent = MongoDBResourceEvent.fromOther(event, new ObjectId())
+    MongoDBStore.insertObject(localEvent, resourceEvents, MongoDBStore.jsonSupportToDBObject)
+    localEvent
   }
 
-  def findResourceEventById(id: String): Maybe[ResourceEvent] =
-    MongoDBStore.findById(id, resourceEvents, MongoDBStore.dbObjectToResourceEvent)
+  def findResourceEventById(id: String): Option[ResourceEvent] = {
+    MongoDBStore.findBy(ResourceEventNames.id, id, resourceEvents, MongoDBResourceEvent.fromDBObject)
+  }
 
   def findResourceEventsByUserId(userId: String)
                                 (sortWith: Option[(ResourceEvent, ResourceEvent) => Boolean]): List[ResourceEvent] = {
-    val query = new BasicDBObject(ResourceJsonNames.userId, userId)
+    val query = new BasicDBObject(ResourceEventNames.userID, userId)
 
-    MongoDBStore.runQuery(query, resourceEvents)(MongoDBStore.dbObjectToResourceEvent)(sortWith)
+    MongoDBStore.runQuery(query, resourceEvents)(MongoDBResourceEvent.fromDBObject)(sortWith)
   }
 
   def findResourceEventsByUserIdAfterTimestamp(userId: String, timestamp: Long): List[ResourceEvent] = {
     val query = new BasicDBObject()
-    query.put(ResourceJsonNames.userId, userId)
-    query.put(ResourceJsonNames.occurredMillis, new BasicDBObject("$gt", timestamp))
+    query.put(ResourceEventNames.userID, userId)
+    query.put(ResourceEventNames.occurredMillis, new BasicDBObject("$gt", timestamp))
     
-    val sort = new BasicDBObject(ResourceJsonNames.occurredMillis, 1)
+    val sort = new BasicDBObject(ResourceEventNames.occurredMillis, 1)
 
     val cursor = resourceEvents.find(query).sort(sort)
 
     try {
       val buffer = new scala.collection.mutable.ListBuffer[ResourceEvent]
       while(cursor.hasNext) {
-        buffer += MongoDBStore.dbObjectToResourceEvent(cursor.next())
+        buffer += MongoDBResourceEvent.fromDBObject(cursor.next())
       }
       buffer.toList.sortWith(_sortByTimestampAsc)
     } finally {
@@ -146,27 +144,27 @@ class MongoDBStore(
   def findResourceEventHistory(userId: String, resName: String,
                                instid: Option[String], upTo: Long) : List[ResourceEvent] = {
     val query = new BasicDBObject()
-    query.put(ResourceJsonNames.userId, userId)
-    query.put(ResourceJsonNames.occurredMillis, new BasicDBObject("$lt", upTo))
-    query.put(ResourceJsonNames.resource, resName)
+    query.put(ResourceEventNames.userID, userId)
+    query.put(ResourceEventNames.occurredMillis, new BasicDBObject("$lt", upTo))
+    query.put(ResourceEventNames.resource, resName)
 
     instid match {
       case Some(id) =>
         Policy.policy.findResource(resName) match {
-          case Some(y) => query.put(ResourceJsonNames.details,
+          case Some(y) => query.put(ResourceEventNames.details,
             new BasicDBObject(y.descriminatorField, instid.get))
           case None =>
         }
       case None =>
     }
 
-    val sort = new BasicDBObject(ResourceJsonNames.occurredMillis, 1)
+    val sort = new BasicDBObject(ResourceEventNames.occurredMillis, 1)
     val cursor = resourceEvents.find(query).sort(sort)
 
     try {
       val buffer = new scala.collection.mutable.ListBuffer[ResourceEvent]
       while(cursor.hasNext) {
-        buffer += MongoDBStore.dbObjectToResourceEvent(cursor.next())
+        buffer += MongoDBResourceEvent.fromDBObject(cursor.next())
       }
       buffer.toList.sortWith(_sortByTimestampAsc)
     } finally {
@@ -176,14 +174,14 @@ class MongoDBStore(
 
   def findResourceEventsForReceivedPeriod(userId: String, startTimeMillis: Long, stopTimeMillis: Long): List[ResourceEvent] = {
     val query = new BasicDBObject()
-    query.put(ResourceJsonNames.userId, userId)
-    query.put(ResourceJsonNames.receivedMillis, new BasicDBObject("$gte", startTimeMillis))
-    query.put(ResourceJsonNames.receivedMillis, new BasicDBObject("$lte", stopTimeMillis))
+    query.put(ResourceEventNames.userID, userId)
+    query.put(ResourceEventNames.receivedMillis, new BasicDBObject("$gte", startTimeMillis))
+    query.put(ResourceEventNames.receivedMillis, new BasicDBObject("$lte", stopTimeMillis))
 
     // Sort them by increasing order for occurred time
-    val orderBy = new BasicDBObject(ResourceJsonNames.occurredMillis, 1)
+    val orderBy = new BasicDBObject(ResourceEventNames.occurredMillis, 1)
 
-    MongoDBStore.runQuery[ResourceEvent](query, resourceEvents, orderBy)(MongoDBStore.dbObjectToResourceEvent)(None)
+    MongoDBStore.runQuery[ResourceEvent](query, resourceEvents, orderBy)(MongoDBResourceEvent.fromDBObject)(None)
   }
   
   def countOutOfSyncEventsForBillingPeriod(userId: String, startMillis: Long, stopMillis: Long): Maybe[Long] = {
@@ -242,14 +240,15 @@ class MongoDBStore(
       MongoDBStore.storeAny[WalletEntry](
         entry,
         walletEntries,
-        ResourceJsonNames.id,
+        WalletJsonNames.id,
         (e) => e.id,
         MongoDBStore.jsonSupportToDBObject)
     }
   }
 
-  def findWalletEntryById(id: String): Maybe[WalletEntry] =
-    MongoDBStore.findById[WalletEntry](id, walletEntries, MongoDBStore.dbObjectToWalletEntry)
+  def findWalletEntryById(id: String): Maybe[WalletEntry] = {
+    MongoDBStore.findBy(WalletJsonNames.id, id, walletEntries, MongoDBStore.dbObjectToWalletEntry): Maybe[WalletEntry]
+  }
 
   def findUserWalletEntries(userId: String) = {
     // TODO: optimize
@@ -325,10 +324,6 @@ class MongoDBStore(
   //-WalletEntryStore
 
   //+IMEventStore
-  def isLocalIMEvent(event: IMEventModel) = {
-    MongoDBStore.isLocalIMEvent(event)
-  }
-
   def createIMEventFromJson(json: String) = {
     MongoDBStore.createIMEventFromJson(json)
   }
@@ -343,12 +338,8 @@ class MongoDBStore(
     localEvent
   }
 
-  def findIMEventById(id: String): Maybe[IMEvent] =
-    MongoDBStore.findById[IMEvent](id, imEvents, MongoDBStore.dbObjectToIMEvent)
-
-  def findIMEventsByUserId(userId: String): List[IMEvent] = {
-    val query = new BasicDBObject(IMEventNames.userID, userId)
-    MongoDBStore.runQuery(query, imEvents)(MongoDBStore.dbObjectToIMEvent)(Some(_sortByTimestampAsc))
+  def findIMEventById(id: String): Option[IMEvent] = {
+    MongoDBStore.findBy(IMEventNames.id, id, imEvents, MongoDBIMEvent.fromDBObject)
   }
   //-IMEventStore
 
@@ -369,8 +360,9 @@ class MongoDBStore(
     policyEntries.update(query, policyObject, true, false)
   }
   
-  def findPolicyEntry(id: String) =
-    MongoDBStore.findById[PolicyEntry](id, policyEntries, MongoDBStore.dbObjectToPolicyEntry)
+  def findPolicyEntry(id: String) = {
+    MongoDBStore.findBy(PolicyJsonNames.id, id, policyEntries, MongoDBStore.dbObjectToPolicyEntry)
+  }
 
   //-PolicyStore
 }
@@ -427,13 +419,6 @@ object MongoDBStore {
    */
   final val POLICY_ENTRIES_COLLECTION = "policyEntries"
 
-  /* TODO: Some of the following methods rely on JSON (de-)serialization).
-  * A method based on proper object serialization would be much faster.
-  */
-  def dbObjectToResourceEvent(dbObject: DBObject): ResourceEvent = {
-    ResourceEvent.fromJson(JSON.serialize(dbObject))
-  }
-
   def dbObjectToUserState(dbObj: DBObject): UserState = {
     UserState.fromJson(JSON.serialize(dbObj))
   }
@@ -442,30 +427,26 @@ object MongoDBStore {
     WalletEntry.fromJson(JSON.serialize(dbObj))
   }
 
-  def dbObjectToIMEvent(dbObj: DBObject): MongoDBIMEvent = {
-    MongoDBIMEvent.fromJson(JSON.serialize(dbObj))
-  }
-
   def dbObjectToPolicyEntry(dbObj: DBObject): PolicyEntry = {
     PolicyEntry.fromJson(JSON.serialize(dbObj))
   }
 
-  def findById[A >: Null <: AnyRef](id: String, collection: DBCollection, deserializer: (DBObject) => A) : Maybe[A] =
-    Maybe {
-    val query = new BasicDBObject(ResourceJsonNames.id, id)
+  def findBy[A >: Null <: AnyRef](name: String,
+                                  value: String,
+                                  collection: DBCollection,
+                                  deserializer: (DBObject) => A) : Option[A] = {
+    val query = new BasicDBObject(name, value)
     val cursor = collection find query
 
-    try {
+    withCloseable(cursor) { cursor ⇒
       if(cursor.hasNext)
-        deserializer apply cursor.next
+        Some(deserializer apply cursor.next)
       else
-        null: A // will be transformed to NoVal by the Maybe polymorphic constructor
-    } finally {
-      cursor.close()
+        None
     }
   }
 
-  def runQuery[A <: AquariumEventModel](query: DBObject, collection: DBCollection, orderBy: DBObject = null)
+  def runQuery[A <: ExternalEventModel](query: DBObject, collection: DBCollection, orderBy: DBObject = null)
                                   (deserializer: (DBObject) => A)
                                   (sortWith: Option[(A, A) => Boolean]): List[A] = {
     val cursor0 = collection find query
@@ -495,7 +476,7 @@ object MongoDBStore {
   }
 
   def storeUserState(userState: UserState, collection: DBCollection) = {
-    storeAny[UserState](userState, collection, ResourceJsonNames.userId, _.userID, MongoDBStore.jsonSupportToDBObject)
+    storeAny[UserState](userState, collection, ResourceEventNames.userID, _.userID, MongoDBStore.jsonSupportToDBObject)
   }
   
   def storePolicyEntry(policyEntry: PolicyEntry, collection: DBCollection): Maybe[RecordID] = {
@@ -566,11 +547,11 @@ object MongoDBStore {
   }
 
   final def createIMEventFromJson(json: String) = {
-    MongoDBIMEvent.fromJson(json)
+    MongoDBIMEvent.fromJsonString(json)
   }
 
   final def createIMEventFromOther(event: IMEventModel) = {
-    MongoDBIMEvent.fromOther(event)
+    MongoDBIMEvent.fromOther(event, null)
   }
 
   final def createIMEventFromJsonBytes(jsonBytes: Array[Byte]) = {
index 8a492b7..e34c64d 100644 (file)
@@ -40,11 +40,11 @@ import gr.grnet.aquarium.util.{findFromMapAsMaybe, findAndRemoveFromMap, shortCl
 import gr.grnet.aquarium.logic.accounting.Policy
 import java.util.Date
 import com.ckkloverdos.maybe.{NoVal, Maybe, Just}
-import gr.grnet.aquarium.event.ResourceEvent
-import gr.grnet.aquarium.event.ResourceEvent.FullMutableResourceTypeMap
+import gr.grnet.aquarium.event.resource.ResourceEventModel.FullMutableResourceTypeMap
 import logic.accounting.dsl.{Timeslot, DSLAgreement}
 import collection.immutable.{TreeMap, SortedMap}
 import util.date.MutableDateCalc
+import event.resource.ResourceEventModel
 
 /**
  * Snapshot of data that are user-related.
@@ -250,7 +250,7 @@ case class ActiveStateSnapshot(isActive: Boolean, snapshotTime: Long) extends Da
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
-case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEvent],
+case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEventModel],
                                         snapshotTime: Long) extends DataSnapshot {
 
   /**
@@ -259,7 +259,7 @@ case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEvent],
    * @return A fresh instance of [[gr.grnet.aquarium.user.LatestResourceEventsWorker]].
    */
   def toMutableWorker = {
-    val map = scala.collection.mutable.Map[ResourceEvent.FullResourceType, ResourceEvent]()
+    val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
     for(latestEvent <- resourceEvents) {
       map(latestEvent.fullResourceInfo) = latestEvent
     }
@@ -285,21 +285,21 @@ case class LatestResourceEventsWorker(latestEventsMap: FullMutableResourceTypeMa
   def toImmutableSnapshot(snapshotTime: Long) =
     LatestResourceEventsSnapshot(latestEventsMap.valuesIterator.toList, snapshotTime)
 
-  def updateResourceEvent(resourceEvent: ResourceEvent): Unit = {
+  def updateResourceEvent(resourceEvent: ResourceEventModel): Unit = {
     latestEventsMap((resourceEvent.resource, resourceEvent.instanceID)) = resourceEvent
   }
   
-  def findResourceEvent(resource: String, instanceId: String): Maybe[ResourceEvent] = {
+  def findResourceEvent(resource: String, instanceId: String): Maybe[ResourceEventModel] = {
     findFromMapAsMaybe(latestEventsMap, (resource, instanceId))
   }
 
-  def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEvent] = {
+  def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEventModel] = {
     findAndRemoveFromMap(latestEventsMap, (resource, instanceId))
   }
 
   def size = latestEventsMap.size
 
-  def foreach[U](f: ResourceEvent => U): Unit = {
+  def foreach[U](f: ResourceEventModel => U): Unit = {
     latestEventsMap.valuesIterator.foreach(f)
   }
 }
@@ -310,7 +310,7 @@ object LatestResourceEventsWorker {
   /**
    * Helper factory to construct a worker from a list of events.
    */
-  def fromList(latestEventsList: List[ResourceEvent]): LatestResourceEventsWorker = {
+  def fromList(latestEventsList: List[ResourceEventModel]): LatestResourceEventsWorker = {
     LatestResourceEventsSnapshot(latestEventsList, 0L).toMutableWorker
   }
 }
@@ -324,7 +324,7 @@ object LatestResourceEventsWorker {
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
-case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[ResourceEvent],
+case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[ResourceEventModel],
                                                   snapshotTime: Long) extends DataSnapshot {
   /**
    * The gateway to playing with mutable state.
@@ -332,7 +332,7 @@ case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[R
    * @return A fresh instance of [[gr.grnet.aquarium.user.ImplicitlyIssuedResourceEventsWorker]].
    */
   def toMutableWorker = {
-    val map = scala.collection.mutable.Map[ResourceEvent.FullResourceType, ResourceEvent]()
+    val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
     for(implicitEvent <- implicitlyIssuedEvents) {
       map(implicitEvent.fullResourceInfo) = implicitEvent
     }
@@ -350,20 +350,20 @@ case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[R
  */
 case class ImplicitlyIssuedResourceEventsWorker(implicitlyIssuedEventsMap: FullMutableResourceTypeMap) {
 
-  def toList: scala.List[ResourceEvent] = {
+  def toList: scala.List[ResourceEventModel] = {
     implicitlyIssuedEventsMap.valuesIterator.toList
   }
 
   def toImmutableSnapshot(snapshotTime: Long) =
     ImplicitlyIssuedResourceEventsSnapshot(toList, snapshotTime)
 
-  def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEvent] = {
+  def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEventModel] = {
     findAndRemoveFromMap(implicitlyIssuedEventsMap, (resource, instanceId))
   }
 
   def size = implicitlyIssuedEventsMap.size
 
-  def foreach[U](f: ResourceEvent => U): Unit = {
+  def foreach[U](f: ResourceEventModel => U): Unit = {
     implicitlyIssuedEventsMap.valuesIterator.foreach(f)
   }
 }
@@ -379,10 +379,10 @@ object ImplicitlyIssuedResourceEventsWorker {
  * @param ignoredFirstEvents
  * @param snapshotTime
  */
-case class IgnoredFirstResourceEventsSnapshot(ignoredFirstEvents: List[ResourceEvent],
+case class IgnoredFirstResourceEventsSnapshot(ignoredFirstEvents: List[ResourceEventModel],
                                               snapshotTime: Long) extends DataSnapshot {
   def toMutableWorker = {
-    val map = scala.collection.mutable.Map[ResourceEvent.FullResourceType, ResourceEvent]()
+    val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
     for(ignoredFirstEvent <- ignoredFirstEvents) {
       map(ignoredFirstEvent.fullResourceInfo) = ignoredFirstEvent
     }
@@ -400,17 +400,17 @@ case class IgnoredFirstResourceEventsWorker(ignoredFirstEventsMap: FullMutableRe
   def toImmutableSnapshot(snapshotTime: Long) =
     IgnoredFirstResourceEventsSnapshot(ignoredFirstEventsMap.valuesIterator.toList, snapshotTime)
 
-  def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEvent] = {
+  def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEventModel] = {
     findAndRemoveFromMap(ignoredFirstEventsMap, (resource, instanceId))
   }
 
-  def updateResourceEvent(resourceEvent: ResourceEvent): Unit = {
+  def updateResourceEvent(resourceEvent: ResourceEventModel): Unit = {
     ignoredFirstEventsMap((resourceEvent.resource, resourceEvent.instanceID)) = resourceEvent
   }
 
   def size = ignoredFirstEventsMap.size
 
-  def foreach[U](f: ResourceEvent => U): Unit = {
+  def foreach[U](f: ResourceEventModel => U): Unit = {
     ignoredFirstEventsMap.valuesIterator.foreach(f)
   }
 }
index cb505cb..52a6f3a 100644 (file)
@@ -45,7 +45,8 @@ import gr.grnet.aquarium.store.{StoreProvider, PolicyStore}
 import gr.grnet.aquarium.logic.accounting.Accounting
 import gr.grnet.aquarium.logic.accounting.algorithm.CostPolicyAlgorithmCompiler
 import gr.grnet.aquarium.AquariumException
-import gr.grnet.aquarium.event.{NewWalletEntry, ResourceEvent}
+import gr.grnet.aquarium.event.{NewWalletEntry}
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  *
@@ -221,14 +222,14 @@ class UserStateComputations extends Loggable {
   }
 
   //+ Utility methods
-  def rcDebugInfo(rcEvent: ResourceEvent) = {
+  def rcDebugInfo(rcEvent: ResourceEventModel) = {
     rcEvent.toDebugString(false)
   }
   //- Utility methods
 
   def processResourceEvent(startingUserState: UserState,
                            userStateWorker: UserStateWorker,
-                           currentResourceEvent: ResourceEvent,
+                           currentResourceEvent: ResourceEventModel,
                            policyStore: PolicyStore,
                            stateChangeReason: UserStateChangeReason,
                            billingMonthInfo: BillingMonthInfo,
@@ -379,7 +380,7 @@ class UserStateComputations extends Loggable {
     _workingUserState
   }
 
-  def processResourceEvents(resourceEvents: Traversable[ResourceEvent],
+  def processResourceEvents(resourceEvents: Traversable[ResourceEventModel],
                             startingUserState: UserState,
                             userStateWorker: UserStateWorker,
                             policyStore: PolicyStore,
@@ -586,7 +587,7 @@ case class UserStateWorker(userId: String,
    * @param instanceId
    * @return
    */
-  def findAndRemovePreviousResourceEvent(resource: String, instanceId: String): Maybe[ResourceEvent] = {
+  def findAndRemovePreviousResourceEvent(resource: String, instanceId: String): Maybe[ResourceEventModel] = {
     // implicitly issued events are checked first
     implicitlyIssuedStartEvents.findAndRemoveResourceEvent(resource, instanceId) match {
       case just @ Just(_) ⇒
@@ -604,15 +605,15 @@ case class UserStateWorker(userId: String,
     }
   }
 
-  def updateIgnored(resourceEvent: ResourceEvent): Unit = {
+  def updateIgnored(resourceEvent: ResourceEventModel): Unit = {
     ignoredFirstResourceEvents.updateResourceEvent(resourceEvent)
   }
 
-  def updatePrevious(resourceEvent: ResourceEvent): Unit = {
+  def updatePrevious(resourceEvent: ResourceEventModel): Unit = {
     previousResourceEvents.updateResourceEvent(resourceEvent)
   }
 
-  def debugTheMaps(clog: ContextualLogger)(rcDebugInfo: ResourceEvent ⇒ String): Unit = {
+  def debugTheMaps(clog: ContextualLogger)(rcDebugInfo: ResourceEventModel ⇒ String): Unit = {
     if(previousResourceEvents.size > 0) {
       val map = previousResourceEvents.latestEventsMap.map { case (k, v) => (k, rcDebugInfo(v)) }
       clog.debugMap("previousResourceEvents", map, 0)
@@ -645,11 +646,11 @@ case class UserStateWorker(userId: String,
    * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
    */
   def findAndRemoveGeneratorsOfImplicitEndEvents(newOccuredMillis: Long
-                                                ): (List[ResourceEvent], List[ResourceEvent]) = {
-    val buffer = mutable.ListBuffer[(ResourceEvent, ResourceEvent)]()
-    val checkSet = mutable.Set[ResourceEvent]()
+                                                ): (List[ResourceEventModel], List[ResourceEventModel]) = {
+    val buffer = mutable.ListBuffer[(ResourceEventModel, ResourceEventModel)]()
+    val checkSet = mutable.Set[ResourceEventModel]()
 
-    def doItFor(map: ResourceEvent.FullMutableResourceTypeMap): Unit = {
+    def doItFor(map: ResourceEventModel.FullMutableResourceTypeMap): Unit = {
       val resourceEvents = map.valuesIterator
       for {
         resourceEvent <- resourceEvents
index 0d01643..91d3b35 100644 (file)
@@ -37,13 +37,12 @@ package gr.grnet.aquarium.util
 
 import akka.amqp._
 import util.Random
-import gr.grnet.aquarium.event.ResourceEvent
 import scopt.OptionParser
 import gr.grnet.aquarium.messaging.AkkaAMQP
 import java.lang.StringBuffer
 import gr.grnet.aquarium.logic.accounting.Policy
-import gr.grnet.aquarium.store.memory.MemIMEvent
 import gr.grnet.aquarium.event.im.{StdIMEvent, IMEventModel}
+import gr.grnet.aquarium.event.resource.{StdResourceEvent, ResourceEventModel}
 
 /**
  *  Generates random resource events to use as input for testing and
@@ -125,7 +124,7 @@ trait RandomEventGenerator extends AkkaAMQP {
   /**
    * Get the next random resource event
    */
-  def nextResourceEvent() : ResourceEvent = {
+  def nextResourceEvent() : ResourceEventModel = {
     val res = rnd.shuffle(resources).head
 
     val extra = res match {
@@ -141,12 +140,12 @@ trait RandomEventGenerator extends AkkaAMQP {
     val ts = tsFrom + (scala.math.random * ((tsTo - tsFrom) + 1)).asInstanceOf[Long]
     val str = genRndAsciiString(35)
 
-    ResourceEvent(
+    new StdResourceEvent(
       CryptoUtils.sha1(str),
       ts, ts,
       rnd.nextInt(userIds.max).toString,
       rnd.nextInt(clientIds.max).toString,
-      res, "1", 1.toString, value, extra)
+      res, "1", value, 1.toString, extra)
   }
 
   def genRndAsciiString(size: Int): String = {
index eb6932d..0ad26ab 100644 (file)
@@ -41,8 +41,9 @@ import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
 import java.util.Date
 import junit.framework.Assert._
 import gr.grnet.aquarium.logic.accounting.{Accounting}
-import gr.grnet.aquarium.event.{WalletEntry, ResourceEvent}
+import gr.grnet.aquarium.event.{WalletEntry}
 import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.event.resource.StdResourceEvent
 
 /**
  * Tests for the methods that do accounting
@@ -119,7 +120,7 @@ class AccountingTest extends DSLTestBase with Accounting with TestMethods {
     val agr = dsl.findAgreement("scaledbandwidth").get
 
     //Simple, continuous resource
-    var evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bandwidthup", "1", "1", 123, Map())
+    var evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bandwidthup", "1", 123, "1", Map())
     var wallet = chargeEvent(evt, agr, 112, new Date(1325755902000L), List(), None)
     wallet match {
       case Just(x) => assertEquals(2, x.size)
@@ -127,11 +128,11 @@ class AccountingTest extends DSLTestBase with Accounting with TestMethods {
     }
 
     //Complex resource event without details, should fail
-    evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "vmtime", "1", "1", 1, Map())
+    evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "vmtime", "1", 1, "1", Map())
     assertFailed[Exception, List[WalletEntry]](chargeEvent(evt, agr, 1, new Date(1325755902000L), List(), None))
 
     //Complex, onoff resource
-    evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "vmtime", "1", "1", 1, Map("vmid" -> "3"))
+    evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "vmtime", "1", 1, "1", Map("vmid" -> "3"))
     wallet = chargeEvent(evt, agr, 0, new Date(1325755902000L), List(), None)
     wallet match {
       case Just(x) => assertEquals(2, x.size)
@@ -139,11 +140,11 @@ class AccountingTest extends DSLTestBase with Accounting with TestMethods {
     }
 
     //Complex, onoff resource, with wrong states, should fail
-    evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "vmtime", "1", "1", 1, Map("vmid" -> "3"))
+    evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "vmtime", "1", 1, "1", Map("vmid" -> "3"))
     assertFailed[Exception, List[WalletEntry]](chargeEvent(evt, agr, 1, new Date(1325755902000L), List(), None))
 
     //Simple, discrete resource
-    evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bookpages", "1", "1", 120, Map())
+    evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bookpages", "1", 120, "1", Map())
     wallet = chargeEvent(evt, agr, 15, new Date(1325755902000L), List(), None)
     wallet match {
       case Just(x) => assertEquals(1, x.size)
@@ -151,12 +152,12 @@ class AccountingTest extends DSLTestBase with Accounting with TestMethods {
     }
 
     //Simple, discrete resource, time of last update equal to current event's occurred time
-    evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bookpages", "1", "1", 120, Map())
+    evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bookpages", "1", 120, "1", Map())
     wallet = chargeEvent(evt, agr, 15, new Date(1325762772000L), List(), None)
     assertEquals(1, wallet.getOr(List(WalletEntry.zero, WalletEntry.zero)).size)
 
     //Simple, continuous resource, time of last update equal to current event's occurred time
-    evt = ResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bandwidthup", "1", "1", 123, Map())
+    evt = StdResourceEvent("123", 1325762772000L, 1325762774000L, "12", "1", "bandwidthup", "1", 123, "1", Map())
     wallet = chargeEvent(evt, agr, 15, new Date(1325762772000L), List(), None)
     assertEquals(0, wallet.getOr(List(WalletEntry.zero)).size)
   }
index df4d516..5c5366c 100644 (file)
@@ -39,11 +39,11 @@ import org.junit.Assert._
 import org.junit.Assume._
 import gr.grnet.aquarium.Configurator._
 import gr.grnet.aquarium.util.{RandomEventGenerator, TestMethods}
-import gr.grnet.aquarium.event.ResourceEvent
 import collection.mutable.ArrayBuffer
 import org.junit.{After, Test, Before}
 import gr.grnet.aquarium.{StoreConfigurator, LogicTestsAssumptions}
 import gr.grnet.aquarium.store.memory.MemStore
+import gr.grnet.aquarium.event.resource.ResourceEventModel
 
 /**
  * @author Georgios Gousios <gousiosg@gmail.com>
@@ -57,7 +57,7 @@ with RandomEventGenerator with StoreConfigurator {
 
     val event = nextResourceEvent()
     val store = config.resourceEventStore
-    val result = store.storeResourceEvent(event)
+    val result = store.insertResourceEvent(event)
   }
 
   @Test
@@ -67,7 +67,7 @@ with RandomEventGenerator with StoreConfigurator {
     val event = nextResourceEvent()
     val store = config.resourceEventStore
 
-    store.storeResourceEvent(event)
+    store.insertResourceEvent(event)
 
     store.findResourceEventById(event.id)
   }
@@ -75,14 +75,14 @@ with RandomEventGenerator with StoreConfigurator {
   @Test
   def testfindEventsByUserId(): Unit = {
     assumeTrue(LogicTestsAssumptions.EnableStoreTests)
-    val events = new ArrayBuffer[ResourceEvent]()
+    val events = new ArrayBuffer[ResourceEventModel]()
     val store = config.resourceEventStore
 
     (1 to 100).foreach {
       n =>
         val e = nextResourceEvent
         events += e
-        store.storeResourceEvent(e)
+        store.insertResourceEvent(e)
     }
 
     val mostUsedId = events