Interface LockManager
-
public interface LockManager
This LockManager is a manager to obtain cluster wide locks. To obtain the
LockManager
you can useLockManager lockManager = HippoServiceRegistry.getService(LockManager.class))
Lock
is obtained, that lock is tied to theThread
that obtained theLock
and can only be unlocked by the sameThread
. The number of invocations onlock(String)
must be balanced withunlock(String)
since callingLock
multiple times increases the hold count: Only when the hold count is 0, the lock is really freed.An example usage is as follows:
public void run() { try (LockResource ignore = lockManager.lock(key)){ // session.refresh(true|false) if JCR nodes are involved // Do work } catch (AlreadyLockedException e) { log.info("'{}' is already locked", key, e); } catch (LockException e) { log.error("Exception while trying to obtain lock, e); } }
AutoCloseable
concept ofLockResource
:public void run() { boolean locked = false; try { lockManager.lock(key); locked = true; // session.refresh(true|false) if JCR nodes are involved // Do work } catch (AlreadyLockedException e) { log.info("'{}' is already locked", key, e); } catch (LockException e) { log.error("Exception while trying to obtain lock, e); }finally { if (locked) { lockManager.unlock(key); } } }
Note that when
key
is already locked by anotherThread
or other cluster node, the invocation of#lock(key)
directly results in anAlreadyLockedException
: This is thus different thanReentrantLock.lock()
behavior (which blocks until the lock is acquired). If you need similar behavior toReentrantLock.lock()
but then cluster wide, you can useLockManagerUtils.waitForLock(LockManager, String, long)
and if you need the cluster wide equivalent ofReentrantLock.tryLock(long, TimeUnit)
you can useLockManagerUtils.waitForLock(LockManager, String, long, long)
.Usage in combination with JCR:
When you use thisLockManager
to obtain a cluster wide lock after which the code is doing JCR node manipulation, eg updating the last modification timestamp on a JCR node, then make sure to always invokesession.refresh(true|false);
LockResource
. The reason for this is that in the cluster wide 'synchronized' part of the code, you want to make sure that all JCR nodes the code is going to touch are in sync with the latest cluster state and that the code is not chatting with local stale JCR nodes. Thus make sure to always invokesession.refresh(true|false);
when dealing with JCR nodes in a cluster wide synchronized code block.
-
-
Field Summary
Fields Modifier and Type Field Description static int
LOCK_KEY_MAX_LENGTH
Maximum number of chars for alock(String)
key.
-
Method Summary
All Methods Instance Methods Abstract Methods Modifier and Type Method Description void
abort(String key)
List<Lock>
getLocks()
boolean
isLocked(String key)
Returnstrue
if there is a lock forkey
.LockResource
lock(String key)
Tries to create aLock
forkey
.void
unlock(String key)
-
-
-
Field Detail
-
LOCK_KEY_MAX_LENGTH
static final int LOCK_KEY_MAX_LENGTH
Maximum number of chars for alock(String)
key.- See Also:
- Constant Field Values
-
-
Method Detail
-
lock
LockResource lock(String key) throws LockException
Tries to create a
Lock
forkey
. Thekey
is not allowed to exceed 256 chars. If there is already aLock
forkey
then in case the currentThread
has the lock, void is returned, otherwise aLockException
is thrown.Invoking this method multiple times with the same
key
and the sameThread
results in the hold count being incremented. To unlock the lock,unlock(String)
orLockResource.close()
must be invoked an equal amount of times aslock(String)
was invoked and the unlock must be invoked with the sameThread
as the one that obtained theLock
. Note that theLockResource.close()
may be invoked by a differentThread
!A lock is released when a successful
unlock(String)
orLockResource.close()
is invoked as many times aslock(String)
. Alternatively, when theLockManager
implementation detects that the Thread that held the lock is not live any more, theLockManager
implementation can also release the lock.In a clustered setup, a lock will be released (in the database) when it has not been refreshed for more than 60 seconds. This is a safeguard in case of a clustered setup where a cluster node has an ungraceful shutdown (crash) : In that case some database lock might still be present for the crashed node. A graceful shutdown should release all locks, implying that every Thread that holds a lock calls
unlock(java.lang.String)
A persisted
Lock
can be marked to be aborted: In this case, theThread
that holds the lock gets interrupted (Thread.interrupt()
). Threads that hold a lock should invokeunlock(String)
when interrupted (in general by just stopping their work and make sure the finally in the try block is hit which in general should contain theunlock(String)
logic.- Parameters:
key
- the key for theLock
wherekey
is now allowed to exceed 256 chars- Returns:
LockResource
such that thislock(String)
method can be used in a try-with-resources statement where theLockResource.close()
results in the lock being freed.- Throws:
LockException
- in case there is already aLock
forkey
(throwing aAlreadyLockedException
) or the lock could not be created due to some other exception (resulting in aLockManagerException
)IllegalArgumentException
- if thekey
exceeds 256 chars
-
unlock
void unlock(String key)
- Parameters:
key
- the key to unlock wherekey
is at most 256 chars. If theThread
that invokesunlock(key)
does not hold theLock
, nothing happens (apart from theLockManager
implementation most likely logging a warning or error, because it is an implementation issue ifunlock(key)
is invoked by a thread that does not hold the lock.- Throws:
IllegalArgumentException
- if thekey
exceeds 256 chars
-
isLocked
boolean isLocked(String key) throws LockManagerException
Returns
true
if there is a lock forkey
. Note that this method returnstrue
orfalse
regardless whether theThread
that invokesisLocked(String)
contains the lock or whether anotherThread
contains the lock- Parameters:
key
- thekey
to check whether there is a lock for- Returns:
true
when locked- Throws:
IllegalArgumentException
- if thekey
exceeds 256 charsLockManagerException
- if some irrecoverable error occurs, for example a database request timeout
-
getLocks
List<Lock> getLocks() throws LockManagerException
- Returns:
- all the
Lock
s that are currently active (including locks that are marked to be aborted but not yet aborted) - Throws:
LockManagerException
- if some irrecoverable error occurs, for example a database request timeout
-
abort
void abort(String key) throws LockManagerException
Indicates the
LockManager
that theThread
containing theLock
forkey
should be interrupted. This method can be invoked by another thread than the one that holds theLock
. In clustered setups it can be requested by other cluster nodes that do not contain aThread
that holds theLock
.When the
LockManager
finds a lock marked to be aborted contained in its own JVM, it must interrupt theThread
that holds theLock
. As a result, the process should stop and theThread
to abort should invokeunlock(String)
If there is no
Lock
forkey
, nothing happens and void is returned.- Parameters:
key
- thekey
to check whether there is a lock for- Throws:
IllegalArgumentException
- if thekey
exceeds 256 charsLockManagerException
- if some irrecoverable error occurs, for example a database request timeout
-
-