2 * Copyright 2011-2012 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.computation
40 import gr.grnet.aquarium.event.model.im.IMEventModel
41 import gr.grnet.aquarium.util.shortClassNameOf
42 import gr.grnet.aquarium.util.date.MutableDateCalc
43 import gr.grnet.aquarium.computation.parts.RoleHistory
44 import gr.grnet.aquarium.util.json.JsonSupport
48 * @author Christos KK Loverdos <loverdos@gmail.com>
51 case class IMStateSnapshot(
53 * This is the latest processed IMEvent
55 latestIMEvent: IMEventModel,
58 * The earliest activation time, if it exists.
60 userEarliestActivationMillis: Option[Long],
63 * The user creation time, if it exists
65 userCreationMillis: Option[Long],
68 * This is the recorded role history
70 roleHistory: RoleHistory
71 ) extends JsonSupport {
74 * True iff the user has ever been activated even once.
76 def hasBeenActivated: Boolean = {
77 userEarliestActivationMillis.isDefined
80 def hasBeenCreated: Boolean = {
81 userCreationMillis.isDefined
85 * Given the newly arrived event, we compute the updated user earliest activation time, if any.
86 * We always update activation time if it is earlier than the currently known activation time.
88 private[this] def updatedEarliestActivationTime(imEvent: IMEventModel): Option[Long] = {
89 this.userEarliestActivationMillis match {
90 case Some(activationMillis) if imEvent.isStateActive && activationMillis < imEvent.occurredMillis ⇒
91 Some(imEvent.occurredMillis)
93 case None if imEvent.isStateActive ⇒
94 Some(imEvent.occurredMillis)
102 * Given the newly arrived event, we compute the updated user creation time, if any.
103 * Only the first `create` event triggers an actual update.
105 private[this] def updatedCreationTime(imEvent: IMEventModel): Option[Long] = {
106 // Allow only the first `create` event
107 if(this.userCreationMillis.isDefined) {
108 this.userCreationMillis
109 } else if(imEvent.isCreateUser) {
110 Some(imEvent.occurredMillis)
117 * Given the newly arrived event, we compute the updated role history.
119 private[this] def updatedRoleHistory(imEvent: IMEventModel): RoleHistory = {
120 this.roleHistory.updatedWithRole(imEvent.role, imEvent.occurredMillis)
124 * Computes an updated state and returns a tuple made of four elements:
125 * a) the updated state, b) a `Boolean` indicating whether the user creation
126 * time has changed, c) a `Boolean` indicating whether the user activation
127 * time has changed and d) a `Boolean` indicating whether the user
128 * role history has changed.
130 * The role history is updated only if the `roleCheck` is not `None` and
131 * the role it represents is different than the role of the `imEvent`.
132 * The motivation for `roleCheck` is to use this method in a loop (as in replaying
133 * events from the [[gr.grnet.aquarium.store.IMEventStore]]).
135 def updatedWithEvent(imEvent: IMEventModel,
136 roleCheck: Option[String]): (IMStateSnapshot, Boolean, Boolean, Boolean) = {
137 // Things of interest that may change by the imEvent:
138 // - user creation time
139 // - user activation time
142 val newCreationTime = updatedCreationTime(imEvent)
143 val creationTimeChanged = this.userCreationMillis != newCreationTime
145 val newActivationTime = updatedEarliestActivationTime(imEvent)
146 val activationTimeChanged = this.userEarliestActivationMillis != newActivationTime
148 val (roleChanged, newRoleHistory) = roleCheck match {
149 case Some(role) if role != imEvent.role ⇒
150 (true, updatedRoleHistory(imEvent))
153 (false, this.roleHistory)
156 val newState = this.copy(
157 latestIMEvent = imEvent,
158 userCreationMillis = newCreationTime,
159 userEarliestActivationMillis = newActivationTime,
160 roleHistory = newRoleHistory
163 (newState, creationTimeChanged, activationTimeChanged, roleChanged)
166 override def toString = {
167 "%s(\n!! %s\n!! %s\n!! %s\n!! %s)".format(
168 shortClassNameOf(this),
170 userCreationMillis.map(new MutableDateCalc(_)),
171 userEarliestActivationMillis.map(new MutableDateCalc(_)),
177 object IMStateSnapshot {
178 def initial(imEvent: IMEventModel): IMStateSnapshot = {
181 if(imEvent.isStateActive) Some(imEvent.occurredMillis) else None,
182 if(imEvent.isCreateUser) Some(imEvent.occurredMillis) else None,
183 RoleHistory.initial(imEvent.role, imEvent.occurredMillis))