import gr.grnet.aquarium.ResourceLocator._
import gr.grnet.aquarium.computation.UserStateComputations
import gr.grnet.aquarium.logic.accounting.algorithm.{SimpleCostPolicyAlgorithmCompiler, CostPolicyAlgorithmCompiler}
+import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap
+import gr.grnet.aquarium.logic.accounting.Policy
/**
* This is the Aquarium entry point.
}
def storeProvider = _storeProvider
+
+ def currentResourcesMap: DSLResourcesMap = {
+ // FIXME: Get rid of this singleton stuff
+ Policy.policy.resourcesMap
+ }
+
+ def initialAgreementForRole(role: String, referenceTimeMillis: Long): String = {
+ // FIXME: Where is the mapping?
+ "default"
+ }
+
+ def initialBalanceForRole(role: String, referenceTimeMillis: Long): Double = {
+ // FIXME: Where is the mapping?
+ 10000.0
+ }
def withStoreProviderClass[C <: StoreProvider](spc: Class[C]): Aquarium = {
val map = this.props.map
case class GetUserBalanceRequest(userID: String, timestamp: Long)
extends ActorMessage
with RouterRequestMessage
- with UserActorRequestMessage
+ with UserActorRequestMessage {
+
+ def referenceTimeMillis = timestamp
+}
*/
case class GetUserBalanceResponse(
- userID: String,
- balance: Either[String, Double])
-extends RouterResponseMessage(balance)
+ balance: Either[String, GetUserBalanceResponseData],
+ override val suggestedHTTPStatus: Int = 200)
+extends RouterResponseMessage(balance, suggestedHTTPStatus)
+
+case class GetUserBalanceResponseData(userID: String, balance: Double)
case class GetUserStateRequest(userID: String, timestamp: Long)
extends ActorMessage
with RouterRequestMessage
- with UserActorRequestMessage
+ with UserActorRequestMessage {
+
+ def referenceTimeMillis = timestamp
+}
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-abstract class RouterResponseMessage[T](val response: Either[String, T]) extends ActorMessage {
+abstract class RouterResponseMessage[T](
+ val response: Either[String, T],
+ val suggestedHTTPStatus: Int = 200)
+extends ActorMessage {
+
def isError = response.isRight
def responseToJsonString: String = {
- response match {
- case Left(error) ⇒
- "{}"
-
- case Right(data) ⇒
- StdConverters.AllConverters.convertEx[PrettyJsonTextFormat](data).value
- }
+ response.fold(
+ _ ⇒ "", // No JSON response on error
+ data ⇒ StdConverters.AllConverters.convertEx[PrettyJsonTextFormat](data).value
+ )
}
}
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-trait UserActorRequestMessage extends ActorMessage
+trait UserActorRequestMessage extends ActorMessage {
+ def referenceTimeMillis: Long
+}
package gr.grnet.aquarium.actor.message.config
-import gr.grnet.aquarium.actor.message.{UserActorRequestMessage, ActorMessage}
+import gr.grnet.aquarium.actor.message.ActorMessage
+import gr.grnet.aquarium.util.shortClassNameOf
+import gr.grnet.aquarium.util.date.MutableDateCalc
/**
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-case class InitializeUserState(userID: String) extends ActorMessage with ActorConfigurationMessage
+case class InitializeUserState(userID: String, referenceTimeMillis: Long)
+extends ActorMessage
+ with ActorConfigurationMessage {
+
+ override def toString = {
+ "%s(%s, %s)".format(shortClassNameOf(this), userID, new MutableDateCalc(referenceTimeMillis).toString)
+ }
+}
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-case class ProcessIMEvent(imEvent: IMEventModel) extends ActorMessage with UserActorRequestMessage
+case class ProcessIMEvent(imEvent: IMEventModel) extends ActorMessage with UserActorRequestMessage {
+ def referenceTimeMillis = imEvent.occurredMillis
+}
*/
case class ProcessResourceEvent(rcEvent: ResourceEventModel)
extends ActorMessage
- with UserActorRequestMessage
+ with UserActorRequestMessage {
+
+ def referenceTimeMillis = rcEvent.occurredMillis
+}
private[this]
def callRouter(message: RouterRequestMessage, responder: RequestResponder): Unit = {
- val configurator = Aquarium.Instance
- val actorProvider = configurator.actorProvider
+ val aquarium = Aquarium.Instance
+ val actorProvider = aquarium.actorProvider
val router = actorProvider.actorForRole(RouterRole)
val futureResponse = router ask message
future ⇒
future.value match {
case None ⇒
- // TODO: Will this ever happen??
+ // TODO: Will this ever happen??
+ logger.warn("Future did not complete for %s".format(message))
+ responder.complete(stringResponse(500, "Internal Server Error", "text/plain"))
case Some(Left(error)) ⇒
logger.error("Error serving %s: %s".format(message, error))
actualResponse match {
case routerResponse: RouterResponseMessage[_] ⇒
routerResponse.response match {
- case Left(error) ⇒
- logger.error("Error %s serving %s: Response is: %s".format(error, message, actualResponse))
- responder.complete(stringResponse(500, "Internal Server Error", "text/plain"))
+ case Left(errorMessage) ⇒
+ logger.error("Error '%s' serving %s. Response is: %s".format(errorMessage, message, actualResponse))
+ responder.complete(stringResponse(routerResponse.suggestedHTTPStatus, errorMessage, "text/plain"))
case Right(response) ⇒
responder.complete(
HttpResponse(
- status = 200,
+ routerResponse.suggestedHTTPStatus,
body = routerResponse.responseToJsonString.getBytes("UTF-8"),
headers = HttpHeader("Content-type", "application/json;charset=utf-8") :: Nil))
}
def role = RouterRole
- private[this] def _launchUserActor(userID: String): ActorRef = {
+ private[this] def _launchUserActor(userID: String, referenceTimeMillis: Long): ActorRef = {
// create a fresh instance
val userActor = _actorProvider.actorForRole(UserActorRole)
UserActorCache.put(userID, userActor)
- userActor ! InitializeUserState(userID)
+ userActor ! InitializeUserState(userID, referenceTimeMillis)
userActor
}
- private[this] def _findOrCreateUserActor(userID: String): ActorRef = {
+ private[this] def _findOrCreateUserActor(userID: String, referenceTimeMillis: Long): ActorRef = {
UserActorCache.get(userID) match {
case Some(userActorRef) ⇒
userActorRef
case None ⇒
- _launchUserActor(userID)
+ _launchUserActor(userID, referenceTimeMillis)
}
}
private[this] def _forwardToUserActor(userID: String, m: UserActorRequestMessage): Unit = {
- _findOrCreateUserActor(userID) forward m
+ _findOrCreateUserActor(userID, m.referenceTimeMillis) forward m
}
import gr.grnet.aquarium.computation.data.IMStateSnapshot
import gr.grnet.aquarium.event.model.im.IMEventModel
import gr.grnet.aquarium.actor.message.config.{InitializeUserState, ActorProviderConfigured, AquariumPropertiesLoaded}
-import gr.grnet.aquarium.computation.NewUserState
-import gr.grnet.aquarium.actor.message.{GetUserBalanceResponse, GetUserStateRequest, GetUserBalanceRequest}
+import gr.grnet.aquarium.actor.message.{GetUserBalanceResponseData, GetUserBalanceResponse, GetUserStateRequest, GetUserBalanceRequest}
+import gr.grnet.aquarium.computation.{BillingMonthInfo, UserStateBootstrappingData, UserState}
+import gr.grnet.aquarium.util.date.TimeHelpers
+import gr.grnet.aquarium.logic.accounting.Policy
+import gr.grnet.aquarium.computation.reason.InitialUserStateSetup
/**
*
class UserActor extends ReflectiveRoleableActor {
private[this] var _userID: String = "<?>"
private[this] var _imState: IMStateSnapshot = _
-// private[this] var _userState: UserState = _
- private[this] var _newUserState: NewUserState = _
+ private[this] var _userState: UserState = _
self.lifeCycle = Temporary
def role = UserActorRole
private[this] def aquarium: Aquarium = Aquarium.Instance
+ private[this] def userStateComputations = aquarium.userStateComputations
- private[this] def _timestampTheshold =
+ private[this] def _timestampTheshold = {
aquarium.props.getLong(Aquarium.Keys.user_state_timestamp_threshold).getOr(10000)
-
+ }
private[this] def _haveUserState = {
- this._newUserState ne null
+ this._userState ne null
}
private[this] def _haveIMState = {
def onActorProviderConfigured(event: ActorProviderConfigured): Unit = {
}
- private[this] def createIMState(userID: String): Unit = {
+ private[this] def createIMState(event: InitializeUserState): Unit = {
+ val userID = event.userID
val store = aquarium.imEventStore
// TODO: Optimization: Since IMState only records roles, we should incrementally
// TODO: built it only for those IMEvents that changed the role.
IMStateSnapshot.initial(imEvent)
case currentState ⇒
- currentState.copyWithEvent(imEvent)
+ currentState.updateHistoryWithEvent(imEvent)
}
this._imState = newState
DEBUG("Recomputed %s = %s", shortNameOfClass(classOf[IMStateSnapshot]), this._imState)
}
- private[this] def createUserState(userID: String): Unit = {
+ /**
+ * Resource events are processed only if the user has been activated.
+ */
+ private[this] def shouldProcessResourceEvents: Boolean = {
+ _haveIMState && this._imState.hasBeenActivated
+ }
+
+ private[this] def createUserState(event: InitializeUserState): Unit = {
+ val userID = event.userID
+ val referenceTime = event.referenceTimeMillis
+
+ if(!_haveIMState) {
+ // Should have been created from `createIMState()`
+ DEBUG("Cannot create user state from %s, since %s = %s", event, shortNameOfClass(classOf[IMStateSnapshot]), this._imState)
+ return
+ }
+
+ if(!this._imState.hasBeenActivated) {
+ // Cannot set the initial state!
+ DEBUG("Cannot create user state from %s, since user is inactive", event)
+ return
+ }
+
+ val userActivationMillis = this._imState.userActivationMillis.get
+ val initialRole = this._imState.roleHistory.firstRole.get.name
+
+ val userStateBootstrap = UserStateBootstrappingData(
+ this._userID,
+ userActivationMillis,
+ initialRole,
+ aquarium.initialAgreementForRole(initialRole, userActivationMillis),
+ aquarium.initialBalanceForRole(initialRole, userActivationMillis)
+ )
+
+ userStateComputations.doFullMonthlyBilling(
+ userStateBootstrap,
+ BillingMonthInfo.fromMillis(TimeHelpers.nowMillis()),
+ aquarium.currentResourcesMap,
+ InitialUserStateSetup,
+ None
+ )
}
def onInitializeUserState(event: InitializeUserState): Unit = {
this._userID = userID
DEBUG("Got %s", event)
- createIMState(userID)
- createUserState(userID)
- }
-
- private[this] def _getAgreementNameForNewUser(imEvent: IMEventModel): String = {
- // FIXME: Implement based on the role
- "default"
+ createIMState(event)
+ createUserState(event)
}
/**
// This happens when the actor is brought to life, then immediately initialized, and then
// sent the first IM event. But from the initialization procedure, this IM event will have
// already been loaded from DB!
- DEBUG("Ignoring first %s after birth", imEvent.toDebugString)
+ INFO("Ignoring first %s after birth", imEvent.toDebugString)
return
}
- this._imState = this._imState.copyWithEvent(imEvent)
+ this._imState = this._imState.updateHistoryWithEvent(imEvent)
- DEBUG("Update %s = %s", shortClassNameOf(this._imState), this._imState)
+ INFO("Update %s = %s", shortClassNameOf(this._imState), this._imState)
}
def onProcessResourceEvent(event: ProcessResourceEvent): Unit = {
val rcEvent = event.rcEvent
- if(!_haveIMState) {
+ if(!shouldProcessResourceEvents) {
// This means the user has not been activated. So, we do not process any resource event
- INFO("Not processing %s", rcEvent.toJsonString)
+ DEBUG("Not processing %s", rcEvent.toJsonString)
return
}
}
def onGetUserBalanceRequest(event: GetUserBalanceRequest): Unit = {
- val userId = event.userID
- // FIXME: Implement
-// self reply GetUserBalanceResponse(userId, Right(_userState.creditsSnapshot.creditAmount))
+ val userID = event.userID
+
+ if(!_haveIMState) {
+ // No IMEvent has arrived, so this user is virtually unknown
+ self reply GetUserBalanceResponse(Left("User not found"), 404/*Not found*/)
+ }
+ else if(!_haveUserState) {
+ // The user is known but we have no state.
+ // Ridiculous. Should have been created at least during initialization.
+ }
+
+ if(!_haveUserState) {
+ self reply GetUserBalanceResponse(Left("Not found"), 404/*Not found*/)
+ } else {
+ self reply GetUserBalanceResponse(Right(GetUserBalanceResponseData(userID, this._userState.totalCredits)))
+ }
}
def onGetUserStateRequest(event: GetUserStateRequest): Unit = {
)
}
+ def createInitialUserState(usb: UserStateBootstrappingData): UserState = {
+ createInitialUserState(
+ usb.userID,
+ usb.userCreationMillis,
+ usb.initialCredits,
+ usb.initialRole,
+ usb.initialAgreement
+ )
+ }
+
def createInitialUserStateFrom(us: UserState): UserState = {
createInitialUserState(
us.userID,
--- /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
+
+/**
+ * Used to bootstrao the user state.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+case class UserStateBootstrappingData(
+ userID: String,
+ userCreationMillis: Long,
+ initialRole: String,
+ initialAgreement: String,
+ initialCredits: Double
+)
protected lazy val userStateStore = storeProvider.userStateStore
protected lazy val resourceEventStore = storeProvider.resourceEventStore
- def findUserStateAtEndOfBillingMonth(userId: String,
+ def findUserStateAtEndOfBillingMonth(userStateBootstrap: UserStateBootstrappingData,
billingMonthInfo: BillingMonthInfo,
- currentUserState: UserState,
defaultResourcesMap: DSLResourcesMap,
calculationReason: UserStateChangeReason,
clogOpt: Option[ContextualLogger] = None): UserState = {
def doCompute: UserState = {
doFullMonthlyBilling(
- userId,
+ userStateBootstrap,
billingMonthInfo,
- currentUserState,
defaultResourcesMap,
calculationReason,
Some(clog))
}
- val userCreationMillis = currentUserState.userCreationMillis
+ val userID = userStateBootstrap.userID
+ val userCreationMillis = userStateBootstrap.userCreationMillis
val userCreationDateCalc = new MutableDateCalc(userCreationMillis)
val billingMonthStartMillis = billingMonthInfo.startMillis
val billingMonthStopMillis = billingMonthInfo.stopMillis
clog.debug("User did not exist before %s", userCreationDateCalc)
// NOTE: Reason here will be: InitialUserStateSetup$
- val initialUserState0 = UserState.createInitialUserStateFrom(currentUserState)
+ val initialUserState0 = UserState.createInitialUserState(userStateBootstrap)
val initialUserState1 = userStateStore.insertUserState(initialUserState0)
clog.debug("Returning INITIAL state [_id=%s] %s".format(initialUserState1._id, initialUserState1))
} else {
// Ask DB cache for the latest known user state for this billing period
val latestUserStateOpt = userStateStore.findLatestUserStateForEndOfBillingMonth(
- userId,
+ userID,
billingMonthInfo.year,
billingMonthInfo.month)
// For this reason, we must count the events again.
val latestStateOOSEventsCounter = latestUserState.billingPeriodOutOfSyncResourceEventsCounter
val actualOOSEventsCounter = resourceEventStore.countOutOfSyncEventsForBillingPeriod(
- userId,
+ userID,
billingMonthStartMillis,
billingMonthStopMillis)
}
//+ Utility methods
- def rcDebugInfo(rcEvent: ResourceEventModel) = {
+ protected def rcDebugInfo(rcEvent: ResourceEventModel) = {
rcEvent.toDebugString(false)
}
-
//- Utility methods
def processResourceEvent(startingUserState: UserState,
walletEntriesBuffer: mutable.Buffer[NewWalletEntry],
clogOpt: Option[ContextualLogger] = None): UserState = {
- val clog = ContextualLogger.fromOther(clogOpt, logger, "walletEntriesForResourceEvent(%s)", currentResourceEvent.id)
+ val clog = ContextualLogger.fromOther(clogOpt, logger, "processResourceEvent(%s)", currentResourceEvent.id)
var _workingUserState = startingUserState
}
- def doFullMonthlyBilling(userId: String,
+ def doFullMonthlyBilling(userStateBootstrap: UserStateBootstrappingData,
billingMonthInfo: BillingMonthInfo,
- currentUserState: UserState,
defaultResourcesMap: DSLResourcesMap,
calculationReason: UserStateChangeReason = NoSpecificChangeReason,
clogOpt: Option[ContextualLogger] = None): UserState = {
+ val userID = userStateBootstrap.userID
val clog = ContextualLogger.fromOther(
clogOpt,
val clogSome = Some(clog)
val previousBillingMonthUserState = findUserStateAtEndOfBillingMonth(
- userId,
+ userStateBootstrap,
billingMonthInfo.previousMonth,
- currentUserState,
defaultResourcesMap,
calculationReason.forPreviousBillingMonth,
clogSome
// First, find and process the actual resource events from DB
val allResourceEventsForMonth = resourceEventStore.findAllRelevantResourceEventsForBillingPeriod(
- userId,
+ userID,
billingMonthStartMillis,
billingMonthEndMillis)
*
* @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
*/
- def findAndRemoveGeneratorsOfImplicitEndEvents(newOccuredMillis: Long
- ): (List[ResourceEventModel], List[ResourceEventModel]) = {
+ def findAndRemoveGeneratorsOfImplicitEndEvents(
+ newOccuredMillis: Long
+ ): (List[ResourceEventModel], List[ResourceEventModel]) = {
+
val buffer = mutable.ListBuffer[(ResourceEventModel, ResourceEventModel)]()
val checkSet = mutable.Set[ResourceEventModel]()
package gr.grnet.aquarium.computation.data
import gr.grnet.aquarium.event.model.im.IMEventModel
+import gr.grnet.aquarium.util.shortClassNameOf
+import gr.grnet.aquarium.util.date.MutableDateCalc
/**
*
*/
case class IMStateSnapshot(
- /**
- * True if the user has ever been activated even once.
- */
- hasBeenActivated: Boolean,
/**
* This is the latest processed IMEvent
*/
latestIMEvent: IMEventModel,
/**
+ * The earliest activation time, if it exists.
+ */
+ userActivationMillis: Option[Long],
+
+ /**
* This is the recorded role history
*/
roleHistory: RoleHistory) {
- def copyWithEvent(imEvent: IMEventModel) = {
+ /**
+ * True iff the user has ever been activated even once.
+ */
+ def hasBeenActivated: Boolean = {
+ userActivationMillis.isDefined
+ }
+
+ def updateHistoryWithEvent(imEvent: IMEventModel) = {
copy(
- hasBeenActivated = this.hasBeenActivated || imEvent.isActive,
- latestIMEvent = imEvent,
- roleHistory = this.roleHistory.copyWithRole(imEvent.role, imEvent.occurredMillis)
+ userActivationMillis = if(imEvent.isStateActive) Some(imEvent.occurredMillis) else this.userActivationMillis,
+ latestIMEvent = imEvent,
+ roleHistory = this.roleHistory.updateWithRole(imEvent.role, imEvent.occurredMillis)
+ )
+ }
+
+ override def toString = {
+ "%s(\n!! %s\n!! %s\n!! %s)".format(
+ shortClassNameOf(this),
+ latestIMEvent,
+ userActivationMillis.map(new MutableDateCalc(_)),
+ roleHistory
)
}
}
object IMStateSnapshot {
def initial(imEvent: IMEventModel): IMStateSnapshot = {
IMStateSnapshot(
- imEvent.isActive,
imEvent,
+ if(imEvent.isStateActive) Some(imEvent.occurredMillis) else None,
RoleHistory.initial(imEvent.role, imEvent.occurredMillis))
}
}
TreeMap(roles.map(role ⇒ (role.timeslot, role)): _*)
}
- def copyWithRole(role: String, validFrom: Long) = {
+ def updateWithRole(role: String, validFrom: Long) = {
+ def fixValidTo(validFrom: Long, validTo: Long): Long = {
+ if(validTo == validFrom) {
+ // Since validTo is exclusive, make at least 1ms gap
+ validFrom + 1
+ } else {
+ validTo
+ }
+ }
+
val newItems = roles match {
case Nil ⇒
RoleHistoryItem(role, validFrom) :: Nil
case head :: tail ⇒
- if(head.isStrictlyAfter(validFrom)) {
+ if(head.startsStrictlyAfter(validFrom)) {
// must search history items to find where this fits in
@tailrec
def check(allChecked: ListBuffer[RoleHistoryItem],
toCheck match {
case Nil ⇒
- allChecked.append(RoleHistoryItem(role, validFrom, lastCheck.validFrom))
+ allChecked.append(RoleHistoryItem(role, validFrom, fixValidTo(validFrom, lastCheck.validFrom)))
allChecked.toList
case toCheckHead :: toCheckTail ⇒
- if(toCheckHead.isStrictlyAfter(validFrom)) {
+ if(toCheckHead.startsStrictlyAfter(validFrom)) {
allChecked.append(toCheckHead)
check(allChecked, toCheckHead, toCheckTail)
} else {
- allChecked.append(RoleHistoryItem(role, validFrom, lastCheck.validFrom))
+ allChecked.append(RoleHistoryItem(role, validFrom, fixValidTo(validFrom, lastCheck.validFrom)))
allChecked.toList
}
}
check(buffer, head, tail)
} else {
// assume head.validTo goes to infinity,
- RoleHistoryItem(role, validFrom) :: head.copyWithValidTo(validFrom) :: tail
+ RoleHistoryItem(role, validFrom) :: head.copyWithValidTo(fixValidTo(head.validFrom, validFrom)) :: tail
}
}
validFrom <= time && time < validTo
}
- def isStrictlyAfter(time: Long) = {
+ def startsStrictlyAfter(time: Long) = {
validFrom > time
}
uidGen.nextUID(),
occurredMillis,
receivedMillis,
- owner.userId,
+ owner.userID,
client.clientId,
resource.name,
instanceId,
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-case class UserSim(userId: String, userCreationDate: Date, aquarium: AquariumSim) { userSelf ⇒
+case class UserSim(userID: String, userCreationDate: Date, aquarium: AquariumSim) { userSelf ⇒
private[this]
def resourceEventStore = aquarium.resourceEventStore
}
def myResourceEvents: List[ResourceEventModel] = {
- resourceEventStore.findResourceEventsByUserId(userId)(None)
+ resourceEventStore.findResourceEventsByUserId(userID)(None)
}
def myResourceEventsByReceivedDate: List[ResourceEventModel] = {
import com.mongodb.DBObject
import com.mongodb.util.JSON
import gr.grnet.aquarium.event.model.im.IMEventModel
+import gr.grnet.aquarium.util.date.MutableDateCalc
/**
def withDetails(newDetails: Map[String, String], newOccurredMillis: Long) =
this.copy(details = newDetails, occurredMillis = newOccurredMillis)
+
+ override def toString = {
+ "%s(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)".format(
+ shortClassNameOf(this),
+ id,
+ new MutableDateCalc(occurredMillis).toString,
+ new MutableDateCalc(receivedMillis).toString,
+ userID,
+ clientID,
+ isActive,
+ role,
+ eventVersion,
+ eventType,
+ details,
+ _id
+ )
+ }
}
object MongoDBIMEvent {
import gr.grnet.aquarium.logic.accounting.algorithm.{ExecutableCostPolicyAlgorithm, CostPolicyAlgorithmCompiler}
import gr.grnet.aquarium.{AquariumException}
import gr.grnet.aquarium.Aquarium.{Instance ⇒ AquariumInstance}
-import gr.grnet.aquarium.computation.{UserState, BillingMonthInfo, UserStateComputations}
import gr.grnet.aquarium.computation.reason.MonthlyBillingCalculation
+import gr.grnet.aquarium.computation.{UserStateBootstrappingData, UserState, BillingMonthInfo, UserStateComputations}
/**
val UserCKKL = Aquarium.newUser("CKKL", UserCreationDate)
- val InitialUserState = UserState.createInitialUserState(
- userID = UserCKKL.userId,
- userCreationMillis = UserCreationDate.getTime,
- totalCredits = 0.0,
+// val InitialUserState = UserState.createInitialUserState(
+// userID = UserCKKL.userID,
+// userCreationMillis = UserCreationDate.getTime,
+// totalCredits = 0.0,
+// initialRole = "default",
+// initialAgreement = DSLAgreement.DefaultAgreementName
+// )
+
+ val UserStateBootstrap = UserStateBootstrappingData(
+ userID = UserCKKL.userID,
+ userCreationMillis = UserCreationDate.getTime(),
initialRole = "default",
- initialAgreement = DSLAgreement.DefaultAgreementName
+ initialAgreement = DSLAgreement.DefaultAgreementName,
+ initialCredits = 0.0
)
// By convention
private[this]
def doFullMonthlyBilling(clog: ContextualLogger, billingMonthInfo: BillingMonthInfo) = {
Computations.doFullMonthlyBilling(
- UserCKKL.userId,
+ UserStateBootstrap,
billingMonthInfo,
- InitialUserState,
DefaultResourcesMap,
MonthlyBillingCalculation(billingMonthInfo),
Some(clog)