/*
* 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.core.lock;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.data.core.InternalXAResource;
import org.apache.jackrabbit.data.core.TransactionContext;
import org.apache.jackrabbit.data.core.TransactionException;
import org.apache.jackrabbit.spi.Path;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
/**
* Session-local lock manager that implements the semantical changes inside
* transactions. This manager validates lock/unlock operations inside its
* view of the locking space.
*/
public class XALockManager implements LockManager, InternalXAResource {
/**
* Attribute name for XA Environment.
*/
private static final String XA_ENV_ATTRIBUTE_NAME = "XALockManager.XAEnv";
/**
* Global lock manager.
*/
private final LockManagerImpl lockMgr;
/**
* Current XA environment.
*/
private XAEnvironment xaEnv;
/**
* Create a new instance of this class.
* @param lockMgr lockMgr global lock manager
*/
public XALockManager(LockManagerImpl lockMgr) {
this.lockMgr = lockMgr;
}
//----------------------------------------------------------< LockManager >
/**
* {@inheritDoc}
*/
public Lock lock(NodeImpl node, boolean isDeep, boolean isSessionScoped)
throws LockException, RepositoryException {
return lock(node, isDeep, isSessionScoped, Long.MAX_VALUE, null);
}
/**
* @see LockManager#lock(NodeImpl, boolean, boolean, long, String)
*/
public Lock lock(NodeImpl node, boolean isDeep, boolean isSessionScoped, long timoutHint, String ownerInfo)
throws LockException, RepositoryException {
LockInfo info;
if (isInXA()) {
info = xaEnv.lock(node, isDeep, isSessionScoped, timoutHint, ownerInfo);
} else {
info = lockMgr.internalLock(node, isDeep, isSessionScoped, timoutHint, ownerInfo);
}
lockMgr.writeLockProperties(node, info.getLockOwner(), info.isDeep());
return new XALockImpl(this, info, node);
}
/**
* {@inheritDoc}
*/
public Lock getLock(NodeImpl node) throws LockException, RepositoryException {
LockInfo info;
if (isInXA()) {
info = xaEnv.getLockInfo(node);
} else {
info = lockMgr.getLockInfo(node.getNodeId());
}
if (info == null) {
throw new LockException("Node not locked: " + node);
}
SessionImpl session = (SessionImpl) node.getSession();
NodeImpl holder = (NodeImpl) session.getItemManager().getItem(info.getId());
return new XALockImpl(this, info, holder);
}
/**
* {@inheritDoc}
*/
public Lock[] getLocks(SessionImpl session) throws RepositoryException {
LockInfo[] infos;
if (isInXA()) {
infos = xaEnv.getLockInfos(session);
} else {
infos = lockMgr.getLockInfos(session);
}
XALockImpl[] locks = new XALockImpl[infos.length];
for (int i = 0; i < infos.length; i++) {
LockInfo info = infos[i];
NodeImpl holder = (NodeImpl) session.getItemManager().getItem(info.getId());
locks[i] = new XALockImpl(this, info, holder);
}
return locks;
}
/**
* {@inheritDoc}
*/
public void unlock(NodeImpl node) throws LockException, RepositoryException {
lockMgr.removeLockProperties(node);
if (isInXA()) {
xaEnv.unlock(node);
} else {
lockMgr.internalUnlock(node);
}
}
/**
* {@inheritDoc}
*/
public boolean holdsLock(NodeImpl node) throws RepositoryException {
LockInfo info;
if (isInXA()) {
info = xaEnv.getLockInfo(node);
} else {
info = lockMgr.getLockInfo(node.getNodeId());
}
return info != null && info.getId().equals(node.getId());
}
/**
* {@inheritDoc}
*/
public boolean isLocked(NodeImpl node) throws RepositoryException {
LockInfo info;
if (isInXA()) {
info = xaEnv.getLockInfo(node);
} else {
info = lockMgr.getLockInfo(node.getNodeId());
}
return info != null;
}
/**
* {@inheritDoc}
*/
public void checkLock(NodeImpl node) throws LockException, RepositoryException {
LockInfo info;
if (isInXA()) {
info = xaEnv.getLockInfo(node);
if (info != null && !info.isLockHolder(node.getSession())) {
throw new LockException("Node locked.");
}
} else {
lockMgr.checkLock(node);
}
}
/**
* {@inheritDoc}
*/
public void checkLock(Path path, Session session)
throws LockException, RepositoryException {
if (isInXA()) {
SessionImpl sessionImpl = (SessionImpl) session;
checkLock(sessionImpl.getItemManager().getNode(path));
} else {
lockMgr.checkLock(path, session);
}
}
/**
* {@inheritDoc}
*/
public void checkUnlock(Session session, NodeImpl node)
throws LockException, RepositoryException {
if (isInXA()) {
LockInfo info = xaEnv.getLockInfo(node);
if (info == null || !info.getId().equals(node.getId())) {
throw new LockException("Node not locked: " + node);
}
if (!info.isLockHolder(session)) {
throw new LockException("Node not locked by session: " + node);
}
} else {
lockMgr.checkUnlock(session, node);
}
}
/**
* {@inheritDoc}
*/
public void addLockToken(SessionImpl session, String lt) throws RepositoryException {
if (isInXA()) {
xaEnv.addLockToken(session, lt);
} else {
lockMgr.addLockToken(session, lt);
}
}
/**
* {@inheritDoc}
*/
public void removeLockToken(SessionImpl session, String lt) throws RepositoryException {
if (isInXA()) {
xaEnv.removeLockToken(session, lt);
} else {
lockMgr.removeLockToken(session, lt);
}
}
//-----------------------------------------------------------< transaction >
/**
* {@inheritDoc}
*/
public void associate(TransactionContext tx) {
XAEnvironment xaEnv = null;
if (tx != null) {
xaEnv = (XAEnvironment) tx.getAttribute(XA_ENV_ATTRIBUTE_NAME);
if (xaEnv == null) {
xaEnv = new XAEnvironment(lockMgr);
tx.setAttribute(XA_ENV_ATTRIBUTE_NAME, xaEnv);
}
}
this.xaEnv = xaEnv;
}
/**
* {@inheritDoc}
*/
public void beforeOperation(TransactionContext tx) {
}
/**
* {@inheritDoc}
*/
public void prepare(TransactionContext tx) throws TransactionException {
XAEnvironment xaEnv = (XAEnvironment) tx.getAttribute(XA_ENV_ATTRIBUTE_NAME);
if (xaEnv != null) {
xaEnv.prepare();
}
}
/**
* {@inheritDoc}
* <p>
* This will finish the update and unlock the shared lock manager.
*/
public void commit(TransactionContext tx) {
XAEnvironment xaEnv = (XAEnvironment) tx.getAttribute(XA_ENV_ATTRIBUTE_NAME);
if (xaEnv != null) {
xaEnv.commit();
}
}
/**
* {@inheritDoc}
* <p>
* This will undo all updates and unlock the shared lock manager.
*/
public void rollback(TransactionContext tx) {
XAEnvironment xaEnv = (XAEnvironment) tx.getAttribute(XA_ENV_ATTRIBUTE_NAME);
if (xaEnv != null) {
xaEnv.rollback();
}
}
/**
* {@inheritDoc}
*/
public void afterOperation(TransactionContext tx) {
}
/**
* Return a flag indicating whether a lock info belongs to a different
* XA environment.
*/
public boolean differentXAEnv(LockInfo info) {
if (isInXA()) {
return xaEnv.differentXAEnv(info);
} else {
return info instanceof XAEnvironment.XALockInfo;
}
}
/**
* Return a flag indicating whether this version manager is currently
* associated with an XA transaction.
*/
private boolean isInXA() {
return xaEnv != null;
}
}