package gr.grnet.aquarium.actor.message
-import gr.grnet.aquarium.user.UserState
+import gr.grnet.aquarium.computation.UserState
/**
*
package user
import gr.grnet.aquarium.actor._
-import gr.grnet.aquarium.user._
import gr.grnet.aquarium.util.shortClassNameOf
import message.config.{ActorProviderConfigured, AquariumPropertiesLoaded}
import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc}
import gr.grnet.aquarium.actor.message.event.{ProcessResourceEvent, ProcessIMEvent}
import gr.grnet.aquarium.actor.message.{GetUserStateResponse, GetUserBalanceResponse, GetUserStateRequest, GetUserBalanceRequest}
+import gr.grnet.aquarium.computation.data.IMStateSnapshot
+import gr.grnet.aquarium.computation.UserState
/**
*
}
}
- this._imState = IMStateSnapshot(imEvent, now)
+ this._imState = IMStateSnapshot(imEvent)
DEBUG("%s %s", if(hadIMState) "Update" else "Set", shortClassNameOf(this._imState))
}
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.util.date.MutableDateCalc
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+final class BillingMonthInfo private(val year: Int,
+ val month: Int,
+ val startMillis: Long,
+ val stopMillis: Long) extends Ordered[BillingMonthInfo] {
+
+ def previousMonth: BillingMonthInfo = {
+ BillingMonthInfo.fromDateCalc(new MutableDateCalc(year, month).goPreviousMonth)
+ }
+
+ def nextMonth: BillingMonthInfo = {
+ BillingMonthInfo.fromDateCalc(new MutableDateCalc(year, month).goNextMonth)
+ }
+
+
+ def compare(that: BillingMonthInfo) = {
+ val ds = this.startMillis - that.startMillis
+ if(ds < 0) -1 else if(ds == 0) 0 else 1
+ }
+
+
+ override def equals(any: Any) = any match {
+ case that: BillingMonthInfo ⇒
+ this.year == that.year && this.month == that.month // normally everything else MUST be the same by construction
+ case _ ⇒
+ false
+ }
+
+ override def hashCode() = {
+ 31 * year + month
+ }
+
+ override def toString = "%s-%02d".format(year, month)
+}
+
+object BillingMonthInfo {
+ def fromMillis(millis: Long): BillingMonthInfo = {
+ fromDateCalc(new MutableDateCalc(millis))
+ }
+
+ def fromDateCalc(mdc: MutableDateCalc): BillingMonthInfo = {
+ val year = mdc.getYear
+ val month = mdc.getMonthOfYear
+ val startMillis = mdc.goStartOfThisMonth.getMillis
+ val stopMillis = mdc.goEndOfThisMonth.getMillis // no need to `copy` here, since we are discarding `mdc`
+
+ new BillingMonthInfo(year, month, startMillis, stopMillis)
+ }
+}
* or implied, of GRNET S.A.
*/
-package gr.grnet.aquarium.user
+package gr.grnet.aquarium.computation
/**
- * Default implementation for [[gr.grnet.aquarium.user.UserStateComputations]].
+ * Default implementation for [[gr.grnet.aquarium.computation.UserStateComputations]].
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
* or implied, of GRNET S.A.
*/
-package gr.grnet.aquarium.user
+package gr.grnet.aquarium.computation
-import gr.grnet.aquarium.util.json.JsonSupport
-import gr.grnet.aquarium.logic.accounting.dsl.DSLAgreement
-import com.ckkloverdos.maybe.{Failed, Maybe}
-import gr.grnet.aquarium.util.date.MutableDateCalc
-import gr.grnet.aquarium.event.{NewWalletEntry, WalletEntry}
import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
-import gr.grnet.aquarium.AquariumException
-import gr.grnet.aquarium.event.im.IMEventModel
+import gr.grnet.aquarium.event.{NewWalletEntry, WalletEntry}
import org.bson.types.ObjectId
-
+import gr.grnet.aquarium.util.json.JsonSupport
+import gr.grnet.aquarium.logic.accounting.dsl.DSLAgreement
+import com.ckkloverdos.maybe.Maybe
+import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, UserStateChangeReason, InitialUserStateSetup, IMEventArrival}
+import gr.grnet.aquarium.AquariumInternalError
+import gr.grnet.aquarium.computation.data.{AgreementSnapshot, ResourceInstanceSnapshot, OwnedResourcesSnapshot, AgreementsSnapshot, CreditSnapshot, LatestResourceEventsSnapshot, ImplicitlyIssuedResourceEventsSnapshot, IMStateSnapshot}
+import gr.grnet.aquarium.event.im.{StdIMEvent, IMEventModel}
/**
* A comprehensive representation of the User's state.
* 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.user.UserStateChangeReason]] for which the usr state has changed.
+ * The [[gr.grnet.aquarium.computation.reason.UserStateChangeReason]] for which the usr state has changed.
* @param totalEventsProcessedCounter
* @param parentUserStateId
* The `ID` of the parent state. The parent state is the one used as a reference point in order to calculate
billingPeriodOutOfSyncResourceEventsCounter: Long,
imStateSnapshot: IMStateSnapshot,
creditsSnapshot: CreditSnapshot,
- agreementsSnapshot: AgreementSnapshot,
+ agreementsSnapshot: AgreementsSnapshot,
ownedResourcesSnapshot: OwnedResourcesSnapshot,
newWalletEntries: List[NewWalletEntry],
+ occurredMillis: Long, // The time fro which this state is relevant
// The last known change reason for this userState
lastChangeReason: UserStateChangeReason = NoSpecificChangeReason,
totalEventsProcessedCounter: Long = 0L,
_id: ObjectId = new ObjectId()
) extends JsonSupport {
- private[this] def _allSnapshots: List[Long] = {
- List(
- imStateSnapshot.snapshotTime,
- creditsSnapshot.snapshotTime, agreementsSnapshot.snapshotTime,
- ownedResourcesSnapshot.snapshotTime,
- implicitlyIssuedSnapshot.snapshotTime,
- latestResourceEventsSnapshot.snapshotTime
- )
- }
-
- def oldestSnapshotTime: Long = _allSnapshots min
-
- def newestSnapshotTime: Long = _allSnapshots max
-
def idOpt: Option[String] = _id match {
case null ⇒ None
case _id ⇒ Some(_id.toString)
//
// def userCreationFormatedDate = new MutableDateCalc(userCreationMillis).toString
- def maybeDSLAgreement(at: Long): Maybe[DSLAgreement] = {
- agreementsSnapshot match {
- case snapshot @ AgreementSnapshot(data, _) ⇒
- snapshot.getAgreement(at)
- case _ ⇒
- Failed(new AquariumException("No agreement snapshot found for user %s".format(userID)))
- }
+ def findDSLAgreementForTime(at: Long): Option[DSLAgreement] = {
+ agreementsSnapshot.findForTime(at)
}
- def findResourceInstanceSnapshot(resource: String, instanceId: String): Maybe[ResourceInstanceSnapshot] = {
+ def findResourceInstanceSnapshot(resource: String, instanceId: String): Option[ResourceInstanceSnapshot] = {
ownedResourcesSnapshot.findResourceInstanceSnapshot(resource, instanceId)
}
ownedResourcesSnapshot = newResources,
stateChangeCounter = this.stateChangeCounter + 1)
}
-
+
def copyForChangeReason(changeReason: UserStateChangeReason) = {
this.copy(lastChangeReason = changeReason)
}
def modifyFromIMEvent(imEvent: IMEventModel, snapshotMillis: Long): UserState = {
this.copy(
isInitial = false,
- imStateSnapshot = IMStateSnapshot(imEvent, snapshotMillis),
- lastChangeReason = IMEventArrival(imEvent)
+ imStateSnapshot = IMStateSnapshot(imEvent),
+ lastChangeReason = IMEventArrival(imEvent),
+ occurredMillis = snapshotMillis
)
}
// calculationReason)
}
-
object UserState {
def fromJson(json: String): UserState = {
StdConverters.AllConverters.convertEx[UserState](JsonTextFormat(json))
final val _id = "_id"
final val userID = "userID"
}
-}
-
-final class BillingMonthInfo private(val year: Int,
- val month: Int,
- val startMillis: Long,
- val stopMillis: Long) extends Ordered[BillingMonthInfo] {
-
- def previousMonth: BillingMonthInfo = {
- BillingMonthInfo.fromDateCalc(new MutableDateCalc(year, month).goPreviousMonth)
- }
-
- def nextMonth: BillingMonthInfo = {
- BillingMonthInfo.fromDateCalc(new MutableDateCalc(year, month).goNextMonth)
- }
+ def createInitialUserState(imEvent: IMEventModel, credits: Double, agreementName: String) = {
+ if(!imEvent.isCreateUser) {
+ throw new AquariumInternalError(
+ "Got '%s' instead of '%s'".format(imEvent.eventType, IMEventModel.EventTypeNames.create))
+ }
- def compare(that: BillingMonthInfo) = {
- val ds = this.startMillis - that.startMillis
- if(ds < 0) -1 else if(ds == 0) 0 else 1
- }
-
-
- override def equals(any: Any) = any match {
- case that: BillingMonthInfo ⇒
- this.year == that.year && this.month == that.month // normally everything else MUST be the same by construction
- case _ ⇒
- false
- }
-
- override def hashCode() = {
- 31 * year + month
+ val userID = imEvent.userID
+ val userCreationMillis = imEvent.occurredMillis
+
+ UserState(
+ true,
+ userID,
+ userCreationMillis,
+ 0L,
+ false,
+ null,
+ ImplicitlyIssuedResourceEventsSnapshot(List()),
+ Nil,
+ Nil,
+ LatestResourceEventsSnapshot(List()),
+ 0L,
+ 0L,
+ IMStateSnapshot(imEvent),
+ CreditSnapshot(credits),
+ AgreementsSnapshot(List(AgreementSnapshot(agreementName, userCreationMillis))),
+ OwnedResourcesSnapshot(Nil),
+ Nil,
+ userCreationMillis,
+ InitialUserStateSetup
+ )
}
- override def toString = "%s-%02d".format(year, month)
-}
-
-object BillingMonthInfo {
- def fromMillis(millis: Long): BillingMonthInfo = {
- fromDateCalc(new MutableDateCalc(millis))
+ def createInitialUserState(userID: String,
+ userCreationMillis: Long,
+ isActive: Boolean,
+ credits: Double,
+ roleNames: List[String] = List(),
+ agreementName: String = DSLAgreement.DefaultAgreementName) = {
+ val now = userCreationMillis
+
+ UserState(
+ true,
+ userID,
+ userCreationMillis,
+ 0L,
+ false,
+ null,
+ ImplicitlyIssuedResourceEventsSnapshot(List()),
+ Nil,
+ Nil,
+ LatestResourceEventsSnapshot(List()),
+ 0L,
+ 0L,
+ IMStateSnapshot(
+ StdIMEvent(
+ "",
+ now, now, userID,
+ "",
+ isActive, roleNames.headOption.getOrElse("default"),
+ "1.0",
+ IMEventModel.EventTypeNames.create, Map())
+ ),
+ CreditSnapshot(credits),
+ AgreementsSnapshot(List(AgreementSnapshot(agreementName, userCreationMillis))),
+ OwnedResourcesSnapshot(Nil),
+ Nil,
+ now,
+ InitialUserStateSetup
+ )
}
- def fromDateCalc(mdc: MutableDateCalc): BillingMonthInfo = {
- val year = mdc.getYear
- val month = mdc.getMonthOfYear
- val startMillis = mdc.goStartOfThisMonth.getMillis
- val stopMillis = mdc.goEndOfThisMonth.getMillis // no need to `copy` here, since we are discarding `mdc`
-
- new BillingMonthInfo(year, month, startMillis, stopMillis)
+ def createInitialUserStateFrom(us: UserState): UserState = {
+ createInitialUserState(
+ us.imStateSnapshot.imEvent,
+ us.creditsSnapshot.creditAmount,
+ us.agreementsSnapshot.agreementsByTimeslot.valuesIterator.toList.last)
}
}
-
-sealed trait UserStateChangeReason {
- /**
- * Return `true` if the result of the calculation should be stored back to the
- * [[gr.grnet.aquarium.store.UserStateStore]].
- *
- */
- def shouldStoreUserState: Boolean
-
- def shouldStoreCalculatedWalletEntries: Boolean
-
- def forPreviousBillingMonth: UserStateChangeReason
-
- def calculateCreditsForImplicitlyTerminated: Boolean
-
- def code: UserStateChangeReasonCodes.ChangeReasonCode
-}
-
-object UserStateChangeReasonCodes {
- type ChangeReasonCode = Int
-
- final val InitialSetupCode = 1
- final val NoSpecificChangeCode = 2
- final val MonthlyBillingCode = 3
- final val RealtimeBillingCode = 4
- final val IMEventArrivalCode = 5
-}
-
-case object InitialUserStateSetup extends UserStateChangeReason {
- def shouldStoreUserState = true
-
- def shouldStoreCalculatedWalletEntries = false
-
- def forPreviousBillingMonth = this
-
- def calculateCreditsForImplicitlyTerminated = false
-
- def code = UserStateChangeReasonCodes.InitialSetupCode
-}
-/**
- * A calculation made for no specific reason. Can be for testing, for example.
- *
- */
-case object NoSpecificChangeReason extends UserStateChangeReason {
- def shouldStoreUserState = false
-
- def shouldStoreCalculatedWalletEntries = false
-
- def forBillingMonthInfo(bmi: BillingMonthInfo) = this
-
- def forPreviousBillingMonth = this
-
- def calculateCreditsForImplicitlyTerminated = false
-
- def code = UserStateChangeReasonCodes.NoSpecificChangeCode
-}
-
-/**
- * An authoritative calculation for the billing period.
- *
- * This marks a state for caching.
- *
- * @param billingMonthInfo
- */
-case class MonthlyBillingCalculation(billingMonthInfo: BillingMonthInfo) extends UserStateChangeReason {
- def shouldStoreUserState = true
-
- def shouldStoreCalculatedWalletEntries = true
-
- def forPreviousBillingMonth = MonthlyBillingCalculation(billingMonthInfo.previousMonth)
-
- def calculateCreditsForImplicitlyTerminated = true
-
- def code = UserStateChangeReasonCodes.MonthlyBillingCode
-}
-
-/**
- * Used for the realtime billing calculation.
- *
- * @param forWhenMillis The time this calculation is for
- */
-case class RealtimeBillingCalculation(forWhenMillis: Long) extends UserStateChangeReason {
- def shouldStoreUserState = false
-
- def shouldStoreCalculatedWalletEntries = false
-
- def forPreviousBillingMonth = this
-
- def calculateCreditsForImplicitlyTerminated = false
-
- def code = UserStateChangeReasonCodes.RealtimeBillingCode
-}
-
-case class IMEventArrival(imEvent: IMEventModel) extends UserStateChangeReason {
- def shouldStoreUserState = true
-
- def shouldStoreCalculatedWalletEntries = false
-
- def forPreviousBillingMonth = this
-
- def calculateCreditsForImplicitlyTerminated = false
-
- def code = UserStateChangeReasonCodes.IMEventArrivalCode
-}
* or implied, of GRNET S.A.
*/
-package gr.grnet.aquarium.user
-
+package gr.grnet.aquarium.computation
import scala.collection.mutable
import gr.grnet.aquarium.util.{ContextualLogger, Loggable}
import gr.grnet.aquarium.event.resource.ResourceEventModel
import gr.grnet.aquarium.event.im.{IMEventModel, StdIMEvent}
import gr.grnet.aquarium.{AquariumInternalError, AquariumException}
+import gr.grnet.aquarium.computation.data._
+import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, UserStateChangeReason, InitialUserStateSetup}
/**
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
class UserStateComputations extends Loggable {
- def createInitialUserState(imEvent: IMEventModel, credits: Double, agreementName: String) = {
- if(!imEvent.isCreateUser) {
- throw new AquariumInternalError(
- "Got '%s' instead of '%s'".format(imEvent.eventType, IMEventModel.EventTypeNames.create))
- }
-
- val userID = imEvent.userID
- val userCreationMillis = imEvent.occurredMillis
- val now = TimeHelpers.nowMillis()
-
- UserState(
- true,
- userID,
- userCreationMillis,
- 0L,
- false,
- null,
- ImplicitlyIssuedResourceEventsSnapshot(List(), now),
- Nil,
- Nil,
- LatestResourceEventsSnapshot(List(), now),
- 0L,
- 0L,
- IMStateSnapshot(imEvent, now),
- CreditSnapshot(credits, now),
- AgreementSnapshot(List(Agreement(agreementName, userCreationMillis)), now),
- OwnedResourcesSnapshot(Nil, now),
- Nil,
- InitialUserStateSetup
- )
- }
-
- def createInitialUserState(userID: String,
- userCreationMillis: Long,
- isActive: Boolean,
- credits: Double,
- roleNames: List[String] = List(),
- agreementName: String = DSLAgreement.DefaultAgreementName) = {
- val now = userCreationMillis
-
- UserState(
- true,
- userID,
- userCreationMillis,
- 0L,
- false,
- null,
- ImplicitlyIssuedResourceEventsSnapshot(List(), now),
- Nil,
- Nil,
- LatestResourceEventsSnapshot(List(), now),
- 0L,
- 0L,
- IMStateSnapshot(
- StdIMEvent(
- "",
- now, now, userID,
- "",
- isActive, roleNames.headOption.getOrElse("default"),
- "1.0",
- IMEventModel.EventTypeNames.create, Map()),
- now
- ),
- CreditSnapshot(credits, now),
- AgreementSnapshot(List(Agreement(agreementName, userCreationMillis)), now),
- OwnedResourcesSnapshot(Nil, now),
- Nil,
- InitialUserStateSetup
- )
- }
-
- def createInitialUserStateFrom(us: UserState): UserState = {
- createInitialUserState(
- us.imStateSnapshot.imEvent,
- us.creditsSnapshot.creditAmount,
- us.agreementsSnapshot.agreementsByTimeslot.valuesIterator.toList.last)
- }
def findUserStateAtEndOfBillingMonth(userId: String,
billingMonthInfo: BillingMonthInfo,
val userCreationMillis = currentUserState.userCreationMillis
val userCreationDateCalc = new MutableDateCalc(userCreationMillis)
val billingMonthStartMillis = billingMonthInfo.startMillis
- val billingMonthStopMillis = billingMonthInfo.stopMillis
+ val billingMonthStopMillis = billingMonthInfo.stopMillis
if(billingMonthStopMillis < userCreationMillis) {
// If the user did not exist for this billing month, piece of cake
clog.debug("User did not exist before %s", userCreationDateCalc)
// NOTE: Reason here will be: InitialUserStateSetup$
- val initialUserState0 = createInitialUserStateFrom(currentUserState)
+ val initialUserState0 = UserState.createInitialUserStateFrom(currentUserState)
val initialUserState1 = userStateStore.insertUserState(initialUserState0)
clog.debug("Returning INITIAL state [_id=%s] %s".format(initialUserState1._id, initialUserState1))
case Some(latestUserState) ⇒
// Found a "latest" user state but need to see if it is indeed the true and one latest.
// For this reason, we must count the events again.
- val latestStateOOSEventsCounter = latestUserState.billingPeriodOutOfSyncResourceEventsCounter
- val actualOOSEventsCounter = resourceEventStore.countOutOfSyncEventsForBillingPeriod(
- userId,
- billingMonthStartMillis,
- billingMonthStopMillis)
-
- val counterDiff = actualOOSEventsCounter - latestStateOOSEventsCounter
- counterDiff match {
- // ZERO, we are OK!
- case 0 ⇒
- // NOTE: Keep the caller's calculation reason
- latestUserState.copyForChangeReason(calculationReason)
-
- // We had more, so must recompute
- 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 = doCompute
- clog.end()
- result
-
- // We had less????
- case n if n < 0 ⇒
- val errMsg = "Found %s out of sync events (%s less). DB must be inconsistent".format(actualOOSEventsCounter, n)
- clog.warn(errMsg)
- throw new AquariumException(errMsg)
- }
+ val latestStateOOSEventsCounter = latestUserState.billingPeriodOutOfSyncResourceEventsCounter
+ val actualOOSEventsCounter = resourceEventStore.countOutOfSyncEventsForBillingPeriod(
+ userId,
+ billingMonthStartMillis,
+ billingMonthStopMillis)
+
+ val counterDiff = actualOOSEventsCounter - latestStateOOSEventsCounter
+ counterDiff match {
+ // ZERO, we are OK!
+ case 0 ⇒
+ // NOTE: Keep the caller's calculation reason
+ latestUserState.copyForChangeReason(calculationReason)
+
+ // We had more, so must recompute
+ 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 = doCompute
+ clog.end()
+ result
+
+ // We had less????
+ case n if n < 0 ⇒
+ val errMsg = "Found %s out of sync events (%s less). DB must be inconsistent".format(actualOOSEventsCounter, n)
+ clog.warn(errMsg)
+ throw new AquariumException(errMsg)
+ }
}
}
}
def rcDebugInfo(rcEvent: ResourceEventModel) = {
rcEvent.toDebugString(false)
}
+
//- Utility methods
def processResourceEvent(startingUserState: UserState,
if(stateChangeReason.shouldStoreCalculatedWalletEntries) {
val newWalletEntry = NewWalletEntry(
- userStateWorker.userId,
+ userStateWorker.userID,
newCreditsDiff,
oldCredits,
newCredits,
}
_workingUserState = _workingUserState.copy(
- creditsSnapshot = CreditSnapshot(newCredits, TimeHelpers.nowMillis()),
+ creditsSnapshot = CreditSnapshot(newCredits),
stateChangeCounter = _workingUserState.stateChangeCounter + 1,
totalEventsProcessedCounter = _workingUserState.totalEventsProcessedCounter + 1
)
// 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,
+ userStateWorker.userID,
LatestResourceEventsWorker.fromList(specialEvents),
ImplicitlyIssuedResourceEventsWorker.Empty,
IgnoredFirstResourceEventsWorker.Empty,
_workingUserState
}
}
-
-/**
- * A helper object holding intermediate state/results during resource event processing.
- *
- * @param previousResourceEvents
- * This is a collection of all the latest resource events.
- * We want these in order to correlate incoming resource events with their previous (in `occurredMillis` time)
- * ones. Will be updated on processing the next resource event.
- *
- * @param implicitlyIssuedStartEvents
- * The implicitly issued resource events at the beginning of the billing period.
- *
- * @param ignoredFirstResourceEvents
- * The resource events that were first (and unused) of their kind.
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class UserStateWorker(userId: String,
- previousResourceEvents: LatestResourceEventsWorker,
- implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker,
- ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker,
- accounting: Accounting,
- resourcesMap: DSLResourcesMap) {
-
- /**
- * Finds the previous resource event by checking two possible sources: a) The implicitly terminated resource
- * events and b) the explicit previous resource events. If the event is found, it is removed from the
- * respective source.
- *
- * If the event is not found, then this must be for a new resource instance.
- * (and probably then some `zero` resource event must be implied as the previous one)
- *
- * @param resource
- * @param instanceId
- * @return
- */
- def findAndRemovePreviousResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
- // implicitly issued events are checked first
- implicitlyIssuedStartEvents.findAndRemoveResourceEvent(resource, instanceId) match {
- case some @ Some(_) ⇒
- some
- case None ⇒
- // explicit previous resource events are checked second
- previousResourceEvents.findAndRemoveResourceEvent(resource, instanceId) match {
- case some @ Some(_) ⇒
- some
- case _ ⇒
- None
- }
- }
- }
-
- def updateIgnored(resourceEvent: ResourceEventModel): Unit = {
- ignoredFirstResourceEvents.updateResourceEvent(resourceEvent)
- }
-
- def updatePrevious(resourceEvent: ResourceEventModel): Unit = {
- previousResourceEvents.updateResourceEvent(resourceEvent)
- }
-
- 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)
- }
- if(implicitlyIssuedStartEvents.size > 0) {
- val map = implicitlyIssuedStartEvents.implicitlyIssuedEventsMap.map { case (k, v) => (k, rcDebugInfo(v)) }
- clog.debugMap("implicitlyTerminatedResourceEvents", map, 0)
- }
- if(ignoredFirstResourceEvents.size > 0) {
- val map = ignoredFirstResourceEvents.ignoredFirstEventsMap.map { case (k, v) => (k, rcDebugInfo(v)) }
- clog.debugMap("ignoredFirstResourceEvents", map, 0)
- }
- }
-
-// private[this]
-// def allPreviousAndAllImplicitlyStarted: List[ResourceEvent] = {
-// val buffer: FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]()
-//
-// buffer ++= implicitlyIssuedStartEvents.implicitlyIssuedEventsMap
-// buffer ++= previousResourceEvents.latestEventsMap
-//
-// buffer.valuesIterator.toList
-// }
-
- /**
- * Find those events from `implicitlyIssuedStartEvents` and `previousResourceEvents` that will generate implicit
- * end events along with those implicitly issued events. Before returning, remove the events that generated the
- * implicit ends from the internal state of this instance.
- *
- * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
- */
- def findAndRemoveGeneratorsOfImplicitEndEvents(newOccuredMillis: Long
- ): (List[ResourceEventModel], List[ResourceEventModel]) = {
- val buffer = mutable.ListBuffer[(ResourceEventModel, ResourceEventModel)]()
- val checkSet = mutable.Set[ResourceEventModel]()
-
- def doItFor(map: ResourceEventModel.FullMutableResourceTypeMap): Unit = {
- val resourceEvents = map.valuesIterator
- for {
- resourceEvent ← resourceEvents
- dslResource ← resourcesMap.findResource(resourceEvent.safeResource)
- costPolicy = dslResource.costPolicy
- } {
- if(costPolicy.supportsImplicitEvents) {
- if(costPolicy.mustConstructImplicitEndEventFor(resourceEvent)) {
- val implicitEnd = costPolicy.constructImplicitEndEventFor(resourceEvent, newOccuredMillis)
-
- if(!checkSet.contains(resourceEvent)) {
- checkSet.add(resourceEvent)
- buffer append ((resourceEvent, implicitEnd))
- }
-
- // remove it anyway
- map.remove((resourceEvent.safeResource, resourceEvent.safeInstanceId))
- }
- }
- }
- }
-
- doItFor(previousResourceEvents.latestEventsMap) // we give priority for previous
- doItFor(implicitlyIssuedStartEvents.implicitlyIssuedEventsMap) // ... over implicitly issued...
-
- (buffer.view.map(_._1).toList, buffer.view.map(_._2).toList)
- }
-}
-
-object UserStateWorker {
- def fromUserState(userState: UserState, accounting: Accounting, resourcesMap: DSLResourcesMap): UserStateWorker = {
- UserStateWorker(
- userState.userID,
- userState.latestResourceEventsSnapshot.toMutableWorker,
- userState.implicitlyIssuedSnapshot.toMutableWorker,
- IgnoredFirstResourceEventsWorker.Empty,
- accounting,
- resourcesMap
- )
- }
-}
--- /dev/null
+/*
+ * 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
+
+import scala.collection.mutable
+import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap
+import gr.grnet.aquarium.logic.accounting.Accounting
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+import gr.grnet.aquarium.computation.data.{LatestResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, IgnoredFirstResourceEventsWorker}
+import gr.grnet.aquarium.util.ContextualLogger
+
+/**
+ * A helper object holding intermediate state/results during resource event processing.
+ *
+ * @param previousResourceEvents
+ * This is a collection of all the latest resource events.
+ * We want these in order to correlate incoming resource events with their previous (in `occurredMillis` time)
+ * ones. Will be updated on processing the next resource event.
+ *
+ * @param implicitlyIssuedStartEvents
+ * The implicitly issued resource events at the beginning of the billing period.
+ *
+ * @param ignoredFirstResourceEvents
+ * The resource events that were first (and unused) of their kind.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class UserStateWorker(userID: String,
+ previousResourceEvents: LatestResourceEventsWorker,
+ implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker,
+ ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker,
+ accounting: Accounting,
+ resourcesMap: DSLResourcesMap) {
+
+ /**
+ * Finds the previous resource event by checking two possible sources: a) The implicitly terminated resource
+ * events and b) the explicit previous resource events. If the event is found, it is removed from the
+ * respective source.
+ *
+ * If the event is not found, then this must be for a new resource instance.
+ * (and probably then some `zero` resource event must be implied as the previous one)
+ *
+ * @param resource
+ * @param instanceId
+ * @return
+ */
+ def findAndRemovePreviousResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
+ // implicitly issued events are checked first
+ implicitlyIssuedStartEvents.findAndRemoveResourceEvent(resource, instanceId) match {
+ case some@Some(_) ⇒
+ some
+ case None ⇒
+ // explicit previous resource events are checked second
+ previousResourceEvents.findAndRemoveResourceEvent(resource, instanceId) match {
+ case some@Some(_) ⇒
+ some
+ case _ ⇒
+ None
+ }
+ }
+ }
+
+ def updateIgnored(resourceEvent: ResourceEventModel): Unit = {
+ ignoredFirstResourceEvents.updateResourceEvent(resourceEvent)
+ }
+
+ def updatePrevious(resourceEvent: ResourceEventModel): Unit = {
+ previousResourceEvents.updateResourceEvent(resourceEvent)
+ }
+
+ 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)
+ }
+ if(implicitlyIssuedStartEvents.size > 0) {
+ val map = implicitlyIssuedStartEvents.implicitlyIssuedEventsMap.map {
+ case (k, v) => (k, rcDebugInfo(v))
+ }
+ clog.debugMap("implicitlyTerminatedResourceEvents", map, 0)
+ }
+ if(ignoredFirstResourceEvents.size > 0) {
+ val map = ignoredFirstResourceEvents.ignoredFirstEventsMap.map {
+ case (k, v) => (k, rcDebugInfo(v))
+ }
+ clog.debugMap("ignoredFirstResourceEvents", map, 0)
+ }
+ }
+
+ // private[this]
+ // def allPreviousAndAllImplicitlyStarted: List[ResourceEvent] = {
+ // val buffer: FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]()
+ //
+ // buffer ++= implicitlyIssuedStartEvents.implicitlyIssuedEventsMap
+ // buffer ++= previousResourceEvents.latestEventsMap
+ //
+ // buffer.valuesIterator.toList
+ // }
+
+ /**
+ * Find those events from `implicitlyIssuedStartEvents` and `previousResourceEvents` that will generate implicit
+ * end events along with those implicitly issued events. Before returning, remove the events that generated the
+ * implicit ends from the internal state of this instance.
+ *
+ * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
+ */
+ def findAndRemoveGeneratorsOfImplicitEndEvents(newOccuredMillis: Long
+ ): (List[ResourceEventModel], List[ResourceEventModel]) = {
+ val buffer = mutable.ListBuffer[(ResourceEventModel, ResourceEventModel)]()
+ val checkSet = mutable.Set[ResourceEventModel]()
+
+ def doItFor(map: ResourceEventModel.FullMutableResourceTypeMap): Unit = {
+ val resourceEvents = map.valuesIterator
+ for {
+ resourceEvent ← resourceEvents
+ dslResource ← resourcesMap.findResource(resourceEvent.safeResource)
+ costPolicy = dslResource.costPolicy
+ } {
+ if(costPolicy.supportsImplicitEvents) {
+ if(costPolicy.mustConstructImplicitEndEventFor(resourceEvent)) {
+ val implicitEnd = costPolicy.constructImplicitEndEventFor(resourceEvent, newOccuredMillis)
+
+ if(!checkSet.contains(resourceEvent)) {
+ checkSet.add(resourceEvent)
+ buffer append ((resourceEvent, implicitEnd))
+ }
+
+ // remove it anyway
+ map.remove((resourceEvent.safeResource, resourceEvent.safeInstanceId))
+ }
+ }
+ }
+ }
+
+ doItFor(previousResourceEvents.latestEventsMap) // we give priority for previous
+ doItFor(implicitlyIssuedStartEvents.implicitlyIssuedEventsMap) // ... over implicitly issued...
+
+ (buffer.view.map(_._1).toList, buffer.view.map(_._2).toList)
+ }
+}
+
+object UserStateWorker {
+ def fromUserState(userState: UserState, accounting: Accounting, resourcesMap: DSLResourcesMap): UserStateWorker = {
+ UserStateWorker(
+ userState.userID,
+ userState.latestResourceEventsSnapshot.toMutableWorker,
+ userState.implicitlyIssuedSnapshot.toMutableWorker,
+ IgnoredFirstResourceEventsWorker.Empty,
+ accounting,
+ resourcesMap
+ )
+ }
+}
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.util.date.MutableDateCalc
+import java.util.Date
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+
+/**
+ * Represents an agreement valid for a specific amount of time. By convention,
+ * if an agreement is currently valid, then the validTo field is equal to `Long.MaxValue`.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class AgreementSnapshot(name: String, validFrom: Long, validTo: Long = Long.MaxValue) {
+ require(validTo > validFrom)
+ require(!name.isEmpty)
+
+ def timeslot = Timeslot(new Date(validFrom), new Date(validTo))
+
+ override def toString =
+ "AgreementSnapshot(%s, %s, %s)".
+ format(name, new MutableDateCalc(validFrom), new MutableDateCalc(validTo))
+}
--- /dev/null
+/*
+ * 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
+
+import java.util.Date
+
+import gr.grnet.aquarium.logic.accounting.dsl.{DSLAgreement, Timeslot}
+import gr.grnet.aquarium.logic.accounting.Policy
+import scala.collection.immutable.{SortedMap, TreeMap}
+
+/**
+ * User agreement data that will be part of UserState.
+ * The provided list of agreements cannot have time gaps. This is checked at object creation type.
+ *
+ * Note: This is copied from UserDataSnapshot.scala/AgreementSnapshot.
+ * TODO: Review
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class AgreementsSnapshot(agreements: List[AgreementSnapshot]) {
+ ensureNoGaps(agreements.sortWith((a,b) => if (b.validFrom > a.validFrom) true else false))
+
+ def ensureNoGaps(agreements: List[AgreementSnapshot]): Unit = agreements match {
+ case ha :: (t @ (hb :: tail)) =>
+ assert(ha.validTo - hb.validFrom == 1);
+ ensureNoGaps(t)
+ case h :: Nil =>
+ assert(h.validTo == Long.MaxValue)
+ case Nil => ()
+ }
+
+ def agreementsByTimeslot: SortedMap[Timeslot, String] = {
+ TreeMap(agreements.map(ag => (ag.timeslot, ag.name)): _*)
+ }
+
+ /**
+ * Get the user agreement at the specified timestamp
+ */
+ def findForTime(at: Long): Option[DSLAgreement] = {
+ // FIXME: Refactor and do not make this static call to Policy
+ agreements.find{ x => x.validFrom < at && x.validTo > at} match {
+ case Some(x) => Policy.policy(new Date(at)).findAgreement(x.name)
+ case None => None
+ }
+ }
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.event.im.IMEventModel
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class IMStateSnapshot(imEvent: IMEventModel)
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class IgnoredFirstResourceEventsSnapshot(ignoredFirstEvents: List[ResourceEventModel]) {
+ def toMutableWorker = {
+ val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
+ for(ignoredFirstEvent <- ignoredFirstEvents) {
+ map(ignoredFirstEvent.fullResourceInfo) = ignoredFirstEvent
+ }
+
+ IgnoredFirstResourceEventsWorker(map)
+ }
+}
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.util.findAndRemoveFromMap
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+import gr.grnet.aquarium.event.resource.ResourceEventModel.FullMutableResourceTypeMap
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class IgnoredFirstResourceEventsWorker(ignoredFirstEventsMap: FullMutableResourceTypeMap) {
+ def toImmutableSnapshot(snapshotTime: Long) =
+ IgnoredFirstResourceEventsSnapshot(ignoredFirstEventsMap.valuesIterator.toList)
+
+ def findAndRemoveResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
+ findAndRemoveFromMap(ignoredFirstEventsMap, (resource, instanceId))
+ }
+
+ def updateResourceEvent(resourceEvent: ResourceEventModel): Unit = {
+ ignoredFirstEventsMap((resourceEvent.resource, resourceEvent.instanceID)) = resourceEvent
+ }
+
+ def size = ignoredFirstEventsMap.size
+
+ def foreach[U](f: ResourceEventModel => U): Unit = {
+ ignoredFirstEventsMap.valuesIterator.foreach(f)
+ }
+}
+
+object IgnoredFirstResourceEventsWorker {
+ final val Empty = IgnoredFirstResourceEventsWorker(scala.collection.mutable.Map())
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+
+/**
+ * Keeps the implicit OFF events when a billing period ends.
+ * This is normally recorded in the [[gr.grnet.aquarium.user.UserState]].
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[ResourceEventModel]) {
+ /**
+ * The gateway to playing with mutable state.
+ *
+ * @return A fresh instance of [[gr.grnet.aquarium.computation.data.ImplicitlyIssuedResourceEventsWorker]].
+ */
+ def toMutableWorker = {
+ val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
+ for(implicitEvent <- implicitlyIssuedEvents) {
+ map(implicitEvent.fullResourceInfo) = implicitEvent
+ }
+
+ ImplicitlyIssuedResourceEventsWorker(map)
+ }
+}
+
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.util.findAndRemoveFromMap
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+import gr.grnet.aquarium.event.resource.ResourceEventModel.FullMutableResourceTypeMap
+
+
+/**
+ * This is the mutable cousin of [[gr.grnet.aquarium.computation.data.ImplicitlyIssuedResourceEventsSnapshot]].
+ *
+ * @param implicitlyIssuedEventsMap
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class ImplicitlyIssuedResourceEventsWorker(implicitlyIssuedEventsMap: FullMutableResourceTypeMap) {
+
+ def toList: scala.List[ResourceEventModel] = {
+ implicitlyIssuedEventsMap.valuesIterator.toList
+ }
+
+ def toImmutableSnapshot(snapshotTime: Long) =
+ ImplicitlyIssuedResourceEventsSnapshot(toList)
+
+ def findAndRemoveResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
+ findAndRemoveFromMap(implicitlyIssuedEventsMap, (resource, instanceId))
+ }
+
+ def size = implicitlyIssuedEventsMap.size
+
+ def foreach[U](f: ResourceEventModel => U): Unit = {
+ implicitlyIssuedEventsMap.valuesIterator.foreach(f)
+ }
+}
+
+object ImplicitlyIssuedResourceEventsWorker {
+ final val Empty = ImplicitlyIssuedResourceEventsWorker(scala.collection.mutable.Map())
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+
+/**
+ * Keeps the latest resource event per resource instance.
+ *
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEventModel]) {
+
+ /**
+ * The gateway to playing with mutable state.
+ *
+ * @return A fresh instance of [[gr.grnet.aquarium.computation.data.LatestResourceEventsWorker]].
+ */
+ def toMutableWorker = {
+ val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
+ for(latestEvent <- resourceEvents) {
+ map(latestEvent.fullResourceInfo) = latestEvent
+ }
+ LatestResourceEventsWorker(map)
+ }
+}
+
--- /dev/null
+/*
+ * 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
+
+import gr.grnet.aquarium.util.findAndRemoveFromMap
+import gr.grnet.aquarium.event.resource.ResourceEventModel
+import gr.grnet.aquarium.event.resource.ResourceEventModel.FullMutableResourceTypeMap
+
+/**
+ * This is the mutable cousin of [[gr.grnet.aquarium.computation.data.LatestResourceEventsSnapshot]].
+ *
+ * @param latestEventsMap
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class LatestResourceEventsWorker(latestEventsMap: FullMutableResourceTypeMap) {
+
+ /**
+ * The gateway to immutable state.
+ *
+ * @param snapshotTime The relevant snapshot time.
+ * @return A fresh instance of [[gr.grnet.aquarium.computation.data.LatestResourceEventsSnapshot]].
+ */
+ def toImmutableSnapshot(snapshotTime: Long) =
+ LatestResourceEventsSnapshot(latestEventsMap.valuesIterator.toList)
+
+ def updateResourceEvent(resourceEvent: ResourceEventModel): Unit = {
+ latestEventsMap((resourceEvent.resource, resourceEvent.instanceID)) = resourceEvent
+ }
+
+ def findResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
+ latestEventsMap.get((resource, instanceId))
+ }
+
+ def findAndRemoveResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
+ findAndRemoveFromMap(latestEventsMap, (resource, instanceId))
+ }
+
+ def size = latestEventsMap.size
+
+ def foreach[U](f: ResourceEventModel => U): Unit = {
+ latestEventsMap.valuesIterator.foreach(f)
+ }
+}
+
+object LatestResourceEventsWorker {
+ final val Empty = LatestResourceEventsWorker(scala.collection.mutable.Map())
+
+ /**
+ * Helper factory to construct a worker from a list of events.
+ */
+ def fromList(latestEventsList: List[ResourceEventModel]): LatestResourceEventsWorker = {
+ LatestResourceEventsSnapshot(latestEventsList).toMutableWorker
+ }
+}
+
--- /dev/null
+/*
+ * 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
+
+/**
+ * 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.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+class OwnedResourcesMap(resourcesMap: Map[(String, String), Double]) {
+ def toResourcesSnapshot(snapshotTime: Long): OwnedResourcesSnapshot =
+ OwnedResourcesSnapshot(
+ resourcesMap map {
+ case ((name, instanceId), value) ⇒
+ ResourceInstanceSnapshot(name, instanceId, value) } toList
+ )
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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 OwnedResourcesSnapshot(resourceInstanceSnapshots: List[ResourceInstanceSnapshot]) {
+
+ def toResourcesMap: OwnedResourcesMap = {
+ val tuples = for(rc <- resourceInstanceSnapshots) yield ((rc.resource, rc.instanceId), (rc.instanceAmount))
+
+ new OwnedResourcesMap(Map(tuples.toSeq: _*))
+ }
+
+ 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.
+ resourceInstanceSnapshots.filterNot(_.isSameResourceInstance(resource, instanceId))
+ }
+
+ def findResourceInstanceSnapshot(resource: String, instanceId: String): Option[ResourceInstanceSnapshot] = {
+ resourceInstanceSnapshots.find(x => resource == x.resource && instanceId == x.instanceId)
+ }
+
+ def getResourceInstanceAmount(resource: String, instanceId: String, defaultValue: Double): Double = {
+ findResourceInstanceSnapshot(resource, instanceId).map(_.instanceAmount).getOrElse(defaultValue)
+ }
+
+ def computeResourcesSnapshotUpdate(resource: String, // resource name
+ instanceId: String, // resource instance id
+ newAmount: Double,
+ snapshotTime: Long): (OwnedResourcesSnapshot,
+ Option[ResourceInstanceSnapshot],
+ ResourceInstanceSnapshot) = {
+
+ val newResourceInstance = ResourceInstanceSnapshot(resource, instanceId, newAmount)
+ val oldResourceInstanceOpt = this.findResourceInstanceSnapshot(resource, instanceId)
+
+ val newResourceInstances = oldResourceInstanceOpt match {
+ case Some(oldResourceInstance) ⇒
+ // Resource instance found, so delete the old one and add the new one
+ newResourceInstance :: resourceInstanceSnapshotsExcept(resource, instanceId)
+
+ case None ⇒
+ // Resource not found, so this is the first time and we just add the new snapshot
+ newResourceInstance :: resourceInstanceSnapshots
+ }
+
+ val newOwnedResources = OwnedResourcesSnapshot(newResourceInstances)
+
+ (newOwnedResources, oldResourceInstanceOpt, newResourceInstance)
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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
+
+/**
+ * Maintains the current state of a resource instance owned by the user.
+ * The encoding is as follows:
+ *
+ * name: DSLResource.name
+ * instanceId: instance-id (in the resource's descriminatorField)
+ * data: current-resource-value
+ * snapshotTime: last-update-timestamp
+ *
+ * In order to have a uniform representation of the resource state for all
+ * resource types (complex or simple) the following convention applies:
+ *
+ * - 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 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]]
+ * 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]]
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+case class ResourceInstanceSnapshot(resource: String,
+ instanceId: String,
+ instanceAmount: Double) {
+
+ def isSameResourceInstance(resource: String, instanceId: String) = {
+ this.resource == resource &&
+ this.instanceId == instanceId
+ }
+}
--- /dev/null
+/*
+ * 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.reason
+
+import gr.grnet.aquarium.computation.BillingMonthInfo
+import gr.grnet.aquarium.event.im.IMEventModel
+
+sealed trait UserStateChangeReason {
+ /**
+ * Return `true` if the result of the calculation should be stored back to the
+ * [[gr.grnet.aquarium.store.UserStateStore]].
+ *
+ */
+ def shouldStoreUserState: Boolean
+
+ def shouldStoreCalculatedWalletEntries: Boolean
+
+ def forPreviousBillingMonth: UserStateChangeReason
+
+ def calculateCreditsForImplicitlyTerminated: Boolean
+
+ def code: UserStateChangeReasonCodes.ChangeReasonCode
+}
+
+case object InitialUserStateSetup extends UserStateChangeReason {
+ def shouldStoreUserState = true
+
+ def shouldStoreCalculatedWalletEntries = false
+
+ def forPreviousBillingMonth = this
+
+ def calculateCreditsForImplicitlyTerminated = false
+
+ def code = UserStateChangeReasonCodes.InitialSetupCode
+}
+/**
+ * A calculation made for no specific reason. Can be for testing, for example.
+ *
+ */
+case object NoSpecificChangeReason extends UserStateChangeReason {
+ def shouldStoreUserState = false
+
+ def shouldStoreCalculatedWalletEntries = false
+
+ def forBillingMonthInfo(bmi: BillingMonthInfo) = this
+
+ def forPreviousBillingMonth = this
+
+ def calculateCreditsForImplicitlyTerminated = false
+
+ def code = UserStateChangeReasonCodes.NoSpecificChangeCode
+}
+
+/**
+ * An authoritative calculation for the billing period.
+ *
+ * This marks a state for caching.
+ *
+ * @param billingMonthInfo
+ */
+case class MonthlyBillingCalculation(billingMonthInfo: BillingMonthInfo) extends UserStateChangeReason {
+ def shouldStoreUserState = true
+
+ def shouldStoreCalculatedWalletEntries = true
+
+ def forPreviousBillingMonth = MonthlyBillingCalculation(billingMonthInfo.previousMonth)
+
+ def calculateCreditsForImplicitlyTerminated = true
+
+ def code = UserStateChangeReasonCodes.MonthlyBillingCode
+}
+
+/**
+ * Used for the realtime billing calculation.
+ *
+ * @param forWhenMillis The time this calculation is for
+ */
+case class RealtimeBillingCalculation(forWhenMillis: Long) extends UserStateChangeReason {
+ def shouldStoreUserState = false
+
+ def shouldStoreCalculatedWalletEntries = false
+
+ def forPreviousBillingMonth = this
+
+ def calculateCreditsForImplicitlyTerminated = false
+
+ def code = UserStateChangeReasonCodes.RealtimeBillingCode
+}
+
+case class IMEventArrival(imEvent: IMEventModel) extends UserStateChangeReason {
+ def shouldStoreUserState = true
+
+ def shouldStoreCalculatedWalletEntries = false
+
+ def forPreviousBillingMonth = this
+
+ def calculateCreditsForImplicitlyTerminated = false
+
+ def code = UserStateChangeReasonCodes.IMEventArrivalCode
+}
--- /dev/null
+/*
+ * 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.reason
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+object UserStateChangeReasonCodes {
+ type ChangeReasonCode = Int
+
+ final val InitialSetupCode = 1
+ final val NoSpecificChangeCode = 2
+ final val MonthlyBillingCode = 3
+ final val RealtimeBillingCode = 4
+ final val IMEventArrivalCode = 5
+}
newTotalAmount: Double,
dslResource: DSLResource,
defaultResourceMap: DSLResourcesMap,
- agreementNamesByTimeslot: Map[Timeslot, String],
+ agreementNamesByTimeslot: SortedMap[Timeslot, String],
algorithmCompiler: CostPolicyAlgorithmCompiler,
policyStore: PolicyStore,
clogOpt: Option[ContextualLogger] = None): (Timeslot, List[Chargeslot]) = {
if (previousOccurred.getTime == event.occurredMillis) {
dslResource.costPolicy match {
case DiscreteCostPolicy => //Ok
- case _ => return Some(List())
+ case _ => return Just(List())
}
}
package gr.grnet.aquarium.store
-import gr.grnet.aquarium.user.UserState
-import com.ckkloverdos.maybe.Maybe
+import gr.grnet.aquarium.computation.UserState
/**
* A store for user state snapshots.
*
- * This is used to hold snapshots of [[gr.grnet.aquarium.user.UserState]]
+ * This is used to hold snapshots of [[gr.grnet.aquarium.computation.UserState]]
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
import java.util.Date
import collection.mutable.ConcurrentMap
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.Configurable
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}
+import gr.grnet.aquarium.computation.UserState
/**
* An implementation of various stores that persists data in memory.
goodOnes.sortWith {
case (us1, us2) ⇒
- us1.oldestSnapshotTime > us2.oldestSnapshotTime
+ us1.occurredMillis > us2.occurredMillis
} match {
case head :: _ ⇒
Some(head)
goodOnes.sortWith {
case (us1, us2) ⇒
- us1.oldestSnapshotTime > us2.oldestSnapshotTime
+ us1.occurredMillis > us2.occurredMillis
} match {
case head :: _ ⇒
Some(head)
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.computation.UserState.{JsonNames ⇒ UserStateJsonNames}
import gr.grnet.aquarium.util.json.JsonSupport
import collection.mutable.ListBuffer
import gr.grnet.aquarium.event._
import com.ckkloverdos.maybe.Maybe
import gr.grnet.aquarium.util._
import gr.grnet.aquarium.converter.Conversions
+import gr.grnet.aquarium.computation.UserState
/**
* Mongodb implementation of the various aquarium stores.
final val RESOURCE_EVENTS_COLLECTION = "resevents"
/**
- * Collection holding the snapshots of [[gr.grnet.aquarium.user.UserState]].
+ * Collection holding the snapshots of [[gr.grnet.aquarium.computation.UserState]].
*
- * [[gr.grnet.aquarium.user.UserState]] is held internally within [[gr.grnet.aquarium.actor.service.user .UserActor]]s.
+ * [[gr.grnet.aquarium.computation.UserState]] is held internally within [[gr.grnet.aquarium.actor.service.user.UserActor]]s.
*/
final val USER_STATES_COLLECTION = "userstates"
+++ /dev/null
-/*
- * 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
-package user
-
-import gr.grnet.aquarium.util.{findFromMapAsMaybe, findAndRemoveFromMap, shortClassNameOf}
-import gr.grnet.aquarium.logic.accounting.Policy
-import java.util.Date
-import com.ckkloverdos.maybe.{NoVal, Maybe, Just}
-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
-import gr.grnet.aquarium.event.im.IMEventModel
-
-/**
- * Snapshot of data that are user-related.
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-
-case class CreditSnapshot(creditAmount: Double, snapshotTime: Long) extends DataSnapshot
-
-case class IMStateSnapshot(imEvent: IMEventModel, snapshotTime: Long) extends DataSnapshot
-
-//case class RolesSnapshot(roles: List[String], snapshotTime: Long) extends DataSnapshot
-
-/**
- * Represents an agreement valid for a specific amount of time. By convention,
- * if an agreement is currently valid, then the validTo field is equal to `Long.MaxValue`.
- */
-case class Agreement(name: String, validFrom: Long, validTo: Long = Long.MaxValue) {
- assert(validTo > validFrom)
- assert(!name.isEmpty)
-
-// Policy.policy(new Date(validFrom)) match {
-// case Just(x) => x.findAgreement(agreement) match {
-// case None => assert(false)
-// case _ =>
-// }
-// case _ => assert(false)
-// }
-
- def timeslot = Timeslot(new Date(validFrom), new Date(validTo))
-
- override def toString =
- "Agreement(%s, %s, %s)".
- format(name, new MutableDateCalc(validFrom), new MutableDateCalc(validTo))
-}
-
-/**
- * All user agreements. The provided list of agreements cannot have time gaps. This
- * is checked at object creation type.
- */
-case class AgreementSnapshot(agreements: List[Agreement], snapshotTime: Long) extends DataSnapshot {
-
- ensureNoGaps(agreements.sortWith((a,b) => if (b.validFrom > a.validFrom) true else false))
-
- def agreementsByTimeslot: SortedMap[Timeslot, String] = {
- TreeMap(agreements.map(ag => (ag.timeslot, ag.name)): _*)
- }
-
- def ensureNoGaps(agreements: List[Agreement]): Unit = agreements match {
- case ha :: (t @ (hb :: tail)) =>
- assert(ha.validTo - hb.validFrom == 1);
- ensureNoGaps(t)
- case h :: Nil =>
- assert(h.validTo == Long.MaxValue)
- case Nil => ()
- }
-
- /**
- * Get the user agreement at the specified timestamp
- */
- def getAgreement(at: Long): Maybe[DSLAgreement] =
- agreements.find{ x => x.validFrom < at && x.validTo > at} match {
- case Some(x) => Policy.policy(new Date(at)).findAgreement(x.name) match {
- case Some(z) => Just(z)
- case None => NoVal
- }
- case None => NoVal
- }
-
- override def toString = {
- "%s(%s, %s)".format(shortClassNameOf(this), agreements, new MutableDateCalc(snapshotTime).toString)
- }
-}
-
-/**
- * Maintains the current state of a resource instance owned by the user.
- * The encoding is as follows:
- *
- * name: DSLResource.name
- * instanceId: instance-id (in the resource's descriminatorField)
- * data: current-resource-value
- * snapshotTime: last-update-timestamp
- *
- * In order to have a uniform representation of the resource state for all
- * resource types (complex or simple) the following convention applies:
- *
- * - 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 instanceAmount This is the amount kept for the resource instance.
-* The general rule is that an amount saved in a [[gr.grnet.aquarium.user.ResourceInstanceSnapshot]]
- * represents a total value, while a value appearing in a [[gr.grnet.aquarium.event.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]]
- * @param snapshotTime
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class ResourceInstanceSnapshot(resource: String,
- instanceId: String,
- instanceAmount: Double,
- snapshotTime: Long) extends DataSnapshot {
-
- def isSameResourceInstance(resource: String, instanceId: String) = {
- this.resource == resource &&
- this.instanceId == instanceId
- }
-}
-
-/**
- * A map from (resourceName, resourceInstanceId) to (value, snapshotTime).
- * This representation is convenient for computations and updating, while the
- * [[gr.grnet.aquarium.user.OwnedResourcesSnapshot]] representation is convenient for JSON serialization.
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-class OwnedResourcesMap(resourcesMap: Map[(String, String), (Double, Long)]) {
- def toResourcesSnapshot(snapshotTime: Long): OwnedResourcesSnapshot =
- OwnedResourcesSnapshot(
- resourcesMap map {
- case ((name, instanceId), (value, snapshotTime)) ⇒
- ResourceInstanceSnapshot(name, instanceId, value, snapshotTime
- )} toList,
- snapshotTime
- )
-}
-
-/**
- *
- * @param resourceInstanceSnapshots
- * @param snapshotTime
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class OwnedResourcesSnapshot(resourceInstanceSnapshots: List[ResourceInstanceSnapshot], snapshotTime: Long)
- extends DataSnapshot {
-
- def toResourcesMap: OwnedResourcesMap = {
- val tuples = for(rc <- resourceInstanceSnapshots) yield ((rc.resource, rc.instanceId), (rc.instanceAmount, rc.snapshotTime))
-
- new OwnedResourcesMap(Map(tuples.toSeq: _*))
- }
-
- 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.
- resourceInstanceSnapshots.filterNot(_.isSameResourceInstance(resource, instanceId))
- }
-
- def findResourceInstanceSnapshot(resource: String, instanceId: String): Option[ResourceInstanceSnapshot] = {
- resourceInstanceSnapshots.find(x => resource == x.resource && instanceId == x.instanceId)
- }
-
- def getResourceInstanceAmount(resource: String, instanceId: String, defaultValue: Double): Double = {
- findResourceInstanceSnapshot(resource, instanceId).map(_.instanceAmount).getOrElse(defaultValue)
- }
-
- def computeResourcesSnapshotUpdate(resource: String, // resource name
- instanceId: String, // resource instance id
- newAmount: Double,
- snapshotTime: Long): (OwnedResourcesSnapshot,
- Option[ResourceInstanceSnapshot],
- ResourceInstanceSnapshot) = {
-
- val newResourceInstance = ResourceInstanceSnapshot(resource, instanceId, newAmount, snapshotTime)
- val oldResourceInstanceOpt = this.findResourceInstanceSnapshot(resource, instanceId)
-
- val newResourceInstances = oldResourceInstanceOpt match {
- case Some(oldResourceInstance) ⇒
- // Resource instance found, so delete the old one and add the new one
- newResourceInstance :: resourceInstanceSnapshotsExcept(resource, instanceId)
- case None ⇒
- // Resource not found, so this is the first time and we just add the new snapshot
- newResourceInstance :: resourceInstanceSnapshots
- }
-
- val newOwnedResources = OwnedResourcesSnapshot(newResourceInstances, snapshotTime)
-
- (newOwnedResources, oldResourceInstanceOpt, newResourceInstance)
- }
-}
-
-
-/**
- * A generic exception thrown when errors occur in dealing with user data snapshots
- *
- * @author Georgios Gousios <gousiosg@gmail.com>
- */
-class DataSnapshotException(msg: String) extends Exception(msg)
-
-/**
- * Holds the user active/suspended status.
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-//case class ActiveStateSnapshot(isActive: Boolean, snapshotTime: Long) extends DataSnapshot
-
-/**
- * Keeps the latest resource event per resource instance.
- *
- * @param resourceEvents
- * @param snapshotTime
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEventModel],
- snapshotTime: Long) extends DataSnapshot {
-
- /**
- * The gateway to playing with mutable state.
- *
- * @return A fresh instance of [[gr.grnet.aquarium.user.LatestResourceEventsWorker]].
- */
- def toMutableWorker = {
- val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
- for(latestEvent <- resourceEvents) {
- map(latestEvent.fullResourceInfo) = latestEvent
- }
- LatestResourceEventsWorker(map)
- }
-}
-
-/**
- * This is the mutable cousin of [[gr.grnet.aquarium.user.LatestResourceEventsSnapshot]].
- *
- * @param latestEventsMap
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class LatestResourceEventsWorker(latestEventsMap: FullMutableResourceTypeMap) {
-
- /**
- * The gateway to immutable state.
- *
- * @param snapshotTime The relevant snapshot time.
- * @return A fresh instance of [[gr.grnet.aquarium.user.LatestResourceEventsSnapshot]].
- */
- def toImmutableSnapshot(snapshotTime: Long) =
- LatestResourceEventsSnapshot(latestEventsMap.valuesIterator.toList, snapshotTime)
-
- def updateResourceEvent(resourceEvent: ResourceEventModel): Unit = {
- latestEventsMap((resourceEvent.resource, resourceEvent.instanceID)) = resourceEvent
- }
-
- def findResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
- latestEventsMap.get((resource, instanceId))
- }
-
- def findAndRemoveResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
- findAndRemoveFromMap(latestEventsMap, (resource, instanceId))
- }
-
- def size = latestEventsMap.size
-
- def foreach[U](f: ResourceEventModel => U): Unit = {
- latestEventsMap.valuesIterator.foreach(f)
- }
-}
-
-object LatestResourceEventsWorker {
- final val Empty = LatestResourceEventsWorker(scala.collection.mutable.Map())
-
- /**
- * Helper factory to construct a worker from a list of events.
- */
- def fromList(latestEventsList: List[ResourceEventModel]): LatestResourceEventsWorker = {
- LatestResourceEventsSnapshot(latestEventsList, 0L).toMutableWorker
- }
-}
-
-/**
- * Keeps the implicit OFF events when a billing period ends.
- * This is normally recorded in the [[gr.grnet.aquarium.user.UserState]].
- *
- * @param implicitlyIssuedEvents
- * @param snapshotTime
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[ResourceEventModel],
- snapshotTime: Long) extends DataSnapshot {
- /**
- * The gateway to playing with mutable state.
- *
- * @return A fresh instance of [[gr.grnet.aquarium.user.ImplicitlyIssuedResourceEventsWorker]].
- */
- def toMutableWorker = {
- val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
- for(implicitEvent <- implicitlyIssuedEvents) {
- map(implicitEvent.fullResourceInfo) = implicitEvent
- }
-
- ImplicitlyIssuedResourceEventsWorker(map)
- }
-}
-
-/**
- * This is the mutable cousin of [[gr.grnet.aquarium.user.ImplicitlyIssuedResourceEventsSnapshot]].
- *
- * @param implicitlyIssuedEventsMap
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-case class ImplicitlyIssuedResourceEventsWorker(implicitlyIssuedEventsMap: FullMutableResourceTypeMap) {
-
- def toList: scala.List[ResourceEventModel] = {
- implicitlyIssuedEventsMap.valuesIterator.toList
- }
-
- def toImmutableSnapshot(snapshotTime: Long) =
- ImplicitlyIssuedResourceEventsSnapshot(toList, snapshotTime)
-
- def findAndRemoveResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
- findAndRemoveFromMap(implicitlyIssuedEventsMap, (resource, instanceId))
- }
-
- def size = implicitlyIssuedEventsMap.size
-
- def foreach[U](f: ResourceEventModel => U): Unit = {
- implicitlyIssuedEventsMap.valuesIterator.foreach(f)
- }
-}
-
-object ImplicitlyIssuedResourceEventsWorker {
- final val Empty = ImplicitlyIssuedResourceEventsWorker(scala.collection.mutable.Map())
-}
-
-/**
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- *
- * @param ignoredFirstEvents
- * @param snapshotTime
- */
-case class IgnoredFirstResourceEventsSnapshot(ignoredFirstEvents: List[ResourceEventModel],
- snapshotTime: Long) extends DataSnapshot {
- def toMutableWorker = {
- val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]()
- for(ignoredFirstEvent <- ignoredFirstEvents) {
- map(ignoredFirstEvent.fullResourceInfo) = ignoredFirstEvent
- }
-
- IgnoredFirstResourceEventsWorker(map)
- }
-}
-
-/**
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- * @param ignoredFirstEventsMap
- */
-case class IgnoredFirstResourceEventsWorker(ignoredFirstEventsMap: FullMutableResourceTypeMap) {
- def toImmutableSnapshot(snapshotTime: Long) =
- IgnoredFirstResourceEventsSnapshot(ignoredFirstEventsMap.valuesIterator.toList, snapshotTime)
-
- def findAndRemoveResourceEvent(resource: String, instanceId: String): Maybe[ResourceEventModel] = {
- findAndRemoveFromMap(ignoredFirstEventsMap, (resource, instanceId))
- }
-
- def updateResourceEvent(resourceEvent: ResourceEventModel): Unit = {
- ignoredFirstEventsMap((resourceEvent.resource, resourceEvent.instanceID)) = resourceEvent
- }
-
- def size = ignoredFirstEventsMap.size
-
- def foreach[U](f: ResourceEventModel => U): Unit = {
- ignoredFirstEventsMap.valuesIterator.foreach(f)
- }
-}
-
-object IgnoredFirstResourceEventsWorker {
- final val Empty = IgnoredFirstResourceEventsWorker(scala.collection.mutable.Map())
-}
\ No newline at end of file
package gr.grnet.aquarium.util
import org.slf4j.Logger
-import com.ckkloverdos.maybe.{Failed, Just, Maybe}
+import com.ckkloverdos.maybe.Failed
/**
* A logger that keeps track of working context and indentation level.
* A sample output follows:
*
* {{{
- [DEBUG] 2012-02-13 12:54:53,653 main - doFullMonthlyBilling(2012-01) BEGIN
- [DEBUG] 2012-02-13 12:54:53,653 main - findUserStateAtEndOfBillingMonth(2011-12) BEGIN
- [DEBUG] 2012-02-13 12:54:53,661 main - findUserStateAtEndOfBillingMonth(2011-12) Found 0 out of sync events, will have to (re)compute user state
- [DEBUG] 2012-02-13 12:54:53,661 main - findUserStateAtEndOfBillingMonth(2011-12) Computing full month billing
- [DEBUG] 2012-02-13 12:54:53,662 main - doFullMonthlyBilling(2011-12) BEGIN
- [DEBUG] 2012-02-13 12:54:53,662 main - findUserStateAtEndOfBillingMonth(2011-11) BEGIN
- [DEBUG] 2012-02-13 12:54:53,663 main - findUserStateAtEndOfBillingMonth(2011-11) Found 0 out of sync events, will have to (re)compute user state
- [DEBUG] 2012-02-13 12:54:53,663 main - findUserStateAtEndOfBillingMonth(2011-11) Computing full month billing
- [DEBUG] 2012-02-13 12:54:53,664 main - doFullMonthlyBilling(2011-11) BEGIN
- [DEBUG] 2012-02-13 12:54:53,664 main - findUserStateAtEndOfBillingMonth(2011-10) BEGIN
- [DEBUG] 2012-02-13 12:54:53,667 main - findUserStateAtEndOfBillingMonth(2011-10) User did not exist before 2011-11-01 00:00:00.000. Returning UserState(Christos,0,0,false,null,ImplicitlyIssuedResourceEventsSnapshot(Map(),0),List(),List(),LatestResourceEventsSnapshot(Map(),0),0,ActiveStateSnapshot(false,0),CreditSnapshot(0.0,0),AgreementSnapshot(List(Agreement(default,0,-1)),0),RolesSnapshot(List(),0),OwnedResourcesSnapshot(List(),0))
- [DEBUG] 2012-02-13 12:54:53,668 main - findUserStateAtEndOfBillingMonth(2011-10) END
- [DEBUG] 2012-02-13 12:54:53,672 main - doFullMonthlyBilling(2011-11) previousResourceEvents = LatestResourceEventsWorker(Map())
- [DEBUG] 2012-02-13 12:54:53,673 main - doFullMonthlyBilling(2011-11) theImplicitOFFs = ImplicitlyIssuedResourceEventsWorker(Map())
- [DEBUG] 2012-02-13 12:54:53,680 main - doFullMonthlyBilling(2011-11) resourceEventStore = MemStore(Map(UserState -> 0, WalletEntry -> 0, ResourceEvent -> 5, PolicyEntry -> 0, IMEvent -> 0))
- [DEBUG] 2012-02-13 12:54:53,681 main - doFullMonthlyBilling(2011-11) Found 0 resource events, starting processing...
- [DEBUG] 2012-02-13 12:54:53,683 main - doFullMonthlyBilling(2011-11) END
- [DEBUG] 2012-02-13 12:54:53,683 main - findUserStateAtEndOfBillingMonth(2011-11) END
- [DEBUG] 2012-02-13 12:54:53,684 main - doFullMonthlyBilling(2011-12) previousResourceEvents = LatestResourceEventsWorker(Map())
- [DEBUG] 2012-02-13 12:54:53,684 main - doFullMonthlyBilling(2011-12) theImplicitOFFs = ImplicitlyIssuedResourceEventsWorker(Map())
- [DEBUG] 2012-02-13 12:54:53,685 main - doFullMonthlyBilling(2011-12) resourceEventStore = MemStore(Map(UserState -> 0, WalletEntry -> 0, ResourceEvent -> 5, PolicyEntry -> 0, IMEvent -> 0))
- [DEBUG] 2012-02-13 12:54:53,686 main - doFullMonthlyBilling(2011-12) Found 0 resource events, starting processing...
- [DEBUG] 2012-02-13 12:54:53,686 main - doFullMonthlyBilling(2011-12) END
- [DEBUG] 2012-02-13 12:54:53,687 main - findUserStateAtEndOfBillingMonth(2011-12) END
- [DEBUG] 2012-02-13 12:54:53,687 main - doFullMonthlyBilling(2012-01) previousResourceEvents = LatestResourceEventsWorker(Map())
- [DEBUG] 2012-02-13 12:54:53,688 main - doFullMonthlyBilling(2012-01) theImplicitOFFs = ImplicitlyIssuedResourceEventsWorker(Map())
- [DEBUG] 2012-02-13 12:54:53,688 main - doFullMonthlyBilling(2012-01) resourceEventStore = MemStore(Map(UserState -> 0, WalletEntry -> 0, ResourceEvent -> 5, PolicyEntry -> 0, IMEvent -> 0))
- [DEBUG] 2012-02-13 12:54:53,689 main - doFullMonthlyBilling(2012-01) Found 4 resource events, starting processing...
- [DEBUG] 2012-02-13 12:54:53,690 main - doFullMonthlyBilling(2012-01) Processing EVENT(2, [2012-01-01 03:00:00.000], 99.0 [MB/Hr], diskspace::pithos/diskspace/DISK.1, Map(), Christos, pithos)
- [DEBUG] 2012-02-13 12:54:53,691 main - doFullMonthlyBilling(2012-01) 0 previousResourceEvents
- [DEBUG] 2012-02-13 12:54:53,691 main - doFullMonthlyBilling(2012-01) 0 theImplicitOFFs
- [DEBUG] 2012-02-13 12:54:53,694 main - doFullMonthlyBilling(2012-01) Processing: ResourceEvent(2,1325379600000,1325379600000,Christos,pithos,diskspace,pithos/diskspace/DISK.1,1.0,99.0,Map())
- [DEBUG] 2012-02-13 12:54:53,704 main - doFullMonthlyBilling(2012-01) Processing EVENT(0, [2012-01-02 01:00:00.000], ON, vmtime::synnefo/vmtime/VM.1, Map(), Christos, synnefo)
- [DEBUG] 2012-02-13 12:54:53,705 main - doFullMonthlyBilling(2012-01) 1 previousResourceEvents
- [DEBUG] 2012-02-13 12:54:53,708 main - doFullMonthlyBilling(2012-01) EVENT(2, [2012-01-01 03:00:00.000], 99.0 [MB/Hr], diskspace::pithos/diskspace/DISK.1, Map(), Christos, pithos)
- [DEBUG] 2012-02-13 12:54:53,709 main - doFullMonthlyBilling(2012-01) 0 theImplicitOFFs
- [DEBUG] 2012-02-13 12:54:53,709 main - doFullMonthlyBilling(2012-01) Ignoring not billable EVENT(0, [2012-01-02 01:00:00.000], ON, vmtime::synnefo/vmtime/VM.1, Map(), Christos, synnefo)
- [DEBUG] 2012-02-13 12:54:53,711 main - doFullMonthlyBilling(2012-01) Processing EVENT(3, [2012-01-02 04:00:00.000], 23.0 [MB/Hr], diskspace::pithos/diskspace/DISK.1, Map(), Christos, pithos)
- [DEBUG] 2012-02-13 12:54:53,712 main - doFullMonthlyBilling(2012-01) 2 previousResourceEvents
- [DEBUG] 2012-02-13 12:54:53,713 main - doFullMonthlyBilling(2012-01) EVENT(0, [2012-01-02 01:00:00.000], ON, vmtime::synnefo/vmtime/VM.1, Map(), Christos, synnefo)
- [DEBUG] 2012-02-13 12:54:53,714 main - doFullMonthlyBilling(2012-01) EVENT(2, [2012-01-01 03:00:00.000], 99.0 [MB/Hr], diskspace::pithos/diskspace/DISK.1, Map(), Christos, pithos)
- [DEBUG] 2012-02-13 12:54:53,714 main - doFullMonthlyBilling(2012-01) 0 theImplicitOFFs
- [DEBUG] 2012-02-13 12:54:53,715 main - doFullMonthlyBilling(2012-01) Processing: ResourceEvent(3,1325469600000,1325469600000,Christos,pithos,diskspace,pithos/diskspace/DISK.1,1.0,23.0,Map())
- [DEBUG] 2012-02-13 12:54:53,716 main - doFullMonthlyBilling(2012-01) Previous : ResourceEvent(2,1325379600000,1325379600000,Christos,pithos,diskspace,pithos/diskspace/DISK.1,1.0,99.0,Map())
- [DEBUG] 2012-02-13 12:54:53,718 main - doFullMonthlyBilling(2012-01) Processing EVENT(1, [2012-01-02 10:00:00.000], OFF, vmtime::synnefo/vmtime/VM.1, Map(), Christos, synnefo)
- [DEBUG] 2012-02-13 12:54:53,719 main - doFullMonthlyBilling(2012-01) 2 previousResourceEvents
- [DEBUG] 2012-02-13 12:54:53,719 main - doFullMonthlyBilling(2012-01) EVENT(0, [2012-01-02 01:00:00.000], ON, vmtime::synnefo/vmtime/VM.1, Map(), Christos, synnefo)
- [DEBUG] 2012-02-13 12:54:53,721 main - doFullMonthlyBilling(2012-01) EVENT(3, [2012-01-02 04:00:00.000], 23.0 [MB/Hr], diskspace::pithos/diskspace/DISK.1, Map(), Christos, pithos)
- [DEBUG] 2012-02-13 12:54:53,721 main - doFullMonthlyBilling(2012-01) 0 theImplicitOFFs
- [DEBUG] 2012-02-13 12:54:53,722 main - doFullMonthlyBilling(2012-01) Processing: ResourceEvent(1,1325491200000,1325491200000,Christos,synnefo,vmtime,synnefo/vmtime/VM.1,1.0,0.0,Map())
- [DEBUG] 2012-02-13 12:54:53,735 main - doFullMonthlyBilling(2012-01) Previous : ResourceEvent(0,1325458800000,1325458800000,Christos,synnefo,vmtime,synnefo/vmtime/VM.1,1.0,1.0,Map())
- [DEBUG] 2012-02-13 12:54:53,736 main - doFullMonthlyBilling(2012-01) END
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() BEGIN
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF()
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() +++ [Events by OccurredMillis] +++
+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 - …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 - …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 - …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),AgreementsSnapshot(List(AgreementSnapshot(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),AgreementsSnapshot(List(AgreementSnapshot(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),AgreementsSnapshot(List(AgreementSnapshot(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 - …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),AgreementsSnapshot(List(AgreementSnapshot(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),AgreementsSnapshot(List(AgreementSnapshot(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 - …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),AgreementsSnapshot(List(AgreementSnapshot(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),AgreementsSnapshot(List(AgreementSnapshot(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 - 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
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() changeReason = MonthlyBillingCalculation(2012-01)
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() implicitlyIssued [#=0] = List()
+DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() latestResourceEvents [#=1]:
+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:20 g.g.a.user.UserStateComputationsTest - testOrphanOFF() newWalletEntries [#=0] = List()
* }}}
*
* @author Christos KK Loverdos <loverdos@gmail.com>
import gr.grnet.aquarium.util.date.MutableDateCalc
import gr.grnet.aquarium.logic.accounting.dsl._
import gr.grnet.aquarium.logic.accounting.{Policy, Accounting}
-import gr.grnet.aquarium.util.{Loggable, ContextualLogger, justForSure}
+import gr.grnet.aquarium.util.{Loggable, ContextualLogger}
import gr.grnet.aquarium.simulation._
import gr.grnet.aquarium.uid.{UIDGenerator, ConcurrentVMLocalUIDGenerator}
-import com.ckkloverdos.maybe.{Maybe, Just, NoVal}
+import com.ckkloverdos.maybe.{Maybe, Just}
import org.junit.{Assert, Ignore, Test}
-import gr.grnet.aquarium.logic.accounting.algorithm.{ExecutableCostPolicyAlgorithm, CostPolicyAlgorithmCompiler, SimpleCostPolicyAlgorithmCompiler}
+import gr.grnet.aquarium.logic.accounting.algorithm.{ExecutableCostPolicyAlgorithm, CostPolicyAlgorithmCompiler}
import gr.grnet.aquarium.{AquariumException, Configurator}
+import gr.grnet.aquarium.computation.{UserState, BillingMonthInfo, UserStateComputations}
+import gr.grnet.aquarium.computation.reason.MonthlyBillingCalculation
/**
val UserCKKL = Aquarium.newUser("CKKL", UserCreationDate)
- val InitialUserState = Computations.createInitialUserState(
+ val InitialUserState = UserState.createInitialUserState(
userID = UserCKKL.userId,
userCreationMillis = UserCreationDate.getTime,
isActive = true,