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