2 * Copyright 2011 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.logic.events
38 import gr.grnet.aquarium.util.TimeHelpers.nowMillis
39 import gr.grnet.aquarium.user._
43 * This is an object representation for a resource name, which provides convenient querying methods.
45 * Also, a `ResourceType` knows how to compute a state change from a particular `ResourceEvent`.
47 * @author Christos KK Loverdos <loverdos@gmail.com>
49 sealed abstract class ResourceType(_name: String) {
50 def resourceName = _name
53 * Return true if the resource type must lead to wallet entries generation and, thus, credit diffs.
55 * Normally, this should always be the case.
57 def isBillableType = true
60 * A resource type is independent if it can, by itself only, create a wallet entry.
62 * It is dependent if it needs one or more other events of he same type to
64 def isIndependentType = true
66 def isKnownType = true
67 def isDiskSpace = false
69 def isBandwidthUpload = false
70 def isBandwidthDownload = false
73 * Calculates the new `UserState` based on the provided resource event, the calculated wallet entries
74 * and the current `UserState`.
76 * This method is an implementation detail and is not exposed. The actual user-level API is provided in `ResourceEvent`.
79 def calcStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState): UserState = {
80 val otherState = calcOtherStateChange(resourceEvent, walletEntries, userState)
81 val newCredits = calcNewCreditSnapshot(walletEntries, userState)
82 otherState.copy(credits = newCredits)
86 def calcOtherStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState): UserState
89 def calcNewCreditSnapshot(walletEntries: List[WalletEntry], userState: UserState): CreditSnapshot = {
90 val newCredits = for {
91 walletEntry <- walletEntries if(walletEntry.finalized)
92 } yield walletEntry.value.toDouble
94 val newCreditSum = newCredits.sum
95 val now = System.currentTimeMillis()
97 CreditSnapshot(userState.credits.data + newCreditSum, now)
102 * Companion object used to parse a resource name and provide an object representation in the form
103 * of a `ResourceType`.
105 * Known resource names, which represent Aquarium resources, are like "bndup", "vmtime" etc. and they are all
106 * defined in `ResourceNames`.
108 * @author Christos KK Loverdos <loverdos@gmail.com>
110 object ResourceType {
111 def fromName(name: String): ResourceType = {
113 case ResourceNames.bnddown ⇒ BandwidthDown
114 case ResourceNames.bndup ⇒ BandwidthUp
115 case ResourceNames.vmtime ⇒ VMTime
116 case _ ⇒ UnknownResourceType(name)
120 def fromResourceEvent(resourceEvent: ResourceEvent): ResourceType = fromName(resourceEvent.resource)
123 case object BandwidthDown extends ResourceType(ResourceNames.bnddown) {
124 override def isBandwidthDownload = true
127 def calcOtherStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState) = {
128 val oldBandwidthDownValue = userState.bandwidthDown.data
129 val bandwidthDownDiff = resourceEvent.value
131 val newBandwidth = BandwidthDownSnapshot(oldBandwidthDownValue + bandwidthDownDiff, nowMillis)
133 userState.copy(bandwidthDown = newBandwidth)
137 case object BandwidthUp extends ResourceType(ResourceNames.bndup) {
138 override def isBandwidthUpload = true
141 def calcOtherStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState) = {
142 val oldBandwidthUpValue = userState.bandwidthUp.data
143 val bandwidthUpDiff = resourceEvent.value
145 val newBandwidth = BandwidthUpSnapshot(oldBandwidthUpValue + bandwidthUpDiff, nowMillis)
147 userState.copy(bandwidthUp = newBandwidth)
151 case object VMTime extends ResourceType(ResourceNames.vmtime) {
152 override def isVMTime = true
154 override def isIndependentType = false
156 def isVMTimeOn(eventDetails: ResourceEvent.Details) = eventDetails.get(ResourceEvent.JsonNames.action) match {
157 case Some("on") ⇒ true
158 case Some("up") ⇒ true
162 def isVMTimeOff(eventDetails: ResourceEvent.Details) = eventDetails.get(ResourceEvent.JsonNames.action) match {
163 case Some("off") ⇒ true
164 case Some("down") ⇒ true
168 private[events] def calcOtherStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState) = {
174 case object DiskSpace extends ResourceType(ResourceNames.dsksp) {
175 override def isDiskSpace = true
177 private[events] def calcOtherStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState) = {
178 val oldDiskSpaceValue = userState.diskSpace.data
179 val diskSpaceDiff = resourceEvent.value
180 val newDiskSpace = DiskSpaceSnapshot(oldDiskSpaceValue + diskSpaceDiff, nowMillis)
181 userState.copy(diskSpace = newDiskSpace)
185 case class UnknownResourceType(originalName: String) extends ResourceType(ResourceNames.unknown) {
186 override def isKnownType = false
189 calcOtherStateChange(resourceEvent: ResourceEvent, walletEntries: List[WalletEntry], userState: UserState) = userState