Change the impl of mem store
[aquarium] / src / main / scala / gr / grnet / aquarium / store / memory / MemStore.scala
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.store.memory
37
38 import gr.grnet.aquarium.user.UserState
39 import gr.grnet.aquarium.Configurable
40 import com.ckkloverdos.props.Props
41 import com.ckkloverdos.maybe.{NoVal, Just, Maybe}
42 import gr.grnet.aquarium.store._
43 import scala.collection.JavaConversions._
44 import java.util.Date
45 import collection.mutable.ConcurrentMap
46 import gr.grnet.aquarium.logic.events.{WalletEntry, ResourceEvent, UserEvent, PolicyEntry}
47 import java.util.concurrent.ConcurrentHashMap
48
49 /**
50  * An implementation of various stores that persists data in memory.
51  *
52  * This is just for testing purposes.
53  * 
54  * @author Christos KK Loverdos <loverdos@gmail.com>
55  * @author Georgios Gousios <gousiosg@gmail.com>
56  */
57
58 class MemStore extends UserStateStore
59   with Configurable with PolicyStore
60   with ResourceEventStore with UserEventStore
61   with WalletEntryStore
62   with StoreProvider {
63
64   private[this] var _userStates = List[UserState]()
65   private var policies: List[PolicyEntry] = List()
66   private[this] val walletEntriesById: ConcurrentMap[String, WalletEntry] = new ConcurrentHashMap[String, WalletEntry]()
67   private val userEventById: ConcurrentMap[String, UserEvent] = new ConcurrentHashMap[String, UserEvent]()
68   private[this] val resourceEventsById: ConcurrentMap[String, ResourceEvent] = new ConcurrentHashMap[String, ResourceEvent]()
69
70   def configure(props: Props) = {
71   }
72
73   override def toString = {
74     val map = Map(
75       "UserState"     -> _userStates.size,
76       "ResourceEvent" -> resourceEventsById.size,
77       "UserEvent"     -> userEventById.size,
78       "PolicyEntry"   -> policies.size,
79       "WalletEntry"   -> walletEntriesById.size
80     )
81
82     "MemStore(%s)" format map
83   }
84
85   //+ StoreProvider
86   def userStateStore = this
87
88   def resourceEventStore = this
89
90   def walletEntryStore = this
91
92   def userEventStore = this
93
94   def policyStore = this
95   //- StoreProvider
96
97
98   //+ UserStateStore
99   def storeUserState(userState: UserState): Maybe[RecordID] = {
100     _userStates = userState :: _userStates
101     Just(RecordID(userState.userId))
102   }
103
104   def findUserStateByUserId(userId: String) = {
105     _userStates.find(_.userId == userId) match {
106       case Some(userState) ⇒
107         Just(userState)
108       case None ⇒
109         NoVal
110     }
111   }
112
113   def findLatestUserStateForEndOfBillingMonth(userId: String,
114                                               yearOfBillingMonth: Int,
115                                               billingMonth: Int): Maybe[UserState] = {
116     val goodOnes = _userStates.filter { userState ⇒
117         val f1 = userState.userId == userId
118         val f2 = userState.isFullBillingMonthState
119         val bm = userState.theFullBillingMonth
120         val f3 = (bm ne null) && {
121           bm.yearOfBillingMonth == yearOfBillingMonth && bm.billingMonth == billingMonth
122         }
123
124         f1 && f2 && f3
125     }
126     
127     goodOnes.sortWith {
128       case (us1, us2) ⇒
129         us1.oldestSnapshotTime > us2.oldestSnapshotTime
130     } match {
131       case head :: _ ⇒
132         Just(head)
133       case _ ⇒
134         NoVal
135     }
136   }
137
138   def deleteUserState(userId: String) {
139     _userStates.filterNot(_.userId == userId)
140   }
141   //- UserStateStore
142
143   //- WalletEntryStore
144   def storeWalletEntry(entry: WalletEntry): Maybe[RecordID] = {
145     walletEntriesById.put(entry.id, entry)
146     Just(RecordID(entry.id))
147   }
148
149   def findWalletEntryById(id: String): Maybe[WalletEntry] = {
150     Maybe(walletEntriesById.apply(id))
151   }
152
153   def findUserWalletEntries(userId: String): List[WalletEntry] = {
154     walletEntriesById.valuesIterator.filter(_.userId == userId).toList
155   }
156
157   def findUserWalletEntriesFromTo(userId: String, from: Date, to: Date): List[WalletEntry] = {
158     walletEntriesById.valuesIterator.filter { we ⇒
159       val receivedDate = we.receivedDate
160
161       we.userId == userId &&
162       ( (from before receivedDate) || (from == receivedDate) ) &&
163       ( (to   after  receivedDate) || (to   == receivedDate) )
164       true
165     }.toList
166   }
167
168   def findLatestUserWalletEntries(userId: String): Maybe[List[WalletEntry]] = NoVal
169
170   def findPreviousEntry(userId: String,
171                         resource: String,
172                         instanceId: String,
173                         finalized: Option[Boolean]): List[WalletEntry] = Nil
174
175   def findWalletEntriesAfter(userId: String, from: Date): List[WalletEntry] = {
176     walletEntriesById.valuesIterator.filter { we ⇒
177       val occurredDate = we.occurredDate
178
179       we.userId == userId &&
180             ( (from before occurredDate) || (from == occurredDate) )
181     }.toList
182   }
183   //- WalletEntryStore
184
185   //+ ResourceEventStore
186   def storeResourceEvent(event: ResourceEvent) = {
187     resourceEventsById(event.id) = event
188     Just(RecordID(event.id))
189   }
190
191   def findResourceEventById(id: String) = {
192     Maybe(resourceEventsById(id))
193   }
194
195   def findResourceEventsByUserId(userId: String)
196                                 (sortWith: Option[(ResourceEvent, ResourceEvent) => Boolean]): List[ResourceEvent] = {
197     val byUserId = resourceEventsById.valuesIterator.filter(_.userId == userId).toArray
198     val sorted = sortWith match {
199       case Some(sorter) ⇒
200         byUserId.sortWith(sorter)
201       case None ⇒
202         byUserId
203     }
204
205     sorted.toList
206   }
207
208   def findResourceEventsByUserIdAfterTimestamp(userId: String, timestamp: Long): List[ResourceEvent] = {
209     resourceEventsById.valuesIterator.filter { ev ⇒
210       ev.userId == userId &&
211       (ev.occurredMillis > timestamp)
212     }.toList
213   }
214
215   def findResourceEventHistory(userId: String,
216                                resName: String,
217                                instid: Option[String],
218                                upTo: Long): List[ResourceEvent] = {
219     Nil
220   }
221
222   def findResourceEventsForReceivedPeriod(userId: String,
223                                           startTimeMillis: Long,
224                                           stopTimeMillis: Long): List[ResourceEvent] = {
225     resourceEventsById.valuesIterator.filter { ev ⇒
226       ev.userId == userId &&
227       ev.receivedMillis >= startTimeMillis &&
228       ev.receivedMillis <= stopTimeMillis
229     }.toList
230   }
231
232   def countOutOfSyncEventsForBillingPeriod(userId: String, startMillis: Long, stopMillis: Long): Maybe[Long] = Maybe {
233     resourceEventsById.valuesIterator.filter { case ev ⇒
234       // out of sync events are those that were received in the billing month but occurred in previous (or next?)
235       // months
236       ev.isOutOfSyncForBillingPeriod(startMillis, stopMillis)
237     }.size.toLong
238   }
239
240   /**
241    * Finds all relevant resource events for the billing period.
242    * The relevant events are those:
243    * a) whose `occurredMillis` is within the given billing period or
244    * b) whose `receivedMillis` is within the given billing period.
245    *
246    * Order them by `occurredMillis`
247    */
248   override def findAllRelevantResourceEventsForBillingPeriod(userId: String,
249                                                              startMillis: Long,
250                                                              stopMillis: Long): List[ResourceEvent] = {
251     resourceEventsById.valuesIterator.filter { case ev ⇒
252       ev.isOccurredOrReceivedWithinMillis(startMillis, stopMillis)
253     }.toList sortWith { case (ev1, ev2) ⇒ ev1.occurredMillis <= ev2.occurredMillis }
254   }
255   //- ResourceEventStore
256
257   def storeUserEvent(event: UserEvent) = {userEventById += (event.id -> event); Just(RecordID(event.id))}
258
259   def findUserEventById(id: String) = Maybe{userEventById.getOrElse(id, null)}
260
261   def findUserEventsByUserId(userId: String) = userEventById.valuesIterator.filter{v => v.userId == userId}.toList
262
263   def loadPolicies(after: Long) =
264     policies.filter(p => p.validFrom > after)
265             .sortWith((a,b) => a.validFrom < b.validFrom)
266
267   def storePolicy(policy: PolicyEntry) = {policies = policy :: policies; Just(RecordID(policy.id))}
268
269   def updatePolicy(policy: PolicyEntry) =
270     policies = policies.foldLeft(List[PolicyEntry]()){
271       (acc, p) =>
272         if (p.id == policy.id)
273           policy :: acc
274         else
275           p :: acc
276   }
277
278   def findPolicy(id: String) = policies.find(p => p.id == id) match {
279     case Some(x) => Just(x)
280     case None => NoVal
281   }
282 }