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
47 * @author Christos KK Loverdos <loverdos@gmail.com>
50 case class IMStateSnapshot(
52 * This is the latest processed IMEvent
54 latestIMEvent: IMEventModel,
57 * The earliest activation time, if it exists.
59 userEarliestActivationMillis: Option[Long],
62 * The user creation time, if it exists
64 userCreationMillis: Option[Long],
67 * This is the recorded role history
69 roleHistory: RoleHistory
73 * True iff the user has ever been activated even once.
75 def hasBeenActivated: Boolean = {
76 userEarliestActivationMillis.isDefined
79 def hasBeenCreated: Boolean = {
80 userCreationMillis.isDefined
84 * Given the newly arrived event, we compute the updated user earliest activation time, if any.
85 * We always update activation time if it is earlier than the currently known activation time.
87 private[this] def updatedEarliestActivationTime(imEvent: IMEventModel): Option[Long] = {
88 this.userEarliestActivationMillis match {
89 case Some(activationMillis) if imEvent.isStateActive && activationMillis < imEvent.occurredMillis ⇒
90 Some(imEvent.occurredMillis)
92 case None if imEvent.isStateActive ⇒
93 Some(imEvent.occurredMillis)
101 * Given the newly arrived event, we compute the updated user creation time, if any.
102 * Only the first `create` event triggers an actual update.
104 private[this] def updatedCreationTime(imEvent: IMEventModel): Option[Long] = {
105 // Allow only the first `create` event
106 if(this.userCreationMillis.isDefined) {
107 this.userCreationMillis
108 } else if(imEvent.isCreateUser) {
109 Some(imEvent.occurredMillis)
116 * Given the newly arrived event, we compute the updated role history.
118 private[this] def updatedRoleHistory(imEvent: IMEventModel): RoleHistory = {
119 this.roleHistory.updatedWithRole(imEvent.role, imEvent.occurredMillis)
123 * Computes an updated state and returns a tuple made of four elements:
124 * a) the updated state, b) a `Boolean` indicating whether the user creation
125 * time has changed, c) a `Boolean` indicating whether the user activation
126 * time has changed and d) a `Boolean` indicating whether the user
127 * role history has changed.
129 * The role history is updated only if the `roleCheck` is not `None` and
130 * the role it represents is different than the role of the `imEvent`.
131 * The motivation for `roleCheck` is to use this method in a loop (as in replaying
132 * events from the [[gr.grnet.aquarium.store.IMEventStore]]).
134 def updatedWithEvent(imEvent: IMEventModel,
135 roleCheck: Option[String]): (IMStateSnapshot, Boolean, Boolean, Boolean) = {
136 // Things of interest that may change by the imEvent:
137 // - user creation time
138 // - user activation time
141 val newCreationTime = updatedCreationTime(imEvent)
142 val creationTimeChanged = this.userCreationMillis != newCreationTime
144 val newActivationTime = updatedEarliestActivationTime(imEvent)
145 val activationTimeChanged = this.userEarliestActivationMillis != newActivationTime
147 val (roleChanged, newRoleHistory) = roleCheck match {
148 case Some(role) if role != imEvent.role ⇒
149 (true, updatedRoleHistory(imEvent))
152 (false, this.roleHistory)
155 val newState = this.copy(
156 latestIMEvent = imEvent,
157 userCreationMillis = newCreationTime,
158 userEarliestActivationMillis = newActivationTime,
159 roleHistory = newRoleHistory
162 (newState, creationTimeChanged, activationTimeChanged, roleChanged)
165 override def toString = {
166 "%s(\n!! %s\n!! %s\n!! %s\n!! %s)".format(
167 shortClassNameOf(this),
169 userCreationMillis.map(new MutableDateCalc(_)),
170 userEarliestActivationMillis.map(new MutableDateCalc(_)),
176 object IMStateSnapshot {
177 def initial(imEvent: IMEventModel): IMStateSnapshot = {
180 if(imEvent.isStateActive) Some(imEvent.occurredMillis) else None,
181 if(imEvent.isCreateUser) Some(imEvent.occurredMillis) else None,
182 RoleHistory.initial(imEvent.role, imEvent.occurredMillis))