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.store.memory
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._
45 import collection.mutable.ConcurrentMap
46 import gr.grnet.aquarium.logic.events.{WalletEntry, ResourceEvent, UserEvent, PolicyEntry}
47 import java.util.concurrent.ConcurrentHashMap
50 * An implementation of various stores that persists data in memory.
52 * This is just for testing purposes.
54 * @author Christos KK Loverdos <loverdos@gmail.com>
55 * @author Georgios Gousios <gousiosg@gmail.com>
58 class MemStore extends UserStateStore
59 with Configurable with PolicyStore
60 with ResourceEventStore with UserEventStore
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]()
70 def configure(props: Props) = {
73 override def toString = {
75 "UserState" -> _userStates.size,
76 "ResourceEvent" -> resourceEventsById.size,
77 "UserEvent" -> userEventById.size,
78 "PolicyEntry" -> policies.size,
79 "WalletEntry" -> walletEntriesById.size
82 "MemStore(%s)" format map
86 def userStateStore = this
88 def resourceEventStore = this
90 def walletEntryStore = this
92 def userEventStore = this
94 def policyStore = this
99 def storeUserState(userState: UserState): Maybe[RecordID] = {
100 _userStates = userState :: _userStates
101 Just(RecordID(userState.userId))
104 def findUserStateByUserId(userId: String) = {
105 _userStates.find(_.userId == userId) match {
106 case Some(userState) ⇒
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
129 us1.oldestSnapshotTime > us2.oldestSnapshotTime
138 def deleteUserState(userId: String) {
139 _userStates.filterNot(_.userId == userId)
144 def storeWalletEntry(entry: WalletEntry): Maybe[RecordID] = {
145 walletEntriesById.put(entry.id, entry)
146 Just(RecordID(entry.id))
149 def findWalletEntryById(id: String): Maybe[WalletEntry] = {
150 Maybe(walletEntriesById.apply(id))
153 def findUserWalletEntries(userId: String): List[WalletEntry] = {
154 walletEntriesById.valuesIterator.filter(_.userId == userId).toList
157 def findUserWalletEntriesFromTo(userId: String, from: Date, to: Date): List[WalletEntry] = {
158 walletEntriesById.valuesIterator.filter { we ⇒
159 val receivedDate = we.receivedDate
161 we.userId == userId &&
162 ( (from before receivedDate) || (from == receivedDate) ) &&
163 ( (to after receivedDate) || (to == receivedDate) )
168 def findLatestUserWalletEntries(userId: String): Maybe[List[WalletEntry]] = NoVal
170 def findPreviousEntry(userId: String,
173 finalized: Option[Boolean]): List[WalletEntry] = Nil
175 def findWalletEntriesAfter(userId: String, from: Date): List[WalletEntry] = {
176 walletEntriesById.valuesIterator.filter { we ⇒
177 val occurredDate = we.occurredDate
179 we.userId == userId &&
180 ( (from before occurredDate) || (from == occurredDate) )
185 //+ ResourceEventStore
186 def storeResourceEvent(event: ResourceEvent) = {
187 resourceEventsById(event.id) = event
188 Just(RecordID(event.id))
191 def findResourceEventById(id: String) = {
192 Maybe(resourceEventsById(id))
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 {
200 byUserId.sortWith(sorter)
208 def findResourceEventsByUserIdAfterTimestamp(userId: String, timestamp: Long): List[ResourceEvent] = {
209 resourceEventsById.valuesIterator.filter { ev ⇒
210 ev.userId == userId &&
211 (ev.occurredMillis > timestamp)
215 def findResourceEventHistory(userId: String,
217 instid: Option[String],
218 upTo: Long): List[ResourceEvent] = {
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
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?)
236 ev.isOutOfSyncForBillingPeriod(startMillis, stopMillis)
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.
246 * Order them by `occurredMillis`
248 override def findAllRelevantResourceEventsForBillingPeriod(userId: String,
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 }
255 //- ResourceEventStore
257 def storeUserEvent(event: UserEvent) = {userEventById += (event.id -> event); Just(RecordID(event.id))}
259 def findUserEventById(id: String) = Maybe{userEventById.getOrElse(id, null)}
261 def findUserEventsByUserId(userId: String) = userEventById.valuesIterator.filter{v => v.userId == userId}.toList
263 def loadPolicies(after: Long) =
264 policies.filter(p => p.validFrom > after)
265 .sortWith((a,b) => a.validFrom < b.validFrom)
267 def storePolicy(policy: PolicyEntry) = {policies = policy :: policies; Just(RecordID(policy.id))}
269 def updatePolicy(policy: PolicyEntry) =
270 policies = policies.foldLeft(List[PolicyEntry]()){
272 if (p.id == policy.id)
278 def findPolicy(id: String) = policies.find(p => p.id == id) match {
279 case Some(x) => Just(x)