2 * Copyright 2011-2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and
31 * documentation are those of the authors and should not be
32 * interpreted as representing official policies, either expressed
33 * or implied, of GRNET S.A.
36 package gr.grnet.aquarium.charging.state
38 import scala.collection.mutable
39 import gr.grnet.aquarium.computation.state.UserStateBootstrap
40 import gr.grnet.aquarium.policy.{ResourceType, UserAgreementModel}
41 import gr.grnet.aquarium.computation.BillingMonthInfo
42 import gr.grnet.aquarium.event.model.resource.ResourceEventModel
43 import gr.grnet.aquarium.computation.state.parts.{ResourceInstanceAmount, AgreementHistory}
44 import gr.grnet.aquarium.charging.wallet.WalletEntry
45 import gr.grnet.aquarium.charging.reason.{InitialUserStateSetup, ChargingReason}
46 import gr.grnet.aquarium.AquariumInternalError
50 * @author Christos KK Loverdos <loverdos@gmail.com>
53 final case class StdUserState(
55 parentIDInStore: Option[String],
59 theFullBillingMonth: Option[BillingMonthInfo],
60 chargingReason: ChargingReason,
61 previousResourceEvents: List[ResourceEventModel],
62 implicitlyIssuedStartEvents: List[ResourceEventModel],
63 accumulatingAmountOfResourceInstance: Map[String, Double],
64 chargingDataOfResourceInstance: Map[String, Map[String, Any]],
65 billingPeriodOutOfSyncResourceEventsCounter: Long,
66 agreementHistory: AgreementHistory,
67 walletEntries: List[WalletEntry]
68 ) extends UserStateModel {
70 def newWithChargingReason(newChargingReason: ChargingReason): UserStateModel = {
71 this.copy(chargingReason = newChargingReason)
74 private[this] def mutableMap[Vin, Vout](
75 inputMap: Map[String, Vin],
77 ): mutable.Map[(String, String), Vout] = {
79 (resourceAndInstanceID, vIn) ← inputMap.toSeq
81 StdUserState.resourceAndInstanceIDOfString(resourceAndInstanceID) -> vInOut(vIn)
84 mutable.Map(items: _*)
87 private[this] def mutableAccumulatingAmountMap: mutable.Map[(String, String), Double] = {
88 mutableMap(accumulatingAmountOfResourceInstance, identity[Double])
91 private[this] def mutableChargingDataMap: mutable.Map[(String, String), mutable.Map[String, Any]] = {
92 mutableMap(chargingDataOfResourceInstance, (vIn: Map[String, Any]) ⇒ mutable.Map(vIn.toSeq: _*))
95 private[this] def mutableImplicitlyIssuedStartMap: mutable.Map[(String, String), ResourceEventModel] = {
96 mutable.Map(implicitlyIssuedStartEvents.map(rem ⇒ (rem.safeResource, rem.safeInstanceID) -> rem): _*)
99 private[this] def mutablePreviousEventsMap: mutable.Map[(String, String), ResourceEventModel] = {
100 mutable.Map(previousResourceEvents.map(rem ⇒ (rem.safeResource, rem.safeInstanceID) -> rem): _*)
103 private[this] def mutableWalletEntries = {
104 val buffer = new mutable.ListBuffer[WalletEntry]
105 buffer ++= this.walletEntries
109 private[this] def mutableAgreementHistory = {
110 this.agreementHistory.toWorkingAgreementHistory
113 def toWorkingUserState(resourceTypesMap: Map[String, ResourceType]): WorkingUserState = {
114 new WorkingUserState(
116 this.parentIDInStore,
119 mutablePreviousEventsMap,
120 mutableImplicitlyIssuedStartMap,
121 mutableAccumulatingAmountMap,
122 mutableChargingDataMap,
124 mutableAgreementHistory,
126 this.billingPeriodOutOfSyncResourceEventsCounter,
132 final object StdUserState {
133 final val ResourceInstanceSeparator = "<:/:>"
134 final val ResourceInstanceSeparatorLength = ResourceInstanceSeparator.length
136 final def stringOfResourceAndInstanceID(resource: String, instanceID: String): String = {
137 def check(key: String, value: String) = {
138 if(value.indexOf(ResourceInstanceSeparator) != -1) {
139 throw new AquariumInternalError(
140 "The resource/instanceID separator '%s' is part of the %s '%s'".format(
141 ResourceInstanceSeparator, key, value
146 check("resource type", resource)
147 check("resource instance ID", instanceID)
149 resource + ResourceInstanceSeparator + instanceID
152 final def resourceAndInstanceIDOfString(resourceAndInstanceID: String): (String, String) = {
153 val index = resourceAndInstanceID.indexOf(ResourceInstanceSeparator)
154 val resource = resourceAndInstanceID.substring(0, index)
155 val instanceID = resourceAndInstanceID.substring(index + ResourceInstanceSeparatorLength)
157 (resource, instanceID)
160 def createInitialUserState(
162 userCreationMillis: Long,
163 occurredMillis: Long,
164 totalCredits: Double,
165 initialAgreement: UserAgreementModel,
166 chargingReason: ChargingReason = InitialUserStateSetup(None)
182 AgreementHistory.initial(initialAgreement),
187 def createInitialUserStateFromBootstrap(
188 usb: UserStateBootstrap,
189 occurredMillis: Long,
190 chargingReason: ChargingReason
193 createInitialUserState(
195 usb.userCreationMillis,
198 usb.initialAgreement,