root / src / main / scala / gr / grnet / aquarium / logic / events / ResourceEvent.scala @ 8621646b
History | View | Annotate | Download (11.2 kB)
1 |
/* |
---|---|
2 |
* Copyright 2011 GRNET S.A. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or |
5 |
* without modification, are permitted provided that the following |
6 |
* conditions are met: |
7 |
* |
8 |
* 1. Redistributions of source code must retain the above |
9 |
* copyright notice, this list of conditions and the following |
10 |
* disclaimer. |
11 |
* |
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. |
16 |
* |
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. |
29 |
* |
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. |
34 |
*/ |
35 |
|
36 |
package gr.grnet.aquarium.logic.events |
37 |
|
38 |
import net.liftweb.json.{JsonAST, Xml} |
39 |
import gr.grnet.aquarium.util.json.JsonHelpers |
40 |
import gr.grnet.aquarium.logic.accounting.dsl._ |
41 |
import com.ckkloverdos.maybe.{MaybeOption, Maybe} |
42 |
import java.util.Date |
43 |
import gr.grnet.aquarium.util.date.MutableDateCalc |
44 |
|
45 |
/** |
46 |
* Event sent to Aquarium by clients for resource accounting. |
47 |
* |
48 |
* @author Christos KK Loverdos <loverdos@gmail.com>. |
49 |
* @author Georgios Gousios <gousiosg@gmail.com>. |
50 |
*/ |
51 |
case class ResourceEvent( |
52 |
override val id: String, // The id at the client side (the sender) TODO: Rename to remoteId or something... |
53 |
override val occurredMillis: Long, // When it occurred at client side (the sender) |
54 |
override val receivedMillis: Long, // When it was received by Aquarium |
55 |
userId: String, // The user for which this resource is relevant |
56 |
clientId: String, // The unique client identifier (usually some hash) |
57 |
resource: String, // String representation of the resource type (e.g. "bndup", "vmtime"). |
58 |
instanceId: String, // String representation of the resource instance id |
59 |
eventVersion: String, |
60 |
value: Double, |
61 |
details: ResourceEvent.Details) |
62 |
extends AquariumEvent(id, occurredMillis, receivedMillis) { |
63 |
|
64 |
def validate() : Boolean = { |
65 |
!safeResource.isEmpty |
66 |
} |
67 |
|
68 |
def safeResource = if(resource eq null) "" else resource |
69 |
def safeInstanceId = if(instanceId eq null) "" else instanceId |
70 |
|
71 |
def hasResource = !safeResource.isEmpty |
72 |
def hasInstanceId = !safeInstanceId.isEmpty |
73 |
|
74 |
def fullResourceInfo = (safeResource, safeInstanceId) |
75 |
|
76 |
def occurredDate = new Date(occurredMillis) |
77 |
|
78 |
def occurredDeltaFrom(that: ResourceEvent): Long = { |
79 |
this.occurredMillis - that.occurredMillis |
80 |
} |
81 |
|
82 |
def isOccurredWithinMillis(fromMillis: Long, toMillis: Long): Boolean = { |
83 |
require(fromMillis <= toMillis, "fromMillis <= toMillis") |
84 |
fromMillis <= occurredMillis && occurredMillis <= toMillis |
85 |
} |
86 |
|
87 |
def isOccurredWithinDates(fromDate: Date, toDate: Date): Boolean = { |
88 |
isOccurredWithinMillis(fromDate.getTime, toDate.getTime) |
89 |
} |
90 |
|
91 |
def isReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = { |
92 |
require(fromMillis <= toMillis, "fromMillis <= toMillis") |
93 |
fromMillis <= receivedMillis && receivedMillis <= toMillis |
94 |
} |
95 |
|
96 |
def isReceivedWithinDates(fromDate: Date, toDate: Date): Boolean = { |
97 |
isReceivedWithinMillis(fromDate.getTime, toDate.getTime) |
98 |
} |
99 |
|
100 |
def isReceivedWithinDateCalcs(fromDate: MutableDateCalc, toDate: MutableDateCalc): Boolean = { |
101 |
isReceivedWithinMillis(fromDate.getMillis, toDate.getMillis) |
102 |
} |
103 |
|
104 |
def isOccurredOrReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = { |
105 |
isOccurredWithinMillis(fromMillis, toMillis) || |
106 |
isReceivedWithinMillis(fromMillis, toMillis) |
107 |
} |
108 |
|
109 |
def isOccurredOrReceivedWithinDates(fromDate: Date, toDate: Date): Boolean = { |
110 |
isOccurredWithinDates(fromDate, toDate) || |
111 |
isReceivedWithinDates(fromDate, toDate) |
112 |
} |
113 |
|
114 |
def isOutOfSyncForBillingMonth(yearOfBillingMonth: Int, billingMonth: Int) = { |
115 |
val billingStartDateCalc = new MutableDateCalc(yearOfBillingMonth, billingMonth) |
116 |
val billingStartMillis = billingStartDateCalc.toMillis |
117 |
// NOTE: no need to `copy` the mutable `billingStartDateCalc` here because we use it once |
118 |
val billingStopMillis = billingStartDateCalc.goEndOfThisMonth.toMillis |
119 |
|
120 |
isOutOfSyncForBillingPeriod(billingStartMillis, billingStopMillis) |
121 |
} |
122 |
|
123 |
def isOutOfSyncForBillingPeriod(billingStartMillis: Long, billingStopMillis: Long): Boolean = { |
124 |
isReceivedWithinMillis(billingStartMillis, billingStopMillis) && |
125 |
(occurredMillis < billingStartMillis || occurredMillis > billingStopMillis) |
126 |
} |
127 |
|
128 |
def toDebugString(resourcesMap: DSLResourcesMap, useOnlyInstanceId: Boolean = false): String = { |
129 |
val instanceInfo = if(useOnlyInstanceId) instanceId else "%s::%s".format(resource, instanceId) |
130 |
val bvalue = beautifyValue(resourcesMap) |
131 |
val occurredFormatted = new MutableDateCalc(occurredMillis).toYYYYMMDDHHMMSS |
132 |
if(occurredMillis == receivedMillis) { |
133 |
"EVENT(%s, [%s], %s, %s, %s, %s, %s)".format( |
134 |
id, |
135 |
occurredFormatted, |
136 |
bvalue, |
137 |
instanceInfo, |
138 |
details, |
139 |
userId, |
140 |
clientId |
141 |
) |
142 |
} else { |
143 |
"EVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format( |
144 |
id, |
145 |
occurredFormatted, |
146 |
new MutableDateCalc(receivedMillis), |
147 |
bvalue, |
148 |
instanceInfo, |
149 |
details, |
150 |
userId, |
151 |
clientId |
152 |
) |
153 |
} |
154 |
} |
155 |
|
156 |
private[this] |
157 |
def beatifyValue(resourceProvider: (String) ⇒ Option[DSLResource]): String = { |
158 |
resourceProvider(this.resource) match { |
159 |
case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒ |
160 |
OnOffPolicyResourceState(this.value).state.toUpperCase |
161 |
case Some(rc @ DSLComplexResource(_, _, _, _)) ⇒ |
162 |
"%s [%s]".format(value, rc.unit) |
163 |
case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒ |
164 |
OnOffPolicyResourceState(this.value).state.toUpperCase |
165 |
case Some(rc @ DSLSimpleResource(_, _, _)) ⇒ |
166 |
"%s [%s]".format(value, rc.unit) |
167 |
case _ ⇒ |
168 |
value.toString |
169 |
} |
170 |
} |
171 |
|
172 |
/** |
173 |
* Returns a beautiful string representation of the value. |
174 |
* |
175 |
* @param policy The policy to be asked for resources. |
176 |
* @return A beautiful string representation of the value. |
177 |
*/ |
178 |
def beautifyValue(policy: DSLPolicy): String = { |
179 |
beatifyValue(policy.findResource) |
180 |
} |
181 |
|
182 |
/** |
183 |
* Returns a beautiful string representation of the value. |
184 |
* |
185 |
* @param resourcesMap The resources map to be asked for resources. |
186 |
* @return A beautiful string representation of the value. |
187 |
*/ |
188 |
def beautifyValue(resourcesMap: DSLResourcesMap): String = { |
189 |
beatifyValue(resourcesMap.findResourceOoooooooooooold) |
190 |
} |
191 |
|
192 |
/** |
193 |
* Return `true` iff this is an event regarding a resource with an |
194 |
* [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]]. |
195 |
*/ |
196 |
def isOnOffEvent(policy: DSLPolicy): Boolean = { |
197 |
policy.findResource(this.resource).map(_.costPolicy) match { |
198 |
case Some(OnOffCostPolicy) ⇒ true |
199 |
case _ ⇒ false |
200 |
} |
201 |
} |
202 |
|
203 |
/** |
204 |
* Return `true` iff this is an event regarding a resource with an |
205 |
* [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]] and a |
206 |
* `value` of `"on"`. |
207 |
*/ |
208 |
def isOnEvent(policy: DSLPolicy): Boolean = { |
209 |
policy.findResource(this.resource) match { |
210 |
case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒ |
211 |
OnOffPolicyResourceState(this.value).isOn |
212 |
case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒ |
213 |
OnOffPolicyResourceState(this.value).isOn |
214 |
case _ ⇒ |
215 |
false |
216 |
} |
217 |
} |
218 |
|
219 |
/** |
220 |
* Return `true` iff this is an event regarding a resource with an |
221 |
* [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]] and a |
222 |
* `value` of `"off"`. |
223 |
*/ |
224 |
def isOffEvent(policy: DSLPolicy): Boolean = { |
225 |
policy.findResource(this.resource) match { |
226 |
case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒ |
227 |
OnOffPolicyResourceState(this.value).isOff |
228 |
case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒ |
229 |
OnOffPolicyResourceState(this.value).isOff |
230 |
case _ ⇒ |
231 |
false |
232 |
} |
233 |
} |
234 |
|
235 |
def copyWithReceivedMillis(millis: Long) = copy(receivedMillis = millis) |
236 |
|
237 |
/** |
238 |
* Find the cost policy of the resource named in this resource event. |
239 |
* |
240 |
* We do not expect cost policies for resources to change, because they are supposed |
241 |
* to be one of their constant characteristics. That is why do not issue a time-dependent |
242 |
* query here for the event's current policy. |
243 |
* |
244 |
* Should the need arises to change the cost policy for a resource, this is a good enough |
245 |
* reason to consider creating another type of resource. |
246 |
*/ |
247 |
def findCostPolicyM(defaultPolicy: DSLPolicy): Maybe[DSLCostPolicy] = { |
248 |
defaultPolicy.findResource(this.safeResource).map(_.costPolicy): Maybe[DSLCostPolicy] |
249 |
} |
250 |
|
251 |
/** |
252 |
* Find the cost policy of the resource named in this resource event. |
253 |
* |
254 |
* We do not expect cost policies for resources to change, because they are supposed |
255 |
* to be one of their constant characteristics. That is why do not issue a time-dependent |
256 |
* query here for the event's current policy. |
257 |
* |
258 |
* Should the need arises to change the cost policy for a resource, this is a good enough |
259 |
* reason to consider creating another type of resource. |
260 |
*/ |
261 |
def findCostPolicyM(resourcesMap: DSLResourcesMap): Maybe[DSLCostPolicy] = { |
262 |
for { |
263 |
rc <- resourcesMap.findResourceM(this.safeResource) |
264 |
} yield { |
265 |
rc.costPolicy |
266 |
} |
267 |
} |
268 |
} |
269 |
|
270 |
object ResourceEvent { |
271 |
type Details = Map[String, String] |
272 |
|
273 |
type ResourceType = String |
274 |
type ResourceIdType = String |
275 |
type FullResourceType = (ResourceType, ResourceIdType) |
276 |
type FullResourceTypeMap = Map[FullResourceType, ResourceEvent] |
277 |
type FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent] |
278 |
|
279 |
def fromJson(json: String): ResourceEvent = { |
280 |
JsonHelpers.jsonToObject[ResourceEvent](json) |
281 |
} |
282 |
|
283 |
def fromJValue(jsonAST: JsonAST.JValue): ResourceEvent = { |
284 |
JsonHelpers.jValueToObject[ResourceEvent](jsonAST) |
285 |
} |
286 |
|
287 |
def fromBytes(bytes: Array[Byte]): ResourceEvent = { |
288 |
JsonHelpers.jsonBytesToObject[ResourceEvent](bytes) |
289 |
} |
290 |
|
291 |
def fromXml(xml: String): ResourceEvent = { |
292 |
fromJValue(Xml.toJson(scala.xml.XML.loadString(xml))) |
293 |
} |
294 |
|
295 |
object JsonNames { |
296 |
final val _id = "_id" |
297 |
final val id = "id" |
298 |
final val userId = "userId" |
299 |
final val occurredMillis = "occurredMillis" |
300 |
final val receivedMillis = "receivedMillis" |
301 |
final val clientId = "clientId" |
302 |
final val resource = "resource" |
303 |
final val resourceId = "resourceId" |
304 |
final val eventVersion = "eventVersion" |
305 |
final val value = "value" |
306 |
final val details = "details" |
307 |
} |
308 |
} |