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