org.apache.jackrabbit.jcr2spi.lock.LockManagerImpl Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.jcr2spi.lock;
import org.apache.jackrabbit.jcr2spi.ItemManager;
import org.apache.jackrabbit.jcr2spi.SessionListener;
import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
import org.apache.jackrabbit.jcr2spi.config.CacheBehaviour;
import org.apache.jackrabbit.jcr2spi.hierarchy.NodeEntry;
import org.apache.jackrabbit.jcr2spi.operation.LockOperation;
import org.apache.jackrabbit.jcr2spi.operation.LockRefresh;
import org.apache.jackrabbit.jcr2spi.operation.LockRelease;
import org.apache.jackrabbit.jcr2spi.operation.Operation;
import org.apache.jackrabbit.jcr2spi.state.ItemState;
import org.apache.jackrabbit.jcr2spi.state.ItemStateLifeCycleListener;
import org.apache.jackrabbit.jcr2spi.state.NodeState;
import org.apache.jackrabbit.jcr2spi.state.PropertyState;
import org.apache.jackrabbit.jcr2spi.state.Status;
import org.apache.jackrabbit.spi.LockInfo;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Date;
/**
* LockManagerImpl
...
* TODO: TOBEFIXED. Lock objects obtained through this mgr are not informed if another session is or becomes lock-holder and removes the lock again.
*/
public class LockManagerImpl implements LockStateManager, SessionListener {
private static Logger log = LoggerFactory.getLogger(LockManagerImpl.class);
private static final long TIMEOUT_EXPIRED = -1;
private static final long TIMEOUT_INFINITE = Long.MAX_VALUE;
/**
* WorkspaceManager used to apply and release locks as well as to retrieve
* Lock information for a given NodeState.
* NOTE: The workspace manager must not be used as ItemStateManager.
*/
private final WorkspaceManager wspManager;
private final ItemManager itemManager;
private final CacheBehaviour cacheBehaviour;
/**
* Map holding all locks that where created by this Session
upon
* calls to {@link LockStateManager#lock(NodeState,boolean,boolean)} or to
* {@link LockStateManager#getLock(NodeState)}. The map entries are removed
* only if a lock ends his life by {@link Node#unlock()} or by implicit
* unlock upon {@link Session#logout()}.
*/
private final Map lockMap;
public LockManagerImpl(WorkspaceManager wspManager, ItemManager itemManager,
CacheBehaviour cacheBehaviour) {
this.wspManager = wspManager;
this.itemManager = itemManager;
this.cacheBehaviour = cacheBehaviour;
// use hard references in order to make sure, that entries referring
// to locks created by the current session are not removed.
lockMap = new HashMap();
}
//----------------< org.apache.jackrabbit.jcr2spi.lock.LockStateManager >---
/**
* @see LockStateManager#lock(NodeState,boolean,boolean)
*/
public Lock lock(NodeState nodeState, boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException {
return lock(nodeState, isDeep, isSessionScoped, Long.MAX_VALUE, null);
}
/**
* @see LockStateManager#lock(NodeState,boolean,boolean,long,String)
*/
public Lock lock(NodeState nodeState, boolean isDeep, boolean isSessionScoped, long timeoutHint, String ownerHint) throws RepositoryException {
// retrieve node first
Node lhNode;
Item item = itemManager.getItem(nodeState.getHierarchyEntry());
if (item.isNode()) {
lhNode = (Node) item;
} else {
throw new RepositoryException("Internal error: ItemManager returned Property from NodeState");
}
// execute the operation
LockOperation op = LockOperation.create(nodeState, isDeep, isSessionScoped, timeoutHint, ownerHint);
wspManager.execute(op);
Lock lock = new LockImpl(new LockState(nodeState, op.getLockInfo()), lhNode);
return lock;
}
/**
* @see LockStateManager#unlock(NodeState)
*/
public void unlock(NodeState nodeState) throws LockException, RepositoryException {
// execute the operation. Note, that its possible that the session is
// lock holder and still the lock was never accessed. thus the lockMap
// does not provide sufficient and reliable information.
Operation op = LockRelease.create(nodeState);
wspManager.execute(op);
// if unlock was successful: clean up lock map and lock life cycle
// in case the corresponding Lock object exists (and thus has been
// added to the map.
if (lockMap.containsKey(nodeState)) {
LockImpl l = lockMap.remove(nodeState);
l.lockState.unlocked();
}
}
/**
* If the session created a lock on the node with the given state, we already
* know the lock. Otherwise, the node state and its ancestors are searched
* for properties indicating a lock.
* Note, that the flag indicating session-scoped lock cannot be retrieved
* unless the current session is the lock holder.
*
* @see LockStateManager#getLock(NodeState)
* @param nodeState
*/
public Lock getLock(NodeState nodeState) throws LockException, RepositoryException {
LockImpl l = getLockImpl(nodeState, false);
// no-lock found or lock doesn't apply to this state -> throw
if (l == null) {
throw new LockException("Node with id '" + nodeState.getNodeId() + "' is not locked.");
}
// a lock exists either on the given node state or as deep lock inherited
// from any of the ancestor states.
return l;
}
/**
* @see LockStateManager#isLocked(NodeState)
*/
public boolean isLocked(NodeState nodeState) throws RepositoryException {
LockImpl l = getLockImpl(nodeState, false);
return l != null;
}
/**
* @see LockStateManager#checkLock(NodeState)
*/
public void checkLock(NodeState nodeState) throws LockException, RepositoryException {
// shortcut: new status indicates that a new state was already added
// thus, the parent state is not locked by foreign lock.
if (nodeState.getStatus() == Status.NEW) {
return;
}
LockImpl l = getLockImpl(nodeState, true);
if (l != null && !l.isLockOwningSession()) {
// lock is present and token is null -> session is not lock-holder.
throw new LockException("Node with id '" + nodeState + "' is locked.");
} // else: state is not locked at all || session is lock-holder
}
/**
* Returns the lock tokens present on the SessionInfo
this
* manager has been created with.
*
* @see LockStateManager#getLockTokens()
*/
public String[] getLockTokens() throws UnsupportedRepositoryOperationException, RepositoryException {
return wspManager.getLockTokens();
}
/**
* Delegates this call to {@link WorkspaceManager#addLockToken(String)}.
* If this succeeds this method will inform all locks stored in the local
* map in order to give them the chance to update their lock information.
*
* @see LockStateManager#addLockToken(String)
*/
public void addLockToken(String lt) throws LockException, RepositoryException {
wspManager.addLockToken(lt);
notifyTokenAdded(lt);
}
/**
* If the lock addressed by the token is session-scoped, this method will
* throw a LockException, such as defined by JSR170 v.1.0.1 for
* {@link Session#removeLockToken(String)}.
Otherwise the call is
* delegated to {@link WorkspaceManager#removeLockToken(String)}.
* All locks stored in the local lock map are notified by the removed
* token in order have them updated their lock information.
*
* @see LockStateManager#removeLockToken(String)
*/
public void removeLockToken(String lt) throws LockException, RepositoryException {
// JSR170 v. 1.0.1 defines that the token of a session-scoped lock may
// not be moved over to another session. Thus removal is not possible
// and the lock is always present in the lock map.
Iterator it = lockMap.values().iterator();
boolean found = false;
// loop over cached locks to determine if the token belongs to a session
// scoped lock, in which case the removal must fail immediately.
while (it.hasNext() && !found) {
LockImpl l = it.next();
if (lt.equals(l.getLockToken())) {
// break as soon as the lock associated with the given token was found.
found = true;
if (l.isSessionScoped()) {
throw new LockException("Cannot remove lock token associated with a session scoped lock.");
}
}
}
// remove lock token from sessionInfo. call will fail, if the session
// is not lock holder.
wspManager.removeLockToken(lt);
// inform about this lt being removed from this session
notifyTokenRemoved(lt);
}
//----------------------------------------------------< SessionListener >---
/**
* @see SessionListener#loggingOut(Session)
*/
public void loggingOut(Session session) {
// remove any session scoped locks:
NodeState[] lhStates = lockMap.keySet().toArray(new NodeState[lockMap.size()]);
for (NodeState nState : lhStates) {
LockImpl l = lockMap.get(nState);
if (l.isSessionScoped() && l.isLockOwningSession()) {
try {
unlock(nState);
} catch (RepositoryException e) {
log.warn("Error while unlocking session scoped lock. Cleaning up local lock status.");
// at least clean up local lock map and the locks life cycle
l.lockState.unlocked();
}
}
}
}
/**
* @see SessionListener#loggedOut(Session)
*/
public void loggedOut(Session session) {
// release all remaining locks without modifying their lock status
LockImpl[] locks = lockMap.values().toArray(new LockImpl[lockMap.size()]);
for (LockImpl lock : locks) {
lock.lockState.release();
}
}
//------------------------------------------------------------< private >---
/**
* Search nearest ancestor that is locked. Returns null
if neither
* the given state nor any of its ancestors is locked.
* Note, that this methods does NOT check if the given node state would
* be affected by the lock present on an ancestor state.
* Note, that in certain cases it might not be possible to detect a lock
* being present due to the fact that the hierarchy might be incomplete or
* not even readable completely. For this reason it seem equally reasonable
* to search for jcr:lockIsDeep property only and omitting all kind of
* verification regarding nodetypes present.
*
* @param nodeState NodeState
from which searching starts.
* Note, that the given state must not have an overlaid state.
* @return a state holding a lock or null
if neither the
* given state nor any of its ancestors is locked.
*/
private NodeState getLockHoldingState(NodeState nodeState) {
NodeEntry entry = nodeState.getNodeEntry();
while (!entry.hasPropertyEntry(NameConstants.JCR_LOCKISDEEP)) {
NodeEntry parent = entry.getParent();
if (parent == null) {
// reached root state without finding a locked node
return null;
}
entry = parent;
}
try {
return entry.getNodeState();
} catch (RepositoryException e) {
// may occur if the nodeState is not accessible or some generic
// error occurred.
// for this case, assume that no lock exists and delegate final
// validation to the spi-implementation.
log.warn("Error while accessing lock holding NodeState: {}", e.getMessage());
return null;
}
}
private LockState buildLockState(NodeState nodeState) throws RepositoryException {
NodeId nId = nodeState.getNodeId();
NodeState lockHoldingState;
LockInfo lockInfo = wspManager.getLockInfo(nId);
if (lockInfo == null) {
// no lock present
return null;
}
NodeId lockNodeId = lockInfo.getNodeId();
if (lockNodeId.equals(nId)) {
lockHoldingState = nodeState;
} else {
NodeEntry lockedEntry = wspManager.getHierarchyManager().getNodeEntry(lockNodeId);
try {
lockHoldingState = lockedEntry.getNodeState();
} catch (RepositoryException e) {
log.warn("Cannot build LockState");
throw new RepositoryException("Cannot build LockState", e);
}
}
if (lockHoldingState == null) {
return null;
} else {
return new LockState(lockHoldingState, lockInfo);
}
}
/**
* Returns the Lock that applies to the given node state (directly or
* by an inherited deep lock) or null
if the state is not
* locked at all.
*
* @param nodeState
* @param lazyLockDiscovery If true, no extra check with the server is made in order to
* determine, whether there is really no lock present. Otherwise, the server
* is asked if a lock is present.
* @return LockImpl that applies to the given state or null
.
* @throws RepositoryException
*/
private LockImpl getLockImpl(NodeState nodeState, boolean lazyLockDiscovery) throws RepositoryException {
NodeState nState = nodeState;
// access first non-NEW state
while (nState.getStatus() == Status.NEW) {
nState = nState.getParent();
}
// shortcut: check if a given state holds a lock, which has been
// store in the lock map. see below (LockImpl) for the conditions that
// must be met in order a lock can be stored.
LockImpl l = getLockFromMap(nState);
if (l != null && l.lockState.appliesToNodeState(nodeState)) {
return l;
}
LockState lState;
if (lazyLockDiscovery) {
// try to retrieve a state (ev. a parent state) that holds a lock.
NodeState lockHoldingState = getLockHoldingState(nState);
if (lockHoldingState == null) {
// assume no lock is present (might not be correct due to incomplete hierarchy)
return null;
} else {
// check lockMap again with the lock-holding state
l = getLockFromMap(nState);
if (l != null) {
return l;
}
lState = buildLockState(lockHoldingState);
}
} else {
// need precise information about lock status -> retrieve lockInfo
// from the persistent layer.
lState = buildLockState(nState);
}
if (lState != null) {
// Test again if a Lock object is stored in the lockmap. Otherwise
// build the lock object and retrieve lock holding node. note that this
// may fail if the session does not have permission to see this node.
LockImpl lock = getLockFromMap(lState.lockHoldingState);
if (lock != null) {
lock.lockState.setLockInfo(lState.lockInfo);
} else {
Item lockHoldingNode = itemManager.getItem(lState.lockHoldingState.getHierarchyEntry());
lock = new LockImpl(lState, (Node)lockHoldingNode);
}
// test if lock applies to the original nodestate
if (lState.appliesToNodeState(nodeState)) {
return lock;
} else {
return null; // lock exists but doesn't apply to the given state
}
} else {
// no lock at all
return null;
}
}
private LockImpl getLockFromMap(NodeState nodeState) {
try {
LockImpl l = lockMap.get(nodeState);
if (l != null && l.isLive()) {
return l;
}
} catch (RepositoryException e) {
// ignore
}
return null;
}
//----------------------------< Notification about modified lock-tokens >---
/**
* Notify all Lock
s that have been accessed so far about the
* new lock token present on the session and allow them to reload their
* lock info.
*
* @param lt
* @throws RepositoryException
*/
private void notifyTokenAdded(String lt) throws RepositoryException {
LockTokenListener[] listeners = lockMap.values().toArray(new LockTokenListener[lockMap.size()]);
for (LockTokenListener listener : listeners) {
listener.lockTokenAdded(lt);
}
}
/**
* Notify all Lock
s that have been accessed so far about the
* removed lock token and allow them to reload their lock info, if necessary.
*
* @param lt
* @throws RepositoryException
*/
private void notifyTokenRemoved(String lt) throws RepositoryException {
LockTokenListener[] listeners = lockMap.values().toArray(new LockTokenListener[lockMap.size()]);
for (LockTokenListener listener : listeners) {
listener.lockTokenRemoved(lt);
}
}
//--------------------------------------------------------------------------
private class LockState implements ItemStateLifeCycleListener {
private final NodeState lockHoldingState;
private LockInfo lockInfo;
private boolean isLive = true;
private long expiration = TIMEOUT_INFINITE;
private LockState(NodeState lockHoldingState, LockInfo lockInfo) {
this.lockHoldingState = lockHoldingState;
setLockInfo(lockInfo);
}
private void refresh() throws RepositoryException {
// lock is still alive -> send refresh-lock operation.
Operation op = LockRefresh.create(lockHoldingState);
wspManager.execute(op);
}
/**
* Returns true, if the given node state is the lockholding state of
* this Lock object OR if this Lock is deep.
* Note, that in the latter case this method does not assert, that the
* given node state is a child state of the lockholding state.
*
* @param nodeState that must be the same or a child of the lock holding
* state stored within this lock object.
* @return true if this lock applies to the given node state.
*/
private boolean appliesToNodeState(NodeState nodeState) {
if (nodeState.getStatus() == Status.NEW) {
return lockInfo.isDeep();
} else {
if (lockHoldingState == nodeState) {
return true;
} else {
return lockInfo != null && lockInfo.isDeep();
}
}
}
/**
* Reload the lockInfo from the server.
*
* @throws RepositoryException
*/
private void reloadLockInfo() throws RepositoryException {
NodeId nId = lockHoldingState.getNodeEntry().getWorkspaceId();
lockInfo = wspManager.getLockInfo(nId);
if (lockInfo == null) {
// lock has been released on the server
unlocked();
}
}
private void setLockInfo(LockInfo lockInfo) {
this.lockInfo = lockInfo;
long seconds = lockInfo.getSecondsRemaining();
if (seconds <= TIMEOUT_EXPIRED) {
expiration = TIMEOUT_EXPIRED;
isLive = false;
} else if (seconds < TIMEOUT_INFINITE) {
// calculate timeout
expiration = new Date().getTime()/1000 + lockInfo.getSecondsRemaining();
} else {
expiration = TIMEOUT_INFINITE;
}
}
/**
* @return true
if the lock is still alive.
*/
private boolean isLive() {
if (isLive) {
isLive = getSecondsRemaining() > 0;
}
return isLive;
}
/**
* @return the number of seconds until the lock's timeout is reached,
* {@link Long#MAX_VALUE} if timeout is infinite or undefined and
* a negative value if timeout has already been reached or the lock
* has been otherwise released.
*/
private long getSecondsRemaining() {
if (!isLive) {
return TIMEOUT_EXPIRED;
} else if (expiration == TIMEOUT_INFINITE) {
return expiration;
} else {
long seconds = expiration - new Date().getTime()/1000;
if (seconds <= 0) {
isLive = false;
return TIMEOUT_EXPIRED;
} else {
return seconds;
}
}
}
/**
* Release this lock by removing from the lock map and unregistering
* it from event listening
*/
private void release() {
if (lockMap.containsKey(lockHoldingState)) {
lockMap.remove(lockHoldingState);
}
stopListening();
}
/**
* This lock has been removed by the current Session or by an external
* unlock request. Since a lock will never come back to life after
* unlocking, it is released an its status is reset accordingly.
*/
private void unlocked() {
if (isLive()) {
release();
isLive = false;
}
}
private void startListening() {
// LockState must be aware of removal of the Node.
lockHoldingState.addListener(this);
// in case of CacheBehaviour.OBSERVATION this lockstate can also
// be aware of another session removing the lock -> listen to
// status changes of the jcr:lockIsDeep property.
if (cacheBehaviour == CacheBehaviour.OBSERVATION) {
try {
if (!lockHoldingState.hasPropertyName(NameConstants.JCR_LOCKISDEEP)) {
// force reloading of the lock holding node.
itemManager.getItem(lockHoldingState.getNodeEntry());
}
PropertyState ps = lockHoldingState.getPropertyState(NameConstants.JCR_LOCKISDEEP);
ps.addListener(this);
} catch (RepositoryException e) {
log.warn("Unable to retrieve jcr:isDeep property after lock creation. {}", e.getMessage());
}
}
}
private void stopListening() {
lockHoldingState.removeListener(this);
if (cacheBehaviour == CacheBehaviour.OBSERVATION) {
try {
if (lockHoldingState.hasPropertyName(NameConstants.JCR_LOCKISDEEP)) {
PropertyState ps = lockHoldingState.getPropertyState(NameConstants.JCR_LOCKISDEEP);
ps.removeListener(this);
}
} catch (ItemNotFoundException e) {
log.debug("jcr:lockIsDeep doesn't exist any more.");
} catch (Exception e) {
log.warn(e.getMessage());
}
}
}
//-------------------------------------< ItemStateLifeCycleListener >---
/**
* @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
*/
public void statusChanged(ItemState state, int previousStatus) {
if (!isLive()) {
// since we only monitor the removal of the lock (by means
// of deletion of the jcr:lockIsDeep property, we are not interested
// if the lock is not active any more.
return;
}
switch (state.getStatus()) {
case Status.REMOVED:
// this lock has been release by someone else (and not by
// a call to LockManager#unlock -> clean up and set isLive
// flag to false.
unlocked();
break;
default:
// not interested
}
}
}
//---------------------------------------------------------------< Lock >---
/**
* Inner class implementing the {@link Lock} interface.
*/
private class LockImpl implements javax.jcr.lock.Lock, LockTokenListener {
private final LockState lockState;
private final Node node;
private boolean reloadInfo = false; // TODO: find better solution
/**
*
* @param lockState
* Note, that the given state must not have an overlaid state.
* @param lockHoldingNode the lock holding Node
itself.
*/
public LockImpl(LockState lockState, Node lockHoldingNode) {
this.lockState = lockState;
this.node = lockHoldingNode;
// if observation is supported OR if this is a session-scoped lock
// hold by this session -> store lock in the map
if (cacheBehaviour == CacheBehaviour.OBSERVATION) {
lockMap.put(lockState.lockHoldingState, this);
lockState.startListening();
} else if (lockState.lockInfo.isLockOwner()) {
lockMap.put(lockState.lockHoldingState, this);
lockState.startListening();
// open-scoped locks: the map entry and the lock information
// stored therein may become outdated if the token is transferred
// to another session -> info must be reloaded.
if (!isSessionScoped()) {
reloadInfo = true;
}
} else {
// foreign lock: info must be reloaded.
reloadInfo = true;
}
}
/**
* @see Lock#getLockOwner()
*/
public String getLockOwner() {
LockInfo info = getLockInfo();
if (info != null) {
return info.getOwner();
} else {
return null;
}
}
/**
* @see Lock#isDeep()
*/
public boolean isDeep() {
LockInfo info = getLockInfo();
return info != null && info.isDeep();
}
/**
* @see Lock#getNode()
*/
public Node getNode() {
return node;
}
/**
* @see Lock#getLockToken()
*/
public String getLockToken() {
// shortcut for jsr 283 session scoped locks: they never expose
// the lock token to the API users.
if (isSessionScoped()) {
return null;
}
updateLockInfo();
LockInfo info = getLockInfo();
if (info != null) {
return info.getLockToken();
} else {
return null;
}
}
/**
* @see Lock#isLive()
*/
public boolean isLive() throws RepositoryException {
updateLockInfo();
return lockState.isLive();
}
/**
* @see Lock#isSessionScoped()
*/
public boolean isSessionScoped() {
LockInfo info = getLockInfo();
return info != null && info.isSessionScoped();
}
/**
* @see Lock#refresh()
*/
public void refresh() throws LockException, RepositoryException {
if (!isLive()) {
throw new LockException("Lock is not alive any more.");
}
if (!isLockOwningSession()) {
// shortcut, since lock is always updated if the session became
// lock-holder of a foreign lock.
throw new LockException("Session does not hold lock.");
} else {
lockState.refresh();
}
}
/**
* @see javax.jcr.lock.Lock#getSecondsRemaining()
*/
public long getSecondsRemaining() throws RepositoryException {
updateLockInfo();
return lockState.getSecondsRemaining();
}
/**
* @see javax.jcr.lock.Lock#isLockOwningSession()
*/
public boolean isLockOwningSession(){
LockInfo info = getLockInfo();
return info != null && info.isLockOwner();
}
//----------------------------------------------< LockTokenListener >---
/**
* A lock token as been added to the current Session. If this Lock
* object is not yet hold by the Session (thus does not know whether
* the new lock token belongs to it), it must reload the LockInfo
* from the server.
*
* @param lockToken
* @throws RepositoryException
* @see LockTokenListener#lockTokenAdded(String)
*/
public void lockTokenAdded(String lockToken) throws RepositoryException {
if (!isSessionScoped() && !isLockOwningSession()) {
// unless this lock is session-scoped (token is never transferred)
// and the session isn't the owner yet (token already present),
// it could be that this affects this lock and session became
// lock holder -> reload info to assert.
lockState.reloadLockInfo();
}
}
/**
*
* @param lockToken
* @throws LockException
* @see LockTokenListener#lockTokenRemoved(String)
*/
public void lockTokenRemoved(String lockToken) throws RepositoryException {
// reload lock info, if session gave away its lock-holder status
// for this lock. this will never be true for session-scoped locks
// that are not exposed (thus cannot be removed).
if (lockToken.equals(getLockToken())) {
lockState.reloadLockInfo();
}
}
//--------------------------------------------------------< private >---
/**
* @return LockInfo
stored within the LockState
*/
private LockInfo getLockInfo() {
return lockState.lockInfo;
}
/**
* Make sure the lock info is really up to date.
* TODO: find better solution.
*/
private void updateLockInfo() {
if (reloadInfo) {
try {
lockState.reloadLockInfo();
} catch (RepositoryException e) {
// may occur if session has been logged out. rather throw?
log.warn("Unable to determine lock status. {}", e.getMessage());
}
} // else: nothing to do.
}
}
//--------------------------------------------------< LockTokenListener >---
/**
*
*/
private interface LockTokenListener {
/**
*
* @param lockToken
* @throws LockException
* @throws RepositoryException
*/
void lockTokenAdded(String lockToken) throws RepositoryException;
/**
*
* @param lockToken
* @throws LockException
* @throws RepositoryException
*/
void lockTokenRemoved(String lockToken) throws RepositoryException;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy