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.data
38 import gr.grnet.aquarium.event.model.im.IMEventModel
39 import gr.grnet.aquarium.util.shortClassNameOf
40 import gr.grnet.aquarium.util.date.MutableDateCalc
44 * @author Christos KK Loverdos <loverdos@gmail.com>
47 case class IMStateSnapshot(
49 * This is the latest processed IMEvent
51 latestIMEvent: IMEventModel,
54 * The earliest activation time, if it exists.
56 userEarliestActivationMillis: Option[Long],
59 * The user creation time, if it exists
61 userCreationMillis: Option[Long],
64 * This is the recorded role history
66 roleHistory: RoleHistory) {
69 * True iff the user has ever been activated even once.
71 def hasBeenActivated: Boolean = {
72 userEarliestActivationMillis.isDefined
75 def hasBeenCreated: Boolean = {
76 userCreationMillis.isDefined
80 * Given the newly arrived event, we compute the updated user earliest activation time, if any.
81 * We always update activation time if it is earlier than the currently known activation time.
83 private[this] def updatedEarliestActivationTime(imEvent: IMEventModel): Option[Long] = {
84 this.userEarliestActivationMillis match {
85 case Some(activationMillis) if imEvent.isStateActive && activationMillis < imEvent.occurredMillis ⇒
86 Some(imEvent.occurredMillis)
88 case None if imEvent.isStateActive ⇒
89 Some(imEvent.occurredMillis)
97 * Given the newly arrived event, we compute the updated user creation time, if any.
98 * Only the first `create` event triggers an actual update.
100 private[this] def updatedCreationTime(imEvent: IMEventModel): Option[Long] = {
101 // Allow only the first `create` event
102 if(this.userCreationMillis.isDefined) {
103 this.userCreationMillis
104 } else if(imEvent.isCreateUser) {
105 Some(imEvent.occurredMillis)
112 * Given the newly arrived event, we compute the updated role history.
114 private[this] def updatedRoleHistory(imEvent: IMEventModel): RoleHistory = {
115 this.roleHistory.updatedWithRole(imEvent.role, imEvent.occurredMillis)
119 * Computes an updated state and returns a tuple made of four elements:
120 * a) the updated state, b) a `Boolean` indicating whether the user creation
121 * time has changed, c) a `Boolean` indicating whether the user activation
122 * time has changed and d) a `Boolean` indicating whether the user
123 * role history has changed.
125 * The role history is updated only if the `roleCheck` is not `None` and
126 * the role it represents is different than the role of the `imEvent`.
127 * The motivation for `roleCheck` is to use this method in a loop (as in replaying
128 * events from the [[gr.grnet.aquarium.store.IMEventStore]]).
130 def updatedWithEvent(imEvent: IMEventModel,
131 roleCheck: Option[String]): (IMStateSnapshot, Boolean, Boolean, Boolean) = {
132 // Things of interest that may change by the imEvent:
133 // - user creation time
134 // - user activation time
137 val newCreationTime = updatedCreationTime(imEvent)
138 val creationTimeChanged = this.userCreationMillis != newCreationTime
140 val newActivationTime = updatedEarliestActivationTime(imEvent)
141 val activationTimeChanged = this.userEarliestActivationMillis != newActivationTime
143 val (roleChanged, newRoleHistory) = roleCheck match {
144 case Some(role) if role != imEvent.role ⇒
145 (true, updatedRoleHistory(imEvent))
148 (false, this.roleHistory)
151 val newState = this.copy(
152 latestIMEvent = imEvent,
153 userCreationMillis = newCreationTime,
154 userEarliestActivationMillis = newActivationTime,
155 roleHistory = newRoleHistory
158 (newState, creationTimeChanged, activationTimeChanged, roleChanged)
161 override def toString = {
162 "%s(\n!! %s\n!! %s\n!! %s\n!! %s)".format(
163 shortClassNameOf(this),
165 userCreationMillis.map(new MutableDateCalc(_)),
166 userEarliestActivationMillis.map(new MutableDateCalc(_)),
172 object IMStateSnapshot {
173 def initial(imEvent: IMEventModel): IMStateSnapshot = {
176 if(imEvent.isStateActive) Some(imEvent.occurredMillis) else None,
177 if(imEvent.isCreateUser) Some(imEvent.occurredMillis) else None,
178 RoleHistory.initial(imEvent.role, imEvent.occurredMillis))