Interface LockManager
This LockManager is a manager to obtain cluster wide locks. To obtain the LockManager
you can use
When a
LockManager lockManager = HippoServiceRegistry.getService(LockManager.class))
Lock
is obtained, that lock is tied to the Thread
that obtained the
Lock
and can only be unlocked by the same Thread
. The number of invocations on lock(String)
must be balanced with unlock(String)
since calling Lock
multiple times increases the hold count: Only
when the hold count is 0, the lock is really freed.
An example usage is as follows:
or without using
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 of LockResource
:
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 another Thread
or other cluster node,
the invocation of #lock(key)
directly results in an AlreadyLockedException
:
This is thus different than ReentrantLock.lock()
behavior (which blocks until the lock is acquired).
If you need similar behavior to ReentrantLock.lock()
but then cluster wide, you can use
LockManagerUtils.waitForLock(LockManager, String, long)
and if you need the cluster wide equivalent
of ReentrantLock.tryLock(long, TimeUnit)
you can use
LockManagerUtils.waitForLock(LockManager, String, long, long)
.
Usage in combination with JCR:
When you use this LockManager
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 invoke
after obtaining the
session.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 invoke
session.refresh(true|false);
when dealing with JCR nodes in a cluster wide synchronized code block.
-
Field Summary
Modifier and TypeFieldDescriptionstatic final int
Maximum number of chars for alock(String)
key. -
Method Summary
-
Field Details
-
LOCK_KEY_MAX_LENGTH
static final int LOCK_KEY_MAX_LENGTHMaximum number of chars for alock(String)
key.- See Also:
-
-
Method Details
-
lock
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
- 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
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
- 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
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
-