WIP Resource event handling
authorChristos KK Loverdos <loverdos@gmail.com>
Fri, 8 Jun 2012 12:11:50 +0000 (15:11 +0300)
committerChristos KK Loverdos <loverdos@gmail.com>
Fri, 8 Jun 2012 12:11:50 +0000 (15:11 +0300)
36 files changed:
pom.xml
src/main/resources/aquarium.properties
src/main/scala/gr/grnet/aquarium/Aquarium.scala
src/main/scala/gr/grnet/aquarium/DataSnapshot.scala
src/main/scala/gr/grnet/aquarium/actor/message/GetUserStateResponse.scala
src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala
src/main/scala/gr/grnet/aquarium/computation/BillingMonthInfo.scala
src/main/scala/gr/grnet/aquarium/computation/UserStateComputations.scala
src/main/scala/gr/grnet/aquarium/computation/data/CreditSnapshot.scala [deleted file]
src/main/scala/gr/grnet/aquarium/computation/reason/UserStateChangeReason.scala
src/main/scala/gr/grnet/aquarium/computation/state/UserState.scala [moved from src/main/scala/gr/grnet/aquarium/computation/UserState.scala with 79% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/UserStateBootstrap.scala [moved from src/main/scala/gr/grnet/aquarium/computation/UserStateBootstrappingData.scala with 85% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/UserStateWorker.scala [moved from src/main/scala/gr/grnet/aquarium/computation/UserStateWorker.scala with 90% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistory.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistory.scala with 97% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistoryItem.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistoryItem.scala with 97% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/IMStateSnapshot.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/IMStateSnapshot.scala with 87% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsSnapshot.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsSnapshot.scala with 96% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsWorker.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsWorker.scala with 95% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsSnapshot.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsSnapshot.scala with 94% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsWorker.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsWorker.scala with 95% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsSnapshot.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsSnapshot.scala with 95% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsWorker.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsWorker.scala with 95% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesMap.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesMap.scala with 91% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesSnapshot.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesSnapshot.scala with 95% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/ResourceInstanceSnapshot.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/ResourceInstanceSnapshot.scala with 88% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistory.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/RoleHistory.scala with 95% similarity]
src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistoryItem.scala [moved from src/main/scala/gr/grnet/aquarium/computation/data/RoleHistoryItem.scala with 96% similarity]
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/model/PolicyEntry.scala
src/main/scala/gr/grnet/aquarium/store/RecordID.scala
src/main/scala/gr/grnet/aquarium/store/UserStateStore.scala
src/main/scala/gr/grnet/aquarium/store/memory/MemStore.scala
src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBStore.scala
src/main/scala/gr/grnet/aquarium/util/ContextualLogger.scala
src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala

diff --git a/pom.xml b/pom.xml
index 9daddfa..d02b3d7 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       <groupId>org.scala-lang</groupId>
       <artifactId>scala-compiler</artifactId>
       <version>${scala.version}</version>
-      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>junit</groupId>
     <resources>
       <resource>
         <directory>${basedir}/src/main/resources</directory>
+        <excludes>
+          <exclude>aquarium.properties</exclude>
+        </excludes>
       </resource>
       <resource>
         <directory>${project.build.directory}/generated-resources</directory>
index aad478b..eab7427 100644 (file)
@@ -64,13 +64,14 @@ mongodb.dbschema=aquarium
 mongodb.connection.pool.size=20
 
 # Relative to AQUARIUM_HOME or an absolute path
-events.store.folder=../events-store
+# DO NOT set this in production
+#events.store.folder=../events-store
 
 # Store resource events to events.store.folder as well
-events.store.save.rc.events=true
+events.store.save.rc.events=false
 
 # Store IM events to events.store.folder as well
-events.store.save.im.events=true
+events.store.save.im.events=false
 
 # How often do we attempt a reconnection to the store(s)?
 anystore.reconnect.period.millis=1000
index 8654577..1c6b64b 100644 (file)
@@ -52,6 +52,7 @@ import gr.grnet.aquarium.computation.UserStateComputations
 import gr.grnet.aquarium.logic.accounting.algorithm.{SimpleCostPolicyAlgorithmCompiler, CostPolicyAlgorithmCompiler}
 import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap
 import gr.grnet.aquarium.logic.accounting.Policy
+import org.slf4j.{Logger, LoggerFactory}
 
 /**
  * This is the Aquarium entry point.
@@ -67,16 +68,27 @@ final class Aquarium(val props: Props) extends Lifecycle with Loggable { aquariu
 
   def isStopping() = _isStopping.get()
 
+  @inline
+  def getClientLogger(client: AnyRef): Logger = {
+    client match {
+      case null ⇒
+        this.logger
+
+      case _ ⇒
+        LoggerFactory.getLogger(client.getClass)
+    }
+  }
+
   def debug(client: AnyRef, fmt: String, args: Any*) = {
-    logger.debug("[%s] %s".format(shortClassNameOf(client), fmt.format(args: _*)))
+    getClientLogger(client).debug(fmt.format(args: _*))
   }
 
   def info(client: AnyRef, fmt: String, args: Any*) = {
-    logger.info("[%s] %s".format(shortClassNameOf(client), fmt.format(args: _*)))
+    getClientLogger(client).info(fmt.format(args: _*))
   }
 
   def warn(client: AnyRef, fmt: String, args: Any*) = {
-    logger.warn("[%s] %s".format(shortClassNameOf(client), fmt.format(args: _*)))
+    getClientLogger(client).warn(fmt.format(args: _*))
   }
 
   /**
@@ -569,9 +581,9 @@ object Aquarium {
     final val aquarium_role_agreement_map = "aquarium.role-agreement.map"
     
     /**
-     * A time period in milliseconds for which we can tolerate stale data regarding user state.
+     * A time period in milliseconds for which we can tolerate stale parts regarding user state.
      *
-     * The smaller the value, the more accurate the user credits and other state data are.
+     * The smaller the value, the more accurate the user credits and other state parts are.
      *
      * If a request for user state (e.g. balance) is received and the request timestamp exceeds
      * the timestamp of the last known balance amount by this value, then a re-computation for
index a41f19c..53e69a8 100644 (file)
 package gr.grnet.aquarium
 
 /**
- * Timely snapshots of data.
+ * Timely snapshots of parts.
  *
- * We are interested only on the time aspect here, the data part is left open to the implementors.
+ * We are interested only on the time aspect here, the parts part is left open to the implementors.
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
 trait DataSnapshot {
   /**
-   * The unix time (millis) when the data was actually calculated/obtained.
+   * The unix time (millis) when the parts was actually calculated/obtained.
    */
   def snapshotTime: Long
 }
\ No newline at end of file
index 1d4904c..b506e49 100644 (file)
@@ -35,7 +35,7 @@
 
 package gr.grnet.aquarium.actor.message
 
-import gr.grnet.aquarium.computation.UserState
+import gr.grnet.aquarium.computation.state.UserState
 
 /**
  *
index 7c16fb8..95d04fe 100644 (file)
@@ -41,15 +41,17 @@ import gr.grnet.aquarium.actor._
 
 import akka.config.Supervision.Temporary
 import gr.grnet.aquarium.actor.message.event.{ProcessResourceEvent, ProcessIMEvent}
-import gr.grnet.aquarium.computation.data.IMStateSnapshot
 import gr.grnet.aquarium.actor.message.config.{InitializeUserState, ActorProviderConfigured, AquariumPropertiesLoaded}
-import gr.grnet.aquarium.computation.{BillingMonthInfo, UserStateBootstrappingData, UserState}
 import gr.grnet.aquarium.util.date.TimeHelpers
 import gr.grnet.aquarium.event.model.im.IMEventModel
 import gr.grnet.aquarium.actor.message.{GetUserStateResponse, GetUserBalanceResponseData, GetUserBalanceResponse, GetUserStateRequest, GetUserBalanceRequest}
 import gr.grnet.aquarium.util.{LogHelpers, shortClassNameOf, shortNameOfClass, shortNameOfType}
-import gr.grnet.aquarium.computation.reason.{RealtimeBillingCalculation, NoSpecificChangeReason, InitialUserActorSetup, UserStateChangeReason, IMEventArrival, InitialUserStateSetup}
-import gr.grnet.aquarium.{AquariumInternalError, AquariumException, Aquarium}
+import gr.grnet.aquarium.computation.reason.{RealtimeBillingCalculation, InitialUserActorSetup, UserStateChangeReason, IMEventArrival, InitialUserStateSetup}
+import gr.grnet.aquarium.{AquariumInternalError, Aquarium}
+import gr.grnet.aquarium.computation.state.parts.IMStateSnapshot
+import gr.grnet.aquarium.computation.BillingMonthInfo
+import gr.grnet.aquarium.computation.state.{UserStateBootstrap, UserState}
+import gr.grnet.aquarium.event.model.resource.ResourceEventModel
 
 /**
  *
@@ -82,8 +84,13 @@ class UserActor extends ReflectiveRoleableActor {
   def role = UserActorRole
 
   private[this] def aquarium: Aquarium = Aquarium.Instance
+
   private[this] def userStateComputations = aquarium.userStateComputations
 
+  private[this] def stdUserStateStoreFunc = (userState: UserState) ⇒ {
+    aquarium.userStateStore.insertUserState(userState)
+  }
+
   private[this] def _timestampTheshold = {
     aquarium.props.getLong(Aquarium.Keys.user_state_timestamp_threshold).getOr(1000L * 60 * 5 /* 5 minutes */)
   }
@@ -157,7 +164,7 @@ class UserActor extends ReflectiveRoleableActor {
     val userCreationMillis = this._imState.userCreationMillis.get
     val initialRole = this._imState.roleHistory.firstRole.get.name
 
-    val userStateBootstrap = UserStateBootstrappingData(
+    val userStateBootstrap = UserStateBootstrap(
       this._userID,
       userCreationMillis,
       initialRole,
@@ -165,13 +172,14 @@ class UserActor extends ReflectiveRoleableActor {
       aquarium.initialBalanceForRole(initialRole, userCreationMillis)
     )
 
-    val userState = userStateComputations.doBillingForMonth(
+    val now = TimeHelpers.nowMillis()
+    val userState = userStateComputations.doMonthBillingUpTo(
+      BillingMonthInfo.fromMillis(now),
+      now,
       userStateBootstrap,
-      BillingMonthInfo.fromMillis(TimeHelpers.nowMillis()),
-      false,
-      TimeHelpers.nowMillis(),
       aquarium.currentResourcesMap,
-      InitialUserStateSetup(None),
+      InitialUserActorSetup(),
+      stdUserStateStoreFunc,
       None
     )
 
@@ -221,12 +229,8 @@ class UserActor extends ReflectiveRoleableActor {
    * call this.
    */
   private[this] def newUserStateWithUpdatedRoleHistory(stateChangeReason: UserStateChangeReason): UserState = {
-    this._userState.copy(
-      roleHistory = this._imState.roleHistory,
-      // FIXME: Also update agreement
-      stateChangeCounter = this._userState.stateChangeCounter + 1,
-      lastChangeReason = stateChangeReason
-    )
+    // FIXME: Also update agreement
+    this._userState.newWithRoleHistory(this._imState.roleHistory, stateChangeReason)
   }
 
   /**
@@ -317,26 +321,27 @@ class UserActor extends ReflectiveRoleableActor {
     val initialRole = this._imState.roleHistory.firstRoleName.getOrElse(aquarium.defaultInitialUserRole)
     val initialAgreement = aquarium.initialAgreementForRole(initialRole, userCreationMillis)
     val initialCredits   = aquarium.initialBalanceForRole(initialRole, userCreationMillis)
-    val userStateBootstrap = UserStateBootstrappingData(
+    val userStateBootstrap = UserStateBootstrap(
       userID,
       userCreationMillis,
       initialRole,
       initialAgreement,
       initialCredits
     )
-    val billingMonthInfoNow =BillingMonthInfo.fromMillis(now)
+    val billingMonthInfo = BillingMonthInfo.fromMillis(now)
     val currentResourcesMap = aquarium.currentResourcesMap
     val calculationReason = RealtimeBillingCalculation(None, now)
+    val eventOccurredMillis = rcEvent.occurredMillis
 
     DEBUG("Using %s", currentResourcesMap)
 
-    this._userState = aquarium.userStateComputations.doBillingForMonth(
+    this._userState = aquarium.userStateComputations.doMonthBillingUpTo(
+      billingMonthInfo,
+      now max eventOccurredMillis, // take into account that the event may be out-of-sync
       userStateBootstrap,
-      billingMonthInfoNow,
-      false,
-      now,
       currentResourcesMap,
-      calculationReason
+      calculationReason,
+      stdUserStateStoreFunc
     )
 
     this._latestResourceEventOccurredMillis = event.rcEvent.occurredMillis
index 69ca62e..57d6e96 100644 (file)
@@ -36,7 +36,7 @@
 package gr.grnet.aquarium.computation
 
 import gr.grnet.aquarium.util.shortClassNameOf
-import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
+import gr.grnet.aquarium.util.date.MutableDateCalc
 
 /**
  * Provides information about the billing month and related calculation utilities.
@@ -91,6 +91,8 @@ final class BillingMonthInfo(
 
   override def toString = "%s(%s-%02d-01)".format(shortClassNameOf(this), year, month)
 
+  def toDebugString = toString
+
   def toShortDebugString = "%s-%02d-01".format(year, month)
 }
 
index bd3262d..ca613d1 100644 (file)
@@ -39,11 +39,12 @@ import scala.collection.mutable
 import gr.grnet.aquarium.util.{ContextualLogger, Loggable}
 import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
 import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap
-import gr.grnet.aquarium.computation.data._
+import gr.grnet.aquarium.computation.state.parts._
 import gr.grnet.aquarium.event.model.NewWalletEntry
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
 import gr.grnet.aquarium.{Aquarium, AquariumInternalError}
 import gr.grnet.aquarium.computation.reason.{MonthlyBillingCalculation, InitialUserStateSetup, UserStateChangeReason}
+import gr.grnet.aquarium.computation.state.{UserStateWorker, UserStateBootstrap, UserState}
 
 /**
  *
@@ -52,19 +53,22 @@ import gr.grnet.aquarium.computation.reason.{MonthlyBillingCalculation, InitialU
 final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
 
   lazy val aquarium = _aquarium
-  lazy val storeProvider        = aquarium.storeProvider
-  lazy val timeslotComputations = new TimeslotComputations {}
-  lazy val algorithmCompiler    = aquarium.algorithmCompiler
-  lazy val policyStore          = storeProvider.policyStore
-  lazy val userStateStore       = storeProvider.userStateStore
-  lazy val resourceEventStore   = storeProvider.resourceEventStore
-
-  def findUserStateAtEndOfBillingMonth(userStateBootstrap: UserStateBootstrappingData,
-                                       billingMonthInfo: BillingMonthInfo,
-                                       billingTimeMillis: Long,
-                                       defaultResourcesMap: DSLResourcesMap,
-                                       calculationReason: UserStateChangeReason,
-                                       clogOpt: Option[ContextualLogger] = None): UserState = {
+
+  lazy val storeProvider         = aquarium.storeProvider
+  lazy val timeslotComputations  = new TimeslotComputations {}
+  lazy val algorithmCompiler     = aquarium.algorithmCompiler
+  lazy val policyStore           = storeProvider.policyStore
+  lazy val userStateStoreForRead = storeProvider.userStateStore
+  lazy val resourceEventStore    = storeProvider.resourceEventStore
+
+  def findUserStateAtEndOfBillingMonth(
+      userStateBootstrap: UserStateBootstrap,
+      billingMonthInfo: BillingMonthInfo,
+      defaultResourcesMap: DSLResourcesMap,
+      calculationReason: UserStateChangeReason,
+      storeFunc: UserState ⇒ UserState,
+      clogOpt: Option[ContextualLogger] = None
+  ): UserState = {
 
     val clog = ContextualLogger.fromOther(
       clogOpt,
@@ -72,14 +76,24 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
       "findUserStateAtEndOfBillingMonth(%s)", billingMonthInfo.toShortDebugString)
     clog.begin()
 
-    def computeFullMonthlyBilling(): UserState = {
-      doFullMonthlyBilling(
+    def computeFullMonthBillingAndSaveState(): UserState = {
+      val userState0 = doFullMonthBilling(
         userStateBootstrap,
         billingMonthInfo,
-        billingTimeMillis,
         defaultResourcesMap,
-        MonthlyBillingCalculation(calculationReason, billingMonthInfo),
-        Some(clog))
+        calculationReason,
+        storeFunc,
+        Some(clog)
+      )
+
+      // We always save the state when it is a full month billing
+      val userState1 = storeFunc.apply(
+        userState0.newWithChangeReason(
+          MonthlyBillingCalculation(calculationReason, billingMonthInfo))
+      )
+
+      clog.debug("Stored full %s %s", billingMonthInfo.toDebugString, userState1.toJsonString)
+      userState1
     }
 
     val userID = userStateBootstrap.userID
@@ -92,30 +106,33 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
       // If the user did not exist for this billing month, piece of cake
       clog.debug("User did not exist before %s", userCreationDateCalc)
 
+      // TODO: The initial user state might have already been created.
+      //       First ask if it exists and compute only if not
       val initialUserState0 = UserState.createInitialUserStateFromBootstrap(
         userStateBootstrap,
         TimeHelpers.nowMillis(),
         InitialUserStateSetup(Some(calculationReason)) // we record the originating calculation reason
       )
-      val initialUserState1 = userStateStore.insertUserState(initialUserState0)
 
-      clog.debug("Initial state %s".format(initialUserState1))
+      // We always save the initial state
+      val initialUserState1 = storeFunc.apply(initialUserState0)
+
+      clog.debug("Stored initial state = %s", initialUserState1.toJsonString)
       clog.end()
 
       return initialUserState1
     }
 
     // Ask DB cache for the latest known user state for this billing period
-    val latestUserStateOpt = userStateStore.findLatestUserStateForEndOfBillingMonth(
+    val latestUserStateOpt = userStateStoreForRead.findLatestUserStateForFullMonthBilling(
       userID,
-      billingMonthInfo.year,
-      billingMonthInfo.month)
+      billingMonthInfo)
 
     latestUserStateOpt match {
       case None ⇒
         // Not found, must compute
         clog.debug("No user state found from cache, will have to (re)compute")
-        val result = computeFullMonthlyBilling
+        val result = computeFullMonthBillingAndSaveState
         clog.end()
         result
 
@@ -139,7 +156,7 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
           case n if n > 0 ⇒
             clog.debug(
               "Found %s out of sync events (%s more), will have to (re)compute user state", actualOOSEventsCounter, n)
-            val result = computeFullMonthlyBilling
+            val result = computeFullMonthBillingAndSaveState
             clog.end()
             result
 
@@ -158,13 +175,15 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
   }
   //- Utility methods
 
-  def processResourceEvent(startingUserState: UserState,
-                           userStateWorker: UserStateWorker,
-                           currentResourceEvent: ResourceEventModel,
-                           stateChangeReason: UserStateChangeReason,
-                           billingMonthInfo: BillingMonthInfo,
-                           walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
-                           clogOpt: Option[ContextualLogger] = None): UserState = {
+  def processResourceEvent(
+      startingUserState: UserState,
+      userStateWorker: UserStateWorker,
+      currentResourceEvent: ResourceEventModel,
+      stateChangeReason: UserStateChangeReason,
+      billingMonthInfo: BillingMonthInfo,
+      walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
+      clogOpt: Option[ContextualLogger] = None
+  ): UserState = {
 
     val clog = ContextualLogger.fromOther(clogOpt, logger, "processResourceEvent(%s)", currentResourceEvent.id)
 
@@ -297,13 +316,15 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
     _workingUserState
   }
 
-  def processResourceEvents(resourceEvents: Traversable[ResourceEventModel],
-                            startingUserState: UserState,
-                            userStateWorker: UserStateWorker,
-                            stateChangeReason: UserStateChangeReason,
-                            billingMonthInfo: BillingMonthInfo,
-                            walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
-                            clogOpt: Option[ContextualLogger] = None): UserState = {
+  def processResourceEvents(
+      resourceEvents: Traversable[ResourceEventModel],
+      startingUserState: UserState,
+      userStateWorker: UserStateWorker,
+      stateChangeReason: UserStateChangeReason,
+      billingMonthInfo: BillingMonthInfo,
+      walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
+      clogOpt: Option[ContextualLogger] = None
+  ): UserState = {
 
     var _workingUserState = startingUserState
 
@@ -323,55 +344,66 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
     _workingUserState
   }
 
-  def doFullMonthlyBilling(userStateBootstrap: UserStateBootstrappingData,
-                           billingMonthInfo: BillingMonthInfo,
-                           billingTimeMillis: Long,
-                           defaultResourcesMap: DSLResourcesMap,
-                           calculationReason: UserStateChangeReason,
-                           clogOpt: Option[ContextualLogger] = None): UserState = {
-    doBillingForMonth(
-      userStateBootstrap,
+  def doFullMonthBilling(
+      userStateBootstrap: UserStateBootstrap,
+      billingMonthInfo: BillingMonthInfo,
+      defaultResourcesMap: DSLResourcesMap,
+      calculationReason: UserStateChangeReason,
+      storeFunc: UserState ⇒ UserState,
+      clogOpt: Option[ContextualLogger] = None
+  ): UserState = {
+
+    doMonthBillingUpTo(
       billingMonthInfo,
-      true,
-      billingTimeMillis,
+      billingMonthInfo.monthStopMillis,
+      userStateBootstrap,
       defaultResourcesMap,
       calculationReason,
+      storeFunc,
       clogOpt
     )
   }
 
-  def doBillingForMonth(userStateBootstrap: UserStateBootstrappingData,
-                        billingMonthInfo: BillingMonthInfo,
-                        fullBillingMonthState: Boolean, // See UserState#isFullBillingMonthState
-                        billingTimeMillis: Long,           // See UserState$occurredMillis
-                        defaultResourcesMap: DSLResourcesMap,
-                        calculationReason: UserStateChangeReason,
-                        clogOpt: Option[ContextualLogger] = None): UserState = {
-
+  def doMonthBillingUpTo(
+      /**
+       * Which month to bill.
+       */
+      billingMonthInfo: BillingMonthInfo,
+      /**
+       * Bill from start of month up to (and including) this time.
+       */
+      billingEndTimeMillis: Long,
+      userStateBootstrap: UserStateBootstrap,
+      defaultResourcesMap: DSLResourcesMap,
+      calculationReason: UserStateChangeReason,
+      storeFunc: UserState ⇒ UserState,
+      clogOpt: Option[ContextualLogger] = None
+  ): UserState = {
+
+    val isFullMonthBilling = billingEndTimeMillis == billingMonthInfo.monthStopMillis
     val userID = userStateBootstrap.userID
 
     val clog = ContextualLogger.fromOther(
       clogOpt,
       logger,
-      "doFullMonthlyBilling(%s)", billingMonthInfo.toShortDebugString)
+      "doMonthBillingUpTo(%s)", new MutableDateCalc(billingEndTimeMillis).toYYYYMMDDHHMMSSSSS)
     clog.begin()
 
+    clog.debug("calculationReason = %s", calculationReason)
+
     val clogSome = Some(clog)
 
     val previousBillingMonthUserState = findUserStateAtEndOfBillingMonth(
       userStateBootstrap,
       billingMonthInfo.previousMonth,
-      billingTimeMillis,
       defaultResourcesMap,
-      calculationReason.forBillingMonthInfo(billingMonthInfo.previousMonth),
+      calculationReason,
+      storeFunc,
       clogSome
     )
 
     val startingUserState = previousBillingMonthUserState
 
-    val billingMonthStartMillis = billingMonthInfo.monthStartMillis
-    val billingMonthEndMillis = billingMonthInfo.monthStopMillis
-
     // Keep the working (current) user state. This will get updated as we proceed with billing for the month
     // specified in the parameters.
     // NOTE: The calculation reason is not the one we get from the previous user state but the one our caller specifies
@@ -384,8 +416,9 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
     // First, find and process the actual resource events from DB
     val allResourceEventsForMonth = resourceEventStore.findAllRelevantResourceEventsForBillingPeriod(
       userID,
-      billingMonthStartMillis,
-      billingMonthEndMillis)
+      billingMonthInfo.monthStartMillis, // from start of month
+      billingEndTimeMillis               // to requested time
+    )
 
     val newWalletEntries = scala.collection.mutable.ListBuffer[NewWalletEntry]()
 
@@ -399,43 +432,46 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
       clogSome
     )
 
-    // Second, for the remaining events which must contribute an implicit OFF, we collect those OFFs
-    // ... in order to generate an implicit ON later
-    val (specialEvents, theirImplicitEnds) = userStateWorker.
-      findAndRemoveGeneratorsOfImplicitEndEvents(billingMonthEndMillis)
-    if(specialEvents.lengthCompare(1) >= 0 || theirImplicitEnds.lengthCompare(1) >= 0) {
-      clog.debug("")
-      clog.debug("Process implicitly issued events")
-      clog.debugSeq("specialEvents", specialEvents, 0)
-      clog.debugSeq("theirImplicitEnds", theirImplicitEnds, 0)
-    }
-
-    // Now, the previous and implicitly started must be our base for the following computation, so we create an
-    // appropriate worker
-    val specialUserStateWorker = UserStateWorker(
-      userStateWorker.userID,
-      LatestResourceEventsWorker.fromList(specialEvents),
-      ImplicitlyIssuedResourceEventsWorker.Empty,
-      IgnoredFirstResourceEventsWorker.Empty,
-      userStateWorker.resourcesMap
-    )
+    if(isFullMonthBilling) {
+      // Second, for the remaining events which must contribute an implicit OFF, we collect those OFFs
+      // ... in order to generate an implicit ON later (during the next billing cycle).
+      val (specialEvents, theirImplicitEnds) = userStateWorker.
+        findAndRemoveGeneratorsOfImplicitEndEvents(billingMonthInfo.monthStopMillis)
+
+      if(specialEvents.lengthCompare(1) >= 0 || theirImplicitEnds.lengthCompare(1) >= 0) {
+        clog.debug("")
+        clog.debug("Process implicitly issued events")
+        clog.debugSeq("specialEvents", specialEvents, 0)
+        clog.debugSeq("theirImplicitEnds", theirImplicitEnds, 0)
+      }
+
+      // Now, the previous and implicitly started must be our base for the following computation, so we create an
+      // appropriate worker
+      val specialUserStateWorker = UserStateWorker(
+        userStateWorker.userID,
+        LatestResourceEventsWorker.fromList(specialEvents),
+        ImplicitlyIssuedResourceEventsWorker.Empty,
+        IgnoredFirstResourceEventsWorker.Empty,
+        userStateWorker.resourcesMap
+      )
 
-    _workingUserState = processResourceEvents(
-      theirImplicitEnds,
-      _workingUserState,
-      specialUserStateWorker,
-      calculationReason,
-      billingMonthInfo,
-      newWalletEntries,
-      clogSome
-    )
+      _workingUserState = processResourceEvents(
+        theirImplicitEnds,
+        _workingUserState,
+        specialUserStateWorker,
+        calculationReason,
+        billingMonthInfo,
+        newWalletEntries,
+        clogSome
+      )
+    }
 
     val lastUpdateTime = TimeHelpers.nowMillis()
 
     _workingUserState = _workingUserState.copy(
-      isFullBillingMonthState = fullBillingMonthState,
+      isFullBillingMonthState = isFullMonthBilling,
 
-      theFullBillingMonth = if(fullBillingMonthState)
+      theFullBillingMonth = if(isFullMonthBilling)
         Some(billingMonthInfo)
       else
         _workingUserState.theFullBillingMonth,
@@ -451,15 +487,6 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable {
       newWalletEntries = newWalletEntries.toList
     )
 
-    clog.debug("calculationReason = %s", calculationReason)
-
-    if(calculationReason.shouldStoreUserState) {
-      val storedUserState = userStateStore.insertUserState(_workingUserState)
-      clog.debug("Saved [_id=%s] %s", storedUserState._id, storedUserState)
-      _workingUserState = storedUserState
-    }
-
-    clog.debug("RETURN %s", _workingUserState)
     clog.end()
     _workingUserState
   }
diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/CreditSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/data/CreditSnapshot.scala
deleted file mode 100644 (file)
index 7ec81d9..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.computation.data
-
-
-/**
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-
-case class CreditSnapshot(creditAmount: Double)
index cc156da..0f9d072 100644 (file)
@@ -40,23 +40,24 @@ import gr.grnet.aquarium.event.model.im.IMEventModel
 import gr.grnet.aquarium.util.shortClassNameOf
 
 /**
- * Provides information explaining the reason Aquarium calculated a new [[gr.grnet.aquarium.computation.UserState]].
+ * Provides information explaining the reason Aquarium calculated a new
+ * [[gr.grnet.aquarium.computation.state.UserState]].
  */
 case class UserStateChangeReason(
-    parentReason: Option[UserStateChangeReason],
+    details: Map[String, String],
     billingMonthInfo: Option[BillingMonthInfo],
-    details: Map[String, Any]
+    parentReason: Option[UserStateChangeReason]
 ) {
 
   require(
-    details.contains(UserStateChangeReason.Names.`type`),
-    "No type present in the details of %s".format(shortClassNameOf(this))
+    details.contains(UserStateChangeReason.Names.name),
+    "No name present in the details of %s".format(shortClassNameOf(this))
   )
 
   private[this] def booleanFromDetails(name: String, default: Boolean) = {
     details.get(name) match {
-      case Some(value: Boolean) ⇒
-        value
+      case Some(value) ⇒
+        value.toBoolean
 
       case _ ⇒
         false
@@ -68,8 +69,8 @@ case class UserStateChangeReason(
     * [[gr.grnet.aquarium.store.UserStateStore]].
     *
     */
-  def shouldStoreUserState: Boolean =
-     booleanFromDetails(UserStateChangeReason.Names.shouldStoreUserState, false)
+//  def shouldStoreUserState: Boolean =
+//     booleanFromDetails(UserStateChangeReason.Names.shouldStoreUserState, false)
 
   def shouldStoreCalculatedWalletEntries: Boolean =
     booleanFromDetails(UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries, false)
@@ -84,15 +85,15 @@ case class UserStateChangeReason(
     )
   }
 
-  def `type`: String = {
+  def name: String = {
     // This must be always present
-    details(UserStateChangeReason.Names.`type`).asInstanceOf[String]
+    details.get(UserStateChangeReason.Names.name).getOrElse("<unknown>")
   }
 }
 
 object UserStateChangeReason {
   object Names {
-    final val `type` = "type"
+    final val name = "name"
 
     final val imEvent = "imEvent"
     final val forWhenMillis = "forWhenMillis"
@@ -121,109 +122,112 @@ sealed trait UserStateChangeReason_ {
   def code: UserStateChangeReasonCodes.ChangeReasonCode
 }
 
+/**
+ * Used when the very first user state is saved.
+ */
 object InitialUserStateSetup {
-  def `type` = "InitialUserStateSetup"
+  def name = "InitialUserStateSetup"
 
   /**
    * When the user state is initially set up.
    */
   def apply(parentReason: Option[UserStateChangeReason]) = {
     UserStateChangeReason(
-      parentReason,
-      None,
       Map(
-        UserStateChangeReason.Names.`type` -> `type`,
-        UserStateChangeReason.Names.shouldStoreUserState -> true
-      )
+        UserStateChangeReason.Names.name -> name//,
+//        UserStateChangeReason.Names.shouldStoreUserState -> true
+      ),
+      None,
+      parentReason
     )
   }
 }
 
 object InitialUserActorSetup {
-  def `type` = "InitialUserActorSetup"
+  def name = "InitialUserActorSetup"
 
   /**
    * When the user processing unit (actor) is initially set up.
    */
   def apply() = {
     UserStateChangeReason(
-      None,
-      None,
       Map(
-        UserStateChangeReason.Names.`type` -> `type`,
-        UserStateChangeReason.Names.shouldStoreUserState -> true
-      )
+        UserStateChangeReason.Names.name -> name//,
+//        UserStateChangeReason.Names.shouldStoreUserState -> true
+      ),
+      None,
+      None
     )
   }
 }
 
 object NoSpecificChangeReason {
-  def `type` = "NoSpecificChangeReason"
+  def name = "NoSpecificChangeReason"
 
   /**
    * A calculation made for no specific reason. Can be for testing, for example.
    */
   def apply() = {
     UserStateChangeReason(
-      None,
-      None,
       Map(
-        UserStateChangeReason.Names.`type` -> `type`
-      )
+        UserStateChangeReason.Names.name -> name
+      ),
+      None,
+      None
     )
   }
 }
 
 object MonthlyBillingCalculation {
-  def `type` = "MonthlyBillingCalculation"
+  def name = "MonthlyBillingCalculation"
 
   /**
    * An authoritative calculation for the billing period.
    */
   def apply(parentReason: UserStateChangeReason, billingMongthInfo: BillingMonthInfo) = {
     UserStateChangeReason(
-      Some(parentReason),
-      Some(billingMongthInfo),
       Map(
-        UserStateChangeReason.Names.`type` -> `type`,
-        UserStateChangeReason.Names.shouldStoreUserState -> true,
-        UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries -> true,
-        UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated -> true
-      )
+        UserStateChangeReason.Names.name -> name,
+//        UserStateChangeReason.Names.shouldStoreUserState -> true,
+        UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries -> true.toString,
+        UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated -> true.toString
+      ),
+      Some(billingMongthInfo),
+      Some(parentReason)
     )
   }
 }
 
 object RealtimeBillingCalculation {
-  def `type` = "RealtimeBillingCalculation"
+  def name = "RealtimeBillingCalculation"
 
   /**
    * Used for the real-time billing calculation.
    */
   def apply(parentReason: Option[UserStateChangeReason], forWhenMillis: Long) = {
     UserStateChangeReason(
-      parentReason,
-      None,
       Map(
-        UserStateChangeReason.Names.`type` -> `type`,
-        UserStateChangeReason.Names.forWhenMillis -> forWhenMillis
-      )
+        UserStateChangeReason.Names.name -> name,
+        UserStateChangeReason.Names.forWhenMillis -> forWhenMillis.toString
+      ),
+      None,
+      parentReason
     )
   }
 }
 
 object IMEventArrival {
-  def `type` = "IMEventArrival"
+  def name = "IMEventArrival"
 
   def apply(imEvent: IMEventModel) = {
     UserStateChangeReason(
-      None,
-      None,
       Map(
-        UserStateChangeReason.Names.`type` -> `type`,
-        UserStateChangeReason.Names.imEvent -> imEvent,
-        UserStateChangeReason.Names.shouldStoreUserState -> true
-      )
+        UserStateChangeReason.Names.name -> name,
+        UserStateChangeReason.Names.imEvent -> imEvent.toJsonString//,
+//        UserStateChangeReason.Names.shouldStoreUserState -> true
+      ),
+      None,
+      None
     )
   }
 }
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation
+package gr.grnet.aquarium.computation.state
 
 import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
 import gr.grnet.aquarium.event.model.NewWalletEntry
 import gr.grnet.aquarium.util.json.JsonSupport
 import gr.grnet.aquarium.logic.accounting.dsl.DSLAgreement
 import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, UserStateChangeReason, InitialUserStateSetup}
-import gr.grnet.aquarium.computation.data.{OwnedResourcesMap, RoleHistory, ResourceInstanceSnapshot, OwnedResourcesSnapshot, AgreementHistory, LatestResourceEventsSnapshot, ImplicitlyIssuedResourceEventsSnapshot}
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
+import gr.grnet.aquarium.computation.BillingMonthInfo
+import gr.grnet.aquarium.computation.parts.RoleHistory
+import gr.grnet.aquarium.computation.state.parts.{OwnedResourcesMap, ResourceInstanceSnapshot, OwnedResourcesSnapshot, AgreementHistory, ImplicitlyIssuedResourceEventsSnapshot, LatestResourceEventsSnapshot}
 
 /**
  * A comprehensive representation of the User's state.
  *
- * Note that it is made of autonomous parts that are actually data snapshots.
+ * Note that it is made of autonomous parts that are actually parts snapshots.
  *
  * The different snapshots need not agree on the snapshot time, ie. some state
  * part may be stale, while other may be fresh.
@@ -54,15 +56,15 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel
  * The user state is meant to be partially updated according to relevant events landing on Aquarium.
  *
  * @define communicatedByIM
- *          This is communicated to Aquarium from the `IM` system.
+ *         This is communicated to Aquarium from the `IM` system.
  *
  *
  * @param userID
- *          The user ID. $communicatedByIM
+ * The user ID. $communicatedByIM
  * @param userCreationMillis
- *          When the user was created.
- *          $communicatedByIM
- *          Set to zero if unknown.
+ * When the user was created.
+ * $communicatedByIM
+ * Set to zero if unknown.
  * @param stateChangeCounter
  * @param isFullBillingMonthState
  * @param theFullBillingMonth
@@ -72,15 +74,15 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel
  * @param agreementHistory
  * @param ownedResourcesSnapshot
  * @param newWalletEntries
- *          The wallet entries computed. Not all user states need to holds wallet entries,
- *          only those that refer to billing periods (end of billing period).
-  * @param lastChangeReason
- *          The [[gr.grnet.aquarium.computation.reason.UserStateChangeReason]] for which the usr state has changed.
+ * The wallet entries computed. Not all user states need to holds wallet entries,
+ * only those that refer to billing periods (end of billing period).
+ * @param lastChangeReason
+ * The [[gr.grnet.aquarium.computation.reason.UserStateChangeReason]] for which the usr state has changed.
  * @param parentUserStateIDInStore
- *          The `ID` of the parent state. The parent state is the one used as a reference point in order to calculate
- *          this user state.
+ * The `ID` of the parent state. The parent state is the one used as a reference point in order to calculate
+ * this user state.
  * @param _id
- *          The unique `ID` given by the store.
+ * The unique `ID` given by the store.
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
@@ -153,12 +155,12 @@ case class UserState(
 
   def idInStore: Option[String] = _id match {
     case null ⇒ None
-    case _id  ⇒ Some(_id.toString)
+    case _id ⇒ Some(_id.toString)
   }
 
-//  def userCreationDate = new Date(userCreationMillis)
-//
-//  def userCreationFormatedDate = new MutableDateCalc(userCreationMillis).toString
+  //  def userCreationDate = new Date(userCreationMillis)
+  //
+  //  def userCreationFormatedDate = new MutableDateCalc(userCreationMillis).toString
 
   def findDSLAgreementForTime(at: Long): Option[DSLAgreement] = {
     agreementHistory.findForTime(at)
@@ -172,7 +174,7 @@ case class UserState(
     ownedResourcesSnapshot.getResourceInstanceAmount(resource, instanceId, defaultValue)
   }
 
-  def newWithResourcesSnapshotUpdate(resource: String,   // resource name
+  def newWithResourcesSnapshotUpdate(resource: String, // resource name
                                      instanceId: String, // resource instance id
                                      newAmount: Double,
                                      snapshotTime: Long): UserState = {
@@ -183,7 +185,8 @@ case class UserState(
     this.copy(
       isInitial = false,
       ownedResourcesSnapshot = newResources,
-      stateChangeCounter = this.stateChangeCounter + 1)
+      stateChangeCounter = this.stateChangeCounter + 1
+    )
   }
 
   def newWithChangeReason(changeReason: UserStateChangeReason) = {
@@ -194,6 +197,16 @@ case class UserState(
     )
   }
 
+  def newWithRoleHistory(newRoleHistory: RoleHistory, changeReason: UserStateChangeReason) = {
+    // FIXME: Also update agreement
+    this.copy(
+      isInitial = false,
+      stateChangeCounter = this.stateChangeCounter + 1,
+      roleHistory = newRoleHistory,
+      lastChangeReason = changeReason
+    )
+  }
+
   def resourcesMap: OwnedResourcesMap = {
     ownedResourcesSnapshot.toResourcesMap
   }
@@ -206,12 +219,12 @@ case class UserState(
     latestResourceEventsSnapshot.findTheLatestID
   }
 
-//  def toShortString = "UserState(%s, %s, %s, %s, %s)".format(
-//    userId,
-//    _id,
-//    parentUserStateId,
-//    totalEventsProcessedCounter,
-//    calculationReason)
+  //  def toShortString = "UserState(%s, %s, %s, %s, %s)".format(
+  //    userId,
+  //    _id,
+  //    parentUserStateId,
+  //    totalEventsProcessedCounter,
+  //    calculationReason)
 }
 
 object UserState {
@@ -220,17 +233,18 @@ object UserState {
   }
 
   object JsonNames {
-    final val _id    = "_id"
+    final val _id = "_id"
     final val userID = "userID"
     final val isFullBillingMonthState = "isFullBillingMonthState"
     final val occurredMillis = "occurredMillis"
+    final val theFullBillingMonth_year  = "theFullBillingMonth.year"  // FQN
+    final val theFullBillingMonth_month = "theFullBillingMonth.month" // FQN
 
     object theFullBillingMonth {
-      final val year  = "year"
+      final val year = "year"
       final val month = "month"
-      final val monthStartMillis = "monthStartMillis"
-      final val monthStopMillis  = "monthStopMillis"
     }
+
   }
 
   def createInitialUserState(
@@ -264,7 +278,7 @@ object UserState {
   }
 
   def createInitialUserStateFromBootstrap(
-      usb: UserStateBootstrappingData,
+      usb: UserStateBootstrap,
       occurredMillis: Long,
       calculationReason: UserStateChangeReason
   ): UserState = {
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation
+package gr.grnet.aquarium.computation.state
 
 /**
- * This is used to bootstrap the user state.
+ * This is used to bootstrap the [[gr.grnet.aquarium.computation.state.UserState]].
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
 
-case class UserStateBootstrappingData(
-    userID: String,
+case class UserStateBootstrap(
+    userID:             String,
     userCreationMillis: Long,
-    initialRole: String,
-    initialAgreement: String,
-    initialCredits: Double
+    initialRole:        String,
+    initialAgreement:   String,
+    initialCredits:     Double
 )
  */
 
 package gr.grnet.aquarium.computation
+package state
 
 import scala.collection.mutable
 import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap
-import gr.grnet.aquarium.computation.data.{LatestResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, IgnoredFirstResourceEventsWorker}
 import gr.grnet.aquarium.util.ContextualLogger
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
+import gr.grnet.aquarium.computation.state.parts.{IgnoredFirstResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, LatestResourceEventsWorker}
 
 /**
  * A helper object holding intermediate state/results during resource event processing.
@@ -57,11 +58,13 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
-case class UserStateWorker(userID: String,
-                           previousResourceEvents: LatestResourceEventsWorker,
-                           implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker,
-                           ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker,
-                           resourcesMap: DSLResourcesMap) {
+case class UserStateWorker(
+                            userID: String,
+                            previousResourceEvents: LatestResourceEventsWorker,
+                            implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker,
+                            ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker,
+                            resourcesMap: DSLResourcesMap
+                            ) {
 
   /**
    * Finds the previous resource event by checking two possible sources: a) The implicitly terminated resource
@@ -138,6 +141,10 @@ case class UserStateWorker(userID: String,
    * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
    */
   def findAndRemoveGeneratorsOfImplicitEndEvents(
+      /**
+       * The `occurredMillis` that will be recorded in the synthetic implicit OFFs.
+       * Normally, this will be the end of a billing month.
+       */
       newOccuredMillis: Long
   ): (List[ResourceEventModel], List[ResourceEventModel]) = {
 
@@ -33,7 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import java.util.Date
 
@@ -42,7 +44,7 @@ import gr.grnet.aquarium.logic.accounting.Policy
 import scala.collection.immutable.{SortedMap, TreeMap}
 
 /**
- * User agreement data that will be part of UserState.
+ * User agreement parts that will be part of UserState.
  * The provided list of agreements cannot have time gaps. This is checked at object creation type.
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.util.date.MutableDateCalc
-import java.util.Date
 import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
 
 /**
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.event.model.im.IMEventModel
 import gr.grnet.aquarium.util.shortClassNameOf
 import gr.grnet.aquarium.util.date.MutableDateCalc
+import gr.grnet.aquarium.computation.parts.RoleHistory
 
 /**
  *
@@ -45,25 +48,26 @@ import gr.grnet.aquarium.util.date.MutableDateCalc
  */
 
 case class IMStateSnapshot(
-                           /**
-                            * This is the latest processed IMEvent
-                            */
-                           latestIMEvent: IMEventModel,
-
-                           /**
-                            * The earliest activation time, if it exists.
-                            */
-                           userEarliestActivationMillis: Option[Long],
-
-                           /**
-                            * The user creation time, if it exists
-                            */
-                           userCreationMillis: Option[Long],
-
-                           /**
-                            * This is the recorded role history
-                            */
-                           roleHistory: RoleHistory) {
+    /**
+     * This is the latest processed IMEvent
+     */
+    latestIMEvent: IMEventModel,
+
+    /**
+     * The earliest activation time, if it exists.
+     */
+    userEarliestActivationMillis: Option[Long],
+
+    /**
+     * The user creation time, if it exists
+     */
+    userCreationMillis: Option[Long],
+
+    /**
+     * This is the recorded role history
+     */
+    roleHistory: RoleHistory
+) {
 
   /**
    * True iff the user has ever been activated even once.
@@ -33,7 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.util.findAndRemoveFromMap
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
@@ -57,7 +59,7 @@ case class IgnoredFirstResourceEventsWorker(ignoredFirstEventsMap: FullMutableRe
 
   def size = ignoredFirstEventsMap.size
 
-  def foreach[U](f: ResourceEventModel => U): Unit = {
+  def foreach[U](f: ResourceEventModel ⇒ U): Unit = {
     ignoredFirstEventsMap.valuesIterator.foreach(f)
   }
 }
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
 
 /**
  * Keeps the implicit OFF events when a billing period ends.
- * This is normally recorded in the [[gr.grnet.aquarium.computation.UserState]].
+ * This is normally recorded in the [[gr.grnet.aquarium.computation.state.UserState]].
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
@@ -47,7 +49,7 @@ case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[R
   /**
    * The gateway to playing with mutable state.
    *
-   * @return A fresh instance of [[gr.grnet.aquarium.computation.data.ImplicitlyIssuedResourceEventsWorker]].
+   * @return A fresh instance of [[gr.grnet.aquarium.computation.state.parts.ImplicitlyIssuedResourceEventsWorker]].
    */
   def toMutableWorker = {
     val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
@@ -33,7 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.util.findAndRemoveFromMap
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
@@ -41,9 +43,7 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel.FullMutableReso
 
 
 /**
- * This is the mutable cousin of [[gr.grnet.aquarium.computation.data.ImplicitlyIssuedResourceEventsSnapshot]].
- *
- * @param implicitlyIssuedEventsMap
+ * This is the mutable cousin of [[gr.grnet.aquarium.computation.state.parts.ImplicitlyIssuedResourceEventsSnapshot]].
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
@@ -33,7 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
 
@@ -49,7 +51,7 @@ case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEventModel]
   /**
    * The gateway to playing with mutable state.
    *
-   * @return A fresh instance of [[gr.grnet.aquarium.computation.data.LatestResourceEventsWorker]].
+   * @return A fresh instance of [[gr.grnet.aquarium.computation.state.parts.LatestResourceEventsWorker]].
    */
   def toMutableWorker = {
     val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 import gr.grnet.aquarium.util.findAndRemoveFromMap
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
 import gr.grnet.aquarium.event.model.resource.ResourceEventModel.FullMutableResourceTypeMap
 
 /**
- * This is the mutable cousin of [[gr.grnet.aquarium.computation.data.LatestResourceEventsSnapshot]].
+ * This is the mutable cousin of [[gr.grnet.aquarium.computation.state.parts.LatestResourceEventsSnapshot]].
  *
  * @param latestEventsMap
  *
@@ -52,7 +54,7 @@ case class LatestResourceEventsWorker(latestEventsMap: FullMutableResourceTypeMa
    * The gateway to immutable state.
    *
    * @param snapshotTime The relevant snapshot time.
-   * @return A fresh instance of [[gr.grnet.aquarium.computation.data.LatestResourceEventsSnapshot]].
+   * @return A fresh instance of [[gr.grnet.aquarium.computation.state.parts.LatestResourceEventsSnapshot]].
    */
   def toImmutableSnapshot(snapshotTime: Long) =
     LatestResourceEventsSnapshot(latestEventsMap.valuesIterator.toList)
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 /**
  * A map from (resourceName, resourceInstanceId) to value.
  *
  * This representation is convenient for computations and updating, while the
- * [[gr.grnet.aquarium.computation.data.OwnedResourcesSnapshot]] representation is convenient for JSON serialization.
+ * [[gr.grnet.aquarium.computation.state.parts.OwnedResourcesSnapshot]] representation is convenient for JSON
+ * serialization.
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
@@ -33,8 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
-
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 /**
  *
@@ -50,8 +51,8 @@ case class OwnedResourcesSnapshot(resourceInstanceSnapshots: List[ResourceInstan
   }
 
   def resourceInstanceSnapshotsExcept(resource: String, instanceId: String) = {
-    // Unfortunately, we have to use a List for data, since JSON serialization is not as flexible
-    // (at least out of the box). Thus, the update is O(L), where L is the length of the data List.
+    // Unfortunately, we have to use a List for parts, since JSON serialization is not as flexible
+    // (at least out of the box). Thus, the update is O(L), where L is the length of the parts List.
     resourceInstanceSnapshots.filterNot(_.isSameResourceInstance(resource, instanceId))
   }
 
@@ -33,7 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation
+package state
+package parts
 
 /**
  * Maintains the current state of a resource instance owned by the user.
@@ -41,7 +43,7 @@ package gr.grnet.aquarium.computation.data
  *
  * name: DSLResource.name
  * instanceId: instance-id (in the resource's descriminatorField)
- * data: current-resource-value
+ * parts: current-resource-value
  * snapshotTime: last-update-timestamp
  *
  * In order to have a uniform representation of the resource state for all
@@ -50,11 +52,13 @@ package gr.grnet.aquarium.computation.data
  *  - If the resource is complex, the (name, instanceId) is (DSLResource.name, instance-id)
  *  - If the resource is simple,  the (name, instanceId) is (DSLResource.name, "1")
  *
- * @param resource        Same as `resource` of [[gr.grnet.aquarium.event.resource.ResourceEventModel]]
- * @param instanceId      Same as `instanceId` of [[gr.grnet.aquarium.event.resource.ResourceEventModel]]
+ * @param resource        Same as `resource` of [[gr.grnet.aquarium.event.model.resource.ResourceEventModel]]
+ * @param instanceId      Same as `instanceId` of [[gr.grnet.aquarium.event.model.resource.ResourceEventModel]]
  * @param instanceAmount  This is the amount kept for the resource instance.
-*                         The general rule is that an amount saved in a [[gr.grnet.aquarium.computation.data. ResourceInstanceSnapshot]]
- *                        represents a total value, while a value appearing in a [[gr.grnet.aquarium.event.resource.ResourceEventModel]]
+*                         The general rule is that an amount saved in a
+ *                        [[gr.grnet.aquarium.computation.parts. ResourceInstanceSnapshot]]
+ *                        represents a total value, while a value appearing in a
+ *                        [[gr.grnet.aquarium.event .model.resource.ResourceEventModel]]
  *                        represents a difference. How these two values are combined to form the new amount is dictated
  *                        by the underlying [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
  *
@@ -33,7 +33,7 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation.parts
 
 import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
 import scala.collection.immutable.{TreeMap, SortedMap}
@@ -46,10 +46,11 @@ import scala.annotation.tailrec
  */
 
 case class RoleHistory(
-                       /**
-                        * The head role is the most recent. The same rule applies for the tail.
-                        */
-                       roles: List[RoleHistoryItem]) {
+    /**
+     * The head role is the most recent. The same rule applies for the tail.
+     */
+    roles: List[RoleHistoryItem]
+) {
 
   def roleNamesByTimeslot: SortedMap[Timeslot, String] = {
     TreeMap(roles.map(role ⇒ (role.timeslot, role.name)): _*)
@@ -33,7 +33,7 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.computation.data
+package gr.grnet.aquarium.computation.parts
 
 import gr.grnet.aquarium.util.date.MutableDateCalc
 import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
@@ -58,7 +58,8 @@ case class RoleHistoryItem(
     /**
      * Validity stop time for this role. The time is exclusive.
      */
-    validTo: Long = Long.MaxValue) {
+    validTo: Long = Long.MaxValue
+) {
 
   try {
     require(
@@ -67,6 +68,7 @@ case class RoleHistoryItem(
   }
   catch {
     case e: IllegalArgumentException ⇒
+      // TODO Remove this
       Aquarium.Instance.debug(this, "!! validFrom = %s, validTo = %s, dx=%s", validFrom, validTo, validTo-validFrom)
       throw e
   }
index dd98ec0..71e3479 100644 (file)
@@ -55,6 +55,8 @@ object JsonConversions {
    * The application-wide JSON formats used from the underlying lift-json library.
    */
   implicit final val Formats = (DefaultFormats ++ JodaTimeSerializers.all)
+//  implicit final val Formats = (DefaultFormats.withHints(FullTypeHints(List(classOf[AnyRef]))) ++ JodaTimeSerializers.all)
+//  Serialization.formats(FullTypeHints(List(classOf[AnyRef])))
 
   /**
    * Converts a value to JSON AST (Abstract Syntax Tree) by acting a bit intelligently, depending on the actual type
index f0cbaa3..4febdcc 100644 (file)
@@ -45,7 +45,7 @@ import net.liftweb.json.Xml
 
 
 /**
- * Every data type conversion happening inside Aquarium is defined here.
+ * Every parts type conversion happening inside Aquarium is defined here.
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
index 3a62163..8ea7401 100644 (file)
@@ -40,7 +40,7 @@ import java.util.Date
 import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
 
 /**
- * Store entry for serialized policy data.
+ * Store entry for serialized policy parts.
  *
  * @author Georgios Gousios <gousiosg@gmail.com>
  */
index 44b092b..91102de 100644 (file)
@@ -36,7 +36,7 @@
 package gr.grnet.aquarium.store
 
 /**
- * Represents an ID given by a store to a data object (document).
+ * Represents an ID given by a store to a parts object (document).
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  */
index 2ffd1e2..00832ef 100644 (file)
 
 package gr.grnet.aquarium.store
 
-import gr.grnet.aquarium.computation.UserState
+import gr.grnet.aquarium.computation.state.UserState
+import gr.grnet.aquarium.computation.BillingMonthInfo
 
 /**
  * A store for user state snapshots.
  *
- * This is used to hold snapshots of [[gr.grnet.aquarium.computation.UserState]]
+ * This is used to hold snapshots of [[gr.grnet.aquarium.computation.state.UserState]].
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>
  */
@@ -48,22 +49,17 @@ import gr.grnet.aquarium.computation.UserState
 trait UserStateStore {
 
   /**
-   * Store a user state.
+   * Stores a user state.
    */
   def insertUserState(userState: UserState): UserState
 
   /**
-   * Find a state by user ID
+   * Finds a state by user ID
    */
   def findUserStateByUserID(userID: String): Option[UserState]
 
   /**
-   * Find the most up-to-date user state for the particular billing period.
+   * Finds the most up-to-date user state for the particular billing period.
    */
-  def findLatestUserStateForEndOfBillingMonth(userID: String, yearOfBillingMonth: Int, billingMonth: Int): Option[UserState]
-
-  /**
-   * Delete a state for a user
-   */
-  def deleteUserState(userId: String): Unit
+  def findLatestUserStateForFullMonthBilling(userID: String, bmi: BillingMonthInfo): Option[UserState]
 }
\ No newline at end of file
index 5db4519..f61ade6 100644 (file)
 package gr.grnet.aquarium.store.memory
 
 import com.ckkloverdos.props.Props
-import com.ckkloverdos.maybe.{NoVal, Just}
+import com.ckkloverdos.maybe.Just
 import gr.grnet.aquarium.store._
 import scala.collection.JavaConversions._
 import collection.mutable.ConcurrentMap
 import java.util.concurrent.ConcurrentHashMap
-import gr.grnet.aquarium.uid.ConcurrentVMLocalUIDGenerator
 import gr.grnet.aquarium.Configurable
 import gr.grnet.aquarium.event.model.PolicyEntry
 import gr.grnet.aquarium.event.model.im.{StdIMEvent, IMEventModel}
 import org.bson.types.ObjectId
 import gr.grnet.aquarium.event.model.resource.{StdResourceEvent, ResourceEventModel}
-import gr.grnet.aquarium.computation.UserState
+import gr.grnet.aquarium.computation.state.UserState
 import gr.grnet.aquarium.util.Tags
+import gr.grnet.aquarium.computation.BillingMonthInfo
 
 /**
- * An implementation of various stores that persists data in memory.
+ * An implementation of various stores that persists parts in memory.
  *
  * This is just for testing purposes.
  * 
@@ -111,16 +111,12 @@ class MemStore extends UserStateStore
     _userStates.find(_.userID == userID)
   }
 
-  def findLatestUserStateForEndOfBillingMonth(userID: String,
-                                              yearOfBillingMonth: Int,
-                                              billingMonth: Int): Option[UserState] = {
+  def findLatestUserStateForFullMonthBilling(userID: String, bmi: BillingMonthInfo): Option[UserState] = {
     val goodOnes = _userStates.filter(_.theFullBillingMonth.isDefined).filter { userState ⇒
         val f1 = userState.userID == userID
         val f2 = userState.isFullBillingMonthState
         val bm = userState.theFullBillingMonth.get
-        val f3 = (bm ne null) && {
-          bm.year == yearOfBillingMonth && bm.month == billingMonth
-        }
+        val f3 = bm == bmi
 
         f1 && f2 && f3
     }
@@ -135,10 +131,6 @@ class MemStore extends UserStateStore
         None
     }
   }
-
-  def deleteUserState(userId: String) {
-    _userStates.filterNot(_.userID == userId)
-  }
   //- UserStateStore
 
   //+ ResourceEventStore
index 617471f..04ecf59 100644 (file)
@@ -36,7 +36,7 @@
 package gr.grnet.aquarium.store.mongodb
 
 import com.mongodb.util.JSON
-import gr.grnet.aquarium.computation.UserState.{JsonNames ⇒ UserStateJsonNames}
+import gr.grnet.aquarium.computation.state.UserState.{JsonNames ⇒ UserStateJsonNames}
 import gr.grnet.aquarium.util.json.JsonSupport
 import collection.mutable.ListBuffer
 import gr.grnet.aquarium.event.model.im.IMEventModel
@@ -51,8 +51,10 @@ import org.bson.types.ObjectId
 import com.ckkloverdos.maybe.Maybe
 import gr.grnet.aquarium.util._
 import gr.grnet.aquarium.converter.Conversions
-import gr.grnet.aquarium.computation.UserState
+import gr.grnet.aquarium.computation.state.UserState
 import gr.grnet.aquarium.event.model.{ExternalEventModel, PolicyEntry}
+import gr.grnet.aquarium.computation.BillingMonthInfo
+import gr.grnet.aquarium.Aquarium
 
 /**
  * Mongodb implementation of the various aquarium stores.
@@ -226,16 +228,16 @@ class MongoDBStore(
     MongoDBStore.firstResultIfExists(cursor, MongoDBStore.dbObjectToUserState)
   }
 
-  def findLatestUserStateForEndOfBillingMonth(userID: String,
-                                              yearOfBillingMonth: Int,
-                                              billingMonth: Int): Option[UserState] = {
+  def findLatestUserStateForFullMonthBilling(userID: String, bmi: BillingMonthInfo): Option[UserState] = {
     val query = new BasicDBObjectBuilder().
       add(UserState.JsonNames.userID, userID).
       add(UserState.JsonNames.isFullBillingMonthState, true).
-      add(UserState.JsonNames.theFullBillingMonth.year, yearOfBillingMonth).
-      add(UserState.JsonNames.theFullBillingMonth.month, billingMonth).
+      add(UserState.JsonNames.theFullBillingMonth_year, bmi.year).
+      add(UserState.JsonNames.theFullBillingMonth_month, bmi.month).
       get()
 
+    logger.debug("findLatestUserStateForFullMonthBilling(%s, %s) query: %s".format(userID, bmi, query))
+
     // Descending order, so that the latest comes first
     val sorter = new BasicDBObject(UserState.JsonNames.occurredMillis, -1)
 
@@ -243,11 +245,6 @@ class MongoDBStore(
 
     MongoDBStore.firstResultIfExists(cursor, MongoDBStore.dbObjectToUserState)
   }
-
-  def deleteUserState(userId: String) = {
-    val query = new BasicDBObject(UserStateJsonNames.userID, userId)
-    userStates.findAndRemove(query)
-  }
   //- UserStateStore
 
   //+IMEventStore
@@ -373,9 +370,10 @@ object MongoDBStore {
   final val RESOURCE_EVENTS_COLLECTION = "resevents"
 
   /**
-   * Collection holding the snapshots of [[gr.grnet.aquarium.computation.UserState]].
+   * Collection holding the snapshots of [[gr.grnet.aquarium.computation.state.UserState]].
    *
-   * [[gr.grnet.aquarium.computation.UserState]] is held internally within [[gr.grnet.aquarium.actor.service.user.UserActor]]s.
+   * [[gr.grnet.aquarium.computation.state.UserState]] is held internally within
+   * [[gr.grnet.aquarium.actor.service.user .UserActor]]s.
    */
   final val USER_STATES_COLLECTION = "userstates"
 
index 3d71eae..71cef07 100644 (file)
@@ -52,36 +52,36 @@ DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          t
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          testOrphanOFF()       EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo)
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          testOrphanOFF()   --- [Events by OccurredMillis] ---
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          testOrphanOFF()
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2012-01)   BEGIN
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2012-01)   BEGIN
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-12)     BEGIN
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-12)       No user state found from cache, will have to (re)compute
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-12)       BEGIN
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-12)       BEGIN
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-11)         BEGIN
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-11)           No user state found from cache, will have to (re)compute
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-11)           BEGIN
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-11)           BEGIN
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10)             BEGIN
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10)               User did not exist before 2011-11-01 00:00:00.000
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10)               Returning INITIAL state [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,0,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,InitialUserStateSetup,0,None,4fa7e12ba0eee3db73fbe8d0)
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10)             END
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-11)             calculationReason = MonthlyBillingCalculation(2011-11)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-11)             Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-11)             RETURN UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-11)           END
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-11)             calculationReason = MonthlyBillingCalculation(2011-11)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-11)             Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-11)             RETURN UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-11)           END
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-11)         END
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-12)         calculationReason = MonthlyBillingCalculation(2011-12)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-12)         Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-12)         RETURN UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2011-12)       END
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-12)         calculationReason = MonthlyBillingCalculation(2011-12)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-12)         Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-12)         RETURN UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2011-12)       END
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-12)     END
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -         walletEntriesForResourceEvent(0)     +++ [EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo)] +++
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -         walletEntriesForResourceEvent(0)       Cost policy OnOffCostPolicy for DSLResource(vmtime,Hr,OnOffCostPolicy,true,instanceId)
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -         walletEntriesForResourceEvent(0)       PreviousM None
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -         walletEntriesForResourceEvent(0)       Ignoring first event of its kind EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo)
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -         walletEntriesForResourceEvent(0)     --- [EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo)] ---
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2012-01)     calculationReason = MonthlyBillingCalculation(2012-01)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2012-01)     Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2012-01)     RETURN UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
-DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthlyBilling(2012-01)   END
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2012-01)     calculationReason = MonthlyBillingCalculation(2012-01)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2012-01)     Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2012-01)     RETURN UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -            doFullMonthBilling(2012-01)   END
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          testOrphanOFF()   _id = 4fa7e12ba0eee3db73fbe8d0
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          testOrphanOFF()   parentId = Some(4fa7e12ba0eee3db73fbe8d0)
 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest -                          testOrphanOFF()   credits = 0.0
index 3417a6e..7d9c28c 100644 (file)
@@ -46,8 +46,9 @@ import gr.grnet.aquarium.logic.accounting.algorithm.{ExecutableCostPolicyAlgorit
 import gr.grnet.aquarium.AquariumException
 import gr.grnet.aquarium.Aquarium.{Instance ⇒ AquariumInstance}
 import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, MonthlyBillingCalculation}
-import gr.grnet.aquarium.computation.{TimeslotComputations, UserStateBootstrappingData, UserState, BillingMonthInfo}
-import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
+import gr.grnet.aquarium.util.date.MutableDateCalc
+import gr.grnet.aquarium.computation.BillingMonthInfo
+import gr.grnet.aquarium.computation.state.{UserStateBootstrap, UserState}
 
 
 /**
@@ -250,7 +251,7 @@ aquariumpolicy:
 //    initialAgreement = DSLAgreement.DefaultAgreementName
 //  )
 
-  val UserStateBootstrap = UserStateBootstrappingData(
+  val UserStateBootstrapper = UserStateBootstrap(
     userID = UserCKKL.userID,
     userCreationMillis = UserCreationDate.getTime(),
     initialRole = "default",
@@ -301,27 +302,30 @@ aquariumpolicy:
   def doFullMonthlyBilling(
       clog: ContextualLogger,
       billingMonthInfo: BillingMonthInfo,
-      isFullBillingMonthState: Boolean,
       billingTimeMillis: Long) = {
 
-    Computations.doBillingForMonth(
-      UserStateBootstrap,
+    Computations.doMonthBillingUpTo(
       billingMonthInfo,
-      isFullBillingMonthState,
       billingTimeMillis,
+      UserStateBootstrapper,
       DefaultResourcesMap,
       MonthlyBillingCalculation(NoSpecificChangeReason(), billingMonthInfo),
+      aquarium.userStateStore.insertUserState,
       Some(clog)
     )
   }
 
   private[this]
-  def expectCredits(clog: ContextualLogger,
-                    creditsConsumed: Double,
-                    userState: UserState,
-                    accuracy: Double = 0.001): Unit = {
+  def expectCredits(
+      clog: ContextualLogger,
+      creditsConsumed: Double,
+      userState: UserState,
+      accuracy: Double = 0.001
+  ): Unit = {
+
     val computed = userState.totalCredits
     Assert.assertEquals(-creditsConsumed, computed, accuracy)
+
     clog.info("Consumed %.3f credits [accuracy = %f]", creditsConsumed, accuracy)
   }
 
@@ -353,7 +357,7 @@ aquariumpolicy:
 
     showResourceEvents(clog)
 
-    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis())
+    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis)
 
     showUserState(clog, userState)
 
@@ -382,7 +386,7 @@ aquariumpolicy:
 
     showResourceEvents(clog)
 
-    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis())
+    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis)
 
     showUserState(clog, userState)
 
@@ -412,7 +416,7 @@ aquariumpolicy:
 
     showResourceEvents(clog)
 
-    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis())
+    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis)
 
     showUserState(clog, userState)
 
@@ -463,7 +467,7 @@ aquariumpolicy:
 
     clog.debugMap("DefaultResourcesMap", DefaultResourcesMap.map, 1)
 
-    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis())
+    val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis)
 
     showUserState(clog, userState)