/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.lock; import org.alfresco.service.namespace.QName; /** * Service interface for managing job locks. *

* Locks are identified by a fully qualified name ({@link QName}) and follow a hierarchical * naming convention i.e. locks higher up a hierarchy can be shared but will prevent explicit * (exclusive) locks from being taken. For example: If exclusive lock a.a.a has been * taken, then a.a and a are all implicitly taken as shared locks. Exclusive lock * a.a.b can be taken by another process and will share locks a.a and a * with the first process. It will not be possible for a third process to take a lock on * a.a, however. *

* LOCK ORDERING:
* The transactional locks will be applied in strict alphabetical order. A very basic deadlock * prevention system (at least) must be in place when applying or reapplying locks and be biased * against locks applied non-alphabetically. * * @author Derek Hulley * @since 3.2 */ public interface JobLockService { /** * Take a transactionally-managed lock. This method can be called repeatedly to both * initially acquire the lock as well as to maintain the lock. This method should * be called repeatedly during the transaction to ensure that the lock remains refreshed. * DO NOT use a long-lived lock to avoid calling this method at intervals; long-lived * locks get left behind during server crashes, amongst other things. *

* The following rules apply to taking and releasing locks:
* - Expired locks can be taken by any process
* - Lock expiration does not prevent a lock from being refreshed or released
* - Only locks that were manipulated using another token will cause failure *

* The locks are automatically released when the transaction is terminated. *

* Any failure to acquire the lock (after retries), refresh the lock or subsequently * release the owned locks will invalidate the transaction and cause rollback. * * @param lockQName the name of the lock to acquire * @param timeToLive the time (in milliseconds) for the lock to remain valid. * This value must not be larger than either the anticipated * operation time or a server startup time. Typically, it should be * a few seconds. * @throws LockAcquisitionException if the lock could not be acquired * @throws IllegalStateException if a transaction is not active */ void getTransactionalLock(QName lockQName, long timeToLive); /** * Take a transactionally-managed lock. This method can be called repeatedly to both * initially acquire the lock as well as to maintain the lock. This method should * be called repeatedly during the transaction to ensure that the lock remains refreshed. * DO NOT use a long-lived lock to avoid calling this method at intervals; long-lived * locks get left behind during server crashes, amongst other things. *

* The following rules apply to taking and releasing locks:
* - Expired locks can be taken by any process
* - Lock expiration does not prevent a lock from being refreshed or released
* - Only locks that were manipulated using another token will cause failure *

* The locks are automatically released when the transaction is terminated. *

* Any failure to acquire the lock (after retries), refresh the lock or subsequently * release the owned locks will invalidate the transaction and cause rollback. *

* If the lock cannot be immediately acquired, the process will wait and retry. Note * that second and subsequent attempts to get the lock during a transaction cannot * make use of retrying; the lock is actually being refreshed and will therefore never * become valid if it doesn't refresh directly. * * @param lockQName the name of the lock to acquire * @param timeToLive the time (in milliseconds) for the lock to remain valid. * This value must not be larger than either the anticipated * operation time or a server startup time. Typically, it should be * a few seconds. * @param retryWait the time (in milliseconds) to wait before trying again * @param retryCount the maximum number of times to attempt the lock acquisition * @throws LockAcquisitionException if the lock could not be acquired * @throws IllegalStateException if a transaction is not active */ void getTransactionalLock(QName lockQName, long timeToLive, long retryWait, int retryCount); /** * Take a manually-managed lock. The lock current thread or transaction will not be tagged - * the returned lock token must be used for further management of the lock. *

* No lock management is provided: the lock must be released manually or will only become * available by expiry. No deadlock management is provided, either. * * @param lockQName the name of the lock to acquire * @param timeToLive the time (in milliseconds) for the lock to remain valid. * This value must not be larger than either the anticipated * operation time or a server startup time. Typically, it should be * a few seconds. * @return Returns the newly-created lock token * @throws LockAcquisitionException if the lock could not be acquired */ String getLock(QName lockQName, long timeToLive); /** * Take a manually-managed lock. The lock current thread or transaction will not be tagged - * the returned lock token must be used for further management of the lock. *

* No lock management is provided: the lock must be released manually or will only become * available by expiry. No deadlock management is provided, either. *

* If the lock cannot be immediately acquired, the process will wait and retry. * * @param lockQName the name of the lock to acquire * @param timeToLive the time (in milliseconds) for the lock to remain valid. * This value must not be larger than either the anticipated * operation time or a server startup time. Typically, it should be * a few seconds. * @param retryWait the time (in milliseconds) to wait before trying again * @param retryCount the maximum number of times to attempt the lock acquisition * @throws LockAcquisitionException if the lock could not be acquired */ String getLock(QName lockQName, long timeToLive, long retryWait, int retryCount); /** * Refresh the lock using a valid lock token. * * @param lockToken the lock token returned when the lock was acquired * @param lockQName the name of the previously-acquired lock * @param timeToLive the time (in milliseconds) for the lock to remain valid * @throws LockAcquisitionException if the lock could not be refreshed or acquired */ void refreshLock(String lockToken, QName lockQName, long timeToLive); /** * Provide a callback to refresh a lock using a valid lock token, pushing responsibility * for regular lock refreshing onto the service implementation code. This method should only * be called once for a given lock token to prevent unnecessary refreshing. *

* Since the lock is not actually refreshed by this method, there will be no LockAcquisitionException. *

* The TTL (time to live) will be divided by two and the result used to trigger a timer thread * to initiate the callback. * * @param lockToken the lock token returned when the lock was acquired * @param lockQName the name of the previously-acquired lock * @param timeToLive the time (in milliseconds) for the lock to remain valid * @param callback the object that will be called at intervals of timeToLive/2 (about) * * @since 3.4.0a */ void refreshLock(String lockToken, QName lockQName, long timeToLive, JobLockRefreshCallback callback); /** * Release the lock using a valid lock token. * * @param lockToken the lock token returned when the lock was acquired * @param lockQName the name of the previously-acquired lock */ void releaseLock(String lockToken, QName lockQName); /** * Interface for implementations that need a timed callback in order to refresh the lock. *

* This callback is designed for processes that need to lock and wait for external processes * to complete; keeping a local thread to refresh the lock is possible but it is more * efficient for the thread pool and timer mechanisms to be shared. *

* The callback implementations must be thread-safe and should be independent * of other callbacks i.e. the simplest and safest is to use an anonymous inner class for * the implementation. *

* IMPORTANT: Do not block the calls to this interface - other callbacks might be held * up producing inconsistent behaviour. Failure to observe this will lead * to warnings and lock termination i.e. the service implementation will * force early termination of the lock and will discard the callback. * * @author Derek Hulley * @since 3.4.0b */ public interface JobLockRefreshCallback { /** * Timed callback from the service to determine if the lock is still required. *

* IMPORTANT: Do not block calls to this method for any reason and do perform any * non-trivial determination of state i.e. have the answer to this * method immediately available at all times. Failure to observe this * will lead to warnings and lock termination. *

* The original lock token is not provided in the callback; this is to prevent * implementations from attempting to link the lock token back to the specific callback * instances. * * @return Return true if the task associated with the callback * is still active i.e. it still needs the lock associated with the * callback or false if the lock is no longer required. * * @since 3.4.0b */ boolean isActive(); /** * Callback received when the lock refresh has failed. Implementations should immediately and * gracefully terminate their associated processes as the associated lock is no longer valid, * which is a direct indication that a competing process has taken and is using the required * lock or that the process has already completed and released the lock. *

* As a convenenience, this method is called when a VM shutdown is detected as well; the * associated lock is not refreshed and this method is called to instruct the locking process * to terminate. *

* This method is also called if the initiating process is self-terminated i.e. if the originating * process releases the lock itself. This method is not called if the process is not * {@link #isActive() active}. * * @since 3.4.0b */ void lockReleased(); } }