ab5abb30f8b6b62af436c2630499a4ba451cb54e
[aquarium] / src / main / scala / gr / grnet / aquarium / computation / UserStateWorker.scala
1 /*
2  * Copyright 2011-2012 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.computation
37
38 import scala.collection.mutable
39 import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap
40 import gr.grnet.aquarium.logic.accounting.Accounting
41 import gr.grnet.aquarium.event.resource.ResourceEventModel
42 import gr.grnet.aquarium.computation.data.{LatestResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, IgnoredFirstResourceEventsWorker}
43 import gr.grnet.aquarium.util.ContextualLogger
44
45 /**
46  * A helper object holding intermediate state/results during resource event processing.
47  *
48  * @param previousResourceEvents
49  * This is a collection of all the latest resource events.
50  * We want these in order to correlate incoming resource events with their previous (in `occurredMillis` time)
51  * ones. Will be updated on processing the next resource event.
52  *
53  * @param implicitlyIssuedStartEvents
54  * The implicitly issued resource events at the beginning of the billing period.
55  *
56  * @param ignoredFirstResourceEvents
57  * The resource events that were first (and unused) of their kind.
58  *
59  * @author Christos KK Loverdos <loverdos@gmail.com>
60  */
61 case class UserStateWorker(userID: String,
62                            previousResourceEvents: LatestResourceEventsWorker,
63                            implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker,
64                            ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker,
65                            accounting: Accounting,
66                            resourcesMap: DSLResourcesMap) {
67
68   /**
69    * Finds the previous resource event by checking two possible sources: a) The implicitly terminated resource
70    * events and b) the explicit previous resource events. If the event is found, it is removed from the
71    * respective source.
72    *
73    * If the event is not found, then this must be for a new resource instance.
74    * (and probably then some `zero` resource event must be implied as the previous one)
75    *
76    * @param resource
77    * @param instanceId
78    * @return
79    */
80   def findAndRemovePreviousResourceEvent(resource: String, instanceId: String): Option[ResourceEventModel] = {
81     // implicitly issued events are checked first
82     implicitlyIssuedStartEvents.findAndRemoveResourceEvent(resource, instanceId) match {
83       case some@Some(_) ⇒
84         some
85       case None ⇒
86         // explicit previous resource events are checked second
87         previousResourceEvents.findAndRemoveResourceEvent(resource, instanceId) match {
88           case some@Some(_) ⇒
89             some
90           case _ ⇒
91             None
92         }
93     }
94   }
95
96   def updateIgnored(resourceEvent: ResourceEventModel): Unit = {
97     ignoredFirstResourceEvents.updateResourceEvent(resourceEvent)
98   }
99
100   def updatePrevious(resourceEvent: ResourceEventModel): Unit = {
101     previousResourceEvents.updateResourceEvent(resourceEvent)
102   }
103
104   def debugTheMaps(clog: ContextualLogger)(rcDebugInfo: ResourceEventModel ⇒ String): Unit = {
105     if(previousResourceEvents.size > 0) {
106       val map = previousResourceEvents.latestEventsMap.map {
107         case (k, v) => (k, rcDebugInfo(v))
108       }
109       clog.debugMap("previousResourceEvents", map, 0)
110     }
111     if(implicitlyIssuedStartEvents.size > 0) {
112       val map = implicitlyIssuedStartEvents.implicitlyIssuedEventsMap.map {
113         case (k, v) => (k, rcDebugInfo(v))
114       }
115       clog.debugMap("implicitlyTerminatedResourceEvents", map, 0)
116     }
117     if(ignoredFirstResourceEvents.size > 0) {
118       val map = ignoredFirstResourceEvents.ignoredFirstEventsMap.map {
119         case (k, v) => (k, rcDebugInfo(v))
120       }
121       clog.debugMap("ignoredFirstResourceEvents", map, 0)
122     }
123   }
124
125   //  private[this]
126   //  def allPreviousAndAllImplicitlyStarted: List[ResourceEvent] = {
127   //    val buffer: FullMutableResourceTypeMap = scala.collection.mutable.Map[FullResourceType, ResourceEvent]()
128   //
129   //    buffer ++= implicitlyIssuedStartEvents.implicitlyIssuedEventsMap
130   //    buffer ++= previousResourceEvents.latestEventsMap
131   //
132   //    buffer.valuesIterator.toList
133   //  }
134
135   /**
136    * Find those events from `implicitlyIssuedStartEvents` and `previousResourceEvents` that will generate implicit
137    * end events along with those implicitly issued events. Before returning, remove the events that generated the
138    * implicit ends from the internal state of this instance.
139    *
140    * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]]
141    */
142   def findAndRemoveGeneratorsOfImplicitEndEvents(newOccuredMillis: Long
143                                                   ): (List[ResourceEventModel], List[ResourceEventModel]) = {
144     val buffer = mutable.ListBuffer[(ResourceEventModel, ResourceEventModel)]()
145     val checkSet = mutable.Set[ResourceEventModel]()
146
147     def doItFor(map: ResourceEventModel.FullMutableResourceTypeMap): Unit = {
148       val resourceEvents = map.valuesIterator
149       for {
150         resourceEvent ← resourceEvents
151         dslResource ← resourcesMap.findResource(resourceEvent.safeResource)
152         costPolicy = dslResource.costPolicy
153       } {
154         if(costPolicy.supportsImplicitEvents) {
155           if(costPolicy.mustConstructImplicitEndEventFor(resourceEvent)) {
156             val implicitEnd = costPolicy.constructImplicitEndEventFor(resourceEvent, newOccuredMillis)
157
158             if(!checkSet.contains(resourceEvent)) {
159               checkSet.add(resourceEvent)
160               buffer append ((resourceEvent, implicitEnd))
161             }
162
163             // remove it anyway
164             map.remove((resourceEvent.safeResource, resourceEvent.safeInstanceId))
165           }
166         }
167       }
168     }
169
170     doItFor(previousResourceEvents.latestEventsMap) // we give priority for previous
171     doItFor(implicitlyIssuedStartEvents.implicitlyIssuedEventsMap) // ... over implicitly issued...
172
173     (buffer.view.map(_._1).toList, buffer.view.map(_._2).toList)
174   }
175 }
176
177 object UserStateWorker {
178   def fromUserState(userState: UserState, accounting: Accounting, resourcesMap: DSLResourcesMap): UserStateWorker = {
179     UserStateWorker(
180       userState.userID,
181       userState.latestResourceEventsSnapshot.toMutableWorker,
182       userState.implicitlyIssuedSnapshot.toMutableWorker,
183       IgnoredFirstResourceEventsWorker.Empty,
184       accounting,
185       resourcesMap
186     )
187   }
188 }