DefaultLockHandler.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2015 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
12 
24 
25 /**
26  * DefaultLockHandler implements the LockHandler interface for relational
27  * databases. It relies on an entity type that implements the PersistentLock
28  * interface.
29  *
30  * @author ingo herwig <ingo@wemove.com>
31  */
32 class DefaultLockHandler implements LockHandler {
33 
34  const SESSION_VARNAME = 'DefaultLockHandler.locks';
35 
36  private $_persistenceFacade = null;
37  private $_session = null;
38  private $_lockType = null;
39 
40  private static $_logger = null;
41 
42  /**
43  * Constructor
44  * @param $persistenceFacade
45  * @param $session
46  * @param $lockType Entity type name of PersistentLock instances
47  */
48  public function __construct(PersistenceFacade $persistenceFacade,
49  Session $session,
50  $lockType) {
51  $this->_persistenceFacade = $persistenceFacade;
52  $this->_session = $session;
53  $this->_lockType = $lockType;
54  if (self::$_logger == null) {
55  self::$_logger = LogManager::getLogger(__CLASS__);
56  }
57  }
58 
59  /**
60  * @see LockHandler::aquireLock()
61  */
62  public function aquireLock(ObjectId $oid, $type, PersistentObject $currentState=null) {
63  $currentUser = $this->getCurrentUser();
64  if (!$currentUser) {
65  return;
66  }
67 
68  // check for existing locks
69  $lock = $this->getLock($oid);
70  if ($lock != null) {
71  if ($lock->getType() == Lock::TYPE_PESSIMISTIC) {
72  // if the existing lock is a pessimistic lock and it is owned by another
73  // user, we throw an exception
74  if ($lock->getLogin() != $currentUser->getLogin()) {
75  throw new PessimisticLockException($lock);
76  }
77  // if the existing lock is a pessimistic lock and is owned by the user
78  // there is no need to aquire a optimistic lock
79  if ($type == Lock::TYPE_OPTIMISTIC) {
80  return;
81  }
82  }
83  }
84 
85  // create the lock instance
86  $lock = new Lock($type, $oid, $currentUser->getLogin(), $this->_session->getID());
87 
88  // set the current state for optimistic locks
89  if ($type == Lock::TYPE_OPTIMISTIC) {
90  $lock->setCurrentState($currentState);
91  }
92 
93  // store the lock
94  $this->storeLock($lock);
95  }
96 
97  /**
98  * @see LockHandler::releaseLock()
99  */
100  public function releaseLock(ObjectId $oid, $type=null) {
101  $currentUser = $this->getCurrentUser();
102  if (!$currentUser) {
103  return;
104  }
105 
106  // delete locks for the given oid and current user
107  $query = new ObjectQuery($this->_lockType, __CLASS__.__METHOD__);
108  $tpl = $query->getObjectTemplate($this->_lockType);
109  $tpl->setValue('objectid', Criteria::asValue("=", $oid));
110  $tpl->setValue('login', Criteria::asValue("=", $currentUser->getLogin()));
111  $locks = $query->execute(BuildDepth::SINGLE);
112  foreach($locks as $lock) {
113  // delete lock immediatly
114  $lock->getMapper()->delete($lock);
115  $this->removeSessionLock($oid, $type);
116  }
117  }
118 
119  /**
120  * @see LockHandler::releaseLocks()
121  */
122  public function releaseLocks(ObjectId $oid) {
123  // delete locks for the given oid
124  $query = new ObjectQuery($this->_lockType, __CLASS__.__METHOD__);
125  $tpl = $query->getObjectTemplate($this->_lockType);
126  $tpl->setValue('objectid', Criteria::asValue("=", $oid));
127  $locks = $query->execute(BuildDepth::SINGLE);
128  foreach($locks as $lock) {
129  // delete lock immediatly
130  $lock->getMapper()->delete($lock);
131  $this->removeSessionLock($oid);
132  }
133  }
134 
135  /**
136  * @see LockHandler::releaseAllLocks()
137  */
138  public function releaseAllLocks() {
139  $currentUser = $this->getCurrentUser();
140  if (!$currentUser) {
141  return;
142  }
143 
144  // delete locks for the current user
145  $query = new ObjectQuery($this->_lockType, __CLASS__.__METHOD__);
146  $tpl = $query->getObjectTemplate($this->_lockType);
147  $tpl->setValue('login', Criteria::asValue("=", $currentUser->getLogin()));
148  $locks = $query->execute(BuildDepth::SINGLE);
149  foreach($locks as $lock) {
150  // delete lock immediatly
151  $lock->getMapper()->delete($lock);
152  $this->removeSessionLock($lock->getValue('objectid'));
153  }
154  }
155 
156  /**
157  * @see LockHandler::getLock()
158  */
159  public function getLock(ObjectId $oid) {
160  // check if a lock is stored in the session (maybe optimistic
161  // or pessimistic)
162  $locks = $this->getSessionLocks();
163  $oidStr = $oid->__toString();
164  $sessionLock = null;
165  if (isset($locks[$oidStr])) {
166  $sessionLock = $locks[$oidStr];
167  }
168 
169  // if the session lock is pessimistic (exclusive), return it
170  if ($sessionLock != null && $sessionLock->getType() == Lock::TYPE_PESSIMISTIC) {
171  return $sessionLock;
172  }
173  else {
174  // otherwise we need to check for a pessimistic lock in the store
175  $query = new ObjectQuery($this->_lockType, __CLASS__.__METHOD__);
176  $tpl = $query->getObjectTemplate($this->_lockType);
177  $tpl->setValue('objectid', Criteria::asValue('=', $oid));
178  $locks = $query->execute(BuildDepth::SINGLE);
179  if (sizeof($locks) > 0) {
180  $lockObj = $locks[0];
181  $lock = new Lock(Lock::TYPE_PESSIMISTIC, $oid, $lockObj->getValue('login'),
182  $lockObj->getValue('sessionid'), $lockObj->getValue('created'));
183 
184  // if the lock belongs to the current user, we store
185  // it in the session for later retrieval
186  $currentUser = $this->getCurrentUser();
187  if ($currentUser && $lockObj->getValue('login') == $currentUser->getLogin()) {
188  $this->addSessionLock($lock);
189  }
190  return $lock;
191  }
192  }
193 
194  return $sessionLock;
195  }
196 
197  /**
198  * @see LockHandler::updateLock()
199  */
200  public function updateLock(ObjectId $oid, PersistentObject $object) {
201  $lock = $this->getLock($oid);
202  if ($lock) {
203  if ($lock->getType() == Lock::TYPE_OPTIMISTIC) {
204  $currentUser = $this->getCurrentUser();
205  if ($currentUser && $lock->getLogin() == $currentUser->getLogin()) {
206  $lock->setCurrentState($object);
207  $this->storeLock($lock);
208  }
209  }
210  }
211  }
212 
213  /**
214  * Store the given Lock instance for later retrieval
215  * @param $lock Lock instance
216  */
217  protected function storeLock(Lock $lock) {
218  if ($lock->getType() == Lock::TYPE_PESSIMISTIC) {
219  // pessimistic locks must be stored in the database in order
220  // to be seen by other users
221  $lockObj = $this->_persistenceFacade->create($this->_lockType, BuildDepth::REQUIRED);
222  $lockObj->setValue('objectid', $lock->getObjectId());
223  $lockObj->setValue('login', $lock->getLogin());
224  $lockObj->setValue('created', $lock->getCreated());
225  // save lock immediatly
226  $lockObj->getMapper()->save($lockObj);
227  }
228  // store the lock in the session for faster retrieval
229  $this->addSessionLock($lock);
230  }
231 
232  /**
233  * Get the current user
234  * @return User instance
235  */
236  protected function getCurrentUser() {
237  return $this->_session->getAuthUser();
238  }
239 
240  /**
241  * Get the Lock instances stored in the session
242  * @return Associative array with the serialized ObjectId instances
243  * as keys and the Lock instances as values
244  */
245  protected function getSessionLocks() {
246  if ($this->_session->exist(self::SESSION_VARNAME)) {
247  return $this->_session->get(self::SESSION_VARNAME);
248  }
249  return array();
250  }
251 
252  /**
253  * Add a given Lock instance to the session
254  * @param $lock Lock instance
255  */
256  protected function addSessionLock(Lock $lock) {
257  $locks = $this->getSessionLocks();
258  $locks[$lock->getObjectId()->__toString()] = $lock;
259  $this->_session->set(self::SESSION_VARNAME, $locks);
260  }
261 
262  /**
263  * Remove a given Lock instance from the session
264  * @param $oid The locked oid
265  * @param $type One of the Lock::Type constants or null for all types (default: _null_)
266  */
267  protected function removeSessionLock(ObjectId $oid, $type) {
268  $locks = $this->getSessionLocks();
269  if (isset($locks[$oid->__toString()])) {
270  $lock = $locks[$oid->__toString()];
271  if ($type == null || $type != null && $lock->getType() == $type) {
272  unset($locks[$oid->__toString()]);
273  $this->_session->set(self::SESSION_VARNAME, $locks);
274  }
275  }
276  }
277 }
278 ?>
getSessionLocks()
Get the Lock instances stored in the session.
getObjectId()
Get the oid of the locked object.
Definition: Lock.php:59
setCurrentState($currentState)
Set the original state of the object in case of an optimistic lock.
Definition: Lock.php:84
Lock represents a lock on an object.
Definition: Lock.php:18
ObjectQuery implements a template based object query.
updateLock(ObjectId $oid, PersistentObject $object)
getCreated()
Get the creation date/time of the lock.
Definition: Lock.php:75
static asValue($operator, $value)
Factory method for constructing a Critera that may be used as value on a PersistentObject's attribute...
Definition: Criteria.php:58
ObjectId is the unique identifier of an object.
Definition: ObjectId.php:27
static getLogger($name)
Get the logger with the given name.
Definition: LogManager.php:35
__construct(PersistenceFacade $persistenceFacade, Session $session, $lockType)
Constructor.
storeLock(Lock $lock)
Store the given Lock instance for later retrieval.
getLogin()
Get the login of the user who holds the lock.
Definition: Lock.php:67
aquireLock(ObjectId $oid, $type, PersistentObject $currentState=null)
Session is the interface for session implementations and defines access to session variables...
Definition: Session.php:21
addSessionLock(Lock $lock)
Add a given Lock instance to the session.
getType()
Get the type of the lock.
Definition: Lock.php:51
__toString()
Get a string representation of the object id.
Definition: ObjectId.php:204
LockHandler defines the interface for LockHandler implementations.
Definition: LockHandler.php:21
removeSessionLock(ObjectId $oid, $type)
Remove a given Lock instance from the session.
PessimisticLockException signals an exception when trying to create an pessimistic lock...
PersistenceFacade defines the interface for PersistenceFacade implementations.
DefaultLockHandler implements the LockHandler interface for relational databases. ...
PersistentObject defines the interface of all persistent objects.