You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2005/12/06 17:08:43 UTC
svn commit: r354456 [1/2] - in /incubator/jackrabbit/trunk/jackrabbit/src:
main/java/org/apache/jackrabbit/core/
main/java/org/apache/jackrabbit/core/lock/
main/java/org/apache/jackrabbit/core/state/
main/java/org/apache/jackrabbit/core/xml/ test/java/...
Author: dpfister
Date: Tue Dec 6 08:07:53 2005
New Revision: 354456
URL: http://svn.apache.org/viewcvs?rev=354456&view=rev
Log:
Make locking part of XAResource's transaction support
Added:
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java (with props)
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java (with props)
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java (with props)
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java (with props)
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java (with props)
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java (with props)
Removed:
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockInfo.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockManagerImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionContext.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionException.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionListener.java
Modified:
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/PathMap.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java
incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java
incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java
incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Tue Dec 6 08:07:53 2005
@@ -17,7 +17,6 @@
package org.apache.jackrabbit.core;
import org.apache.jackrabbit.BaseException;
-import org.apache.jackrabbit.core.lock.LockManager;
import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
import org.apache.jackrabbit.core.nodetype.NodeDef;
import org.apache.jackrabbit.core.nodetype.NodeDefId;
@@ -3730,8 +3729,7 @@
checkLockable();
- LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- return lockMgr.lock(this, isDeep, isSessionScoped);
+ return session.getLockManager().lock(this, isDeep, isSessionScoped);
}
/**
@@ -3743,13 +3741,10 @@
// check state of this instance
sanityCheck();
- // todo: fixme for transactions
- if (isTransactionalNew()) {
+ if (isNew()) {
throw new LockException("Node not locked: " + safeGetJCRPath());
}
-
- LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- return lockMgr.getLock(this);
+ return session.getLockManager().getLock(this);
}
/**
@@ -3770,9 +3765,7 @@
}
checkLockable();
-
- LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- lockMgr.unlock(this);
+ session.getLockManager().unlock(this);
}
/**
@@ -3782,14 +3775,11 @@
// check state of this instance
sanityCheck();
- // todo: fixme for transactions
- if (!isNodeType(QName.MIX_LOCKABLE) || isTransactionalNew()) {
+ if (!isNodeType(QName.MIX_LOCKABLE) || isNew()) {
// a node that is new or not lockable never holds a lock
return false;
}
-
- LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- return lockMgr.holdsLock(this);
+ return session.getLockManager().holdsLock(this);
}
/**
@@ -3799,13 +3789,10 @@
// check state of this instance
sanityCheck();
- // todo: fixme for transactions
- if (isTransactionalNew()) {
+ if (isNew()) {
return false;
}
-
- LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- return lockMgr.isLocked(this);
+ return session.getLockManager().isLocked(this);
}
/**
@@ -3830,14 +3817,11 @@
* @throws RepositoryException if some other error occurs
*/
protected void checkLock() throws LockException, RepositoryException {
- // todo: fixme for transactions
- if (isTransactionalNew()) {
- // a new node must not be checked
+ if (isNew()) {
+ // a new node needs no check
return;
}
-
- LockManager lockMgr = ((WorkspaceImpl) session.getWorkspace()).getLockManager();
- lockMgr.checkLock(this);
+ session.getLockManager().checkLock(this);
}
//--------------------------------------------------------< inner classes >
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/PathMap.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/PathMap.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/PathMap.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/PathMap.java Tue Dec 6 08:07:53 2005
@@ -318,6 +318,19 @@
}
/**
+ * Remove all children of this element. Removes this element itself
+ * if this element does not contain associated information.
+ */
+ public void removeAll() {
+ children = null;
+ childrenCount = 0;
+
+ if (obj == null && parent != null) {
+ parent.remove(getPathElement(), false);
+ }
+ }
+
+ /**
* Return the object associated with this element
* @return object associated with this element
*/
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Tue Dec 6 08:07:53 2005
@@ -27,7 +27,7 @@
import org.apache.jackrabbit.core.fs.FileSystemException;
import org.apache.jackrabbit.core.fs.FileSystemResource;
import org.apache.jackrabbit.core.lock.LockManager;
-import org.apache.jackrabbit.core.lock.LockManagerImpl;
+import org.apache.jackrabbit.core.lock.SharedLockManager;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.nodetype.virtual.VirtualNodeTypeStateManager;
@@ -1127,7 +1127,7 @@
/**
* Lock manager
*/
- private LockManagerImpl lockMgr;
+ private SharedLockManager lockMgr;
/**
* Creates a new <code>WorkspaceInfo</code> based on the given
@@ -1269,7 +1269,7 @@
*/
synchronized LockManager getLockManager() throws RepositoryException {
if (lockMgr == null) {
- lockMgr = new LockManagerImpl(getSystemSession(), config.getFileSystem());
+ lockMgr = new SharedLockManager(getSystemSession(), config.getFileSystem());
}
return lockMgr;
}
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SessionImpl.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/SessionImpl.java Tue Dec 6 08:07:53 2005
@@ -38,6 +38,7 @@
import org.apache.jackrabbit.core.xml.SessionImporter;
import org.apache.jackrabbit.core.xml.SysViewSAXEventGenerator;
import org.apache.jackrabbit.core.util.Dumpable;
+import org.apache.jackrabbit.core.lock.LockManager;
import org.apache.jackrabbit.name.MalformedPathException;
import org.apache.jackrabbit.name.NamespaceResolver;
import org.apache.jackrabbit.name.Path;
@@ -403,16 +404,6 @@
}
/**
- * Dispatch events belonging to a save operation.
- *
- * @param events events to dispatch as result of a successful save
- * operation
- */
- protected void dispatch(EventStateCollection events) {
- events.dispatch();
- }
-
- /**
* Returns the names of all workspaces of this repository with respect of the
* access rights of this session.
*
@@ -1240,7 +1231,7 @@
synchronized (lockTokens) {
if (lockTokens.add(lt) && notify) {
try {
- wsp.getLockManager().lockTokenAdded(this, lt);
+ getLockManager().lockTokenAdded(this, lt);
} catch (RepositoryException e) {
log.error("Lock manager not available.", e);
}
@@ -1275,12 +1266,23 @@
synchronized (lockTokens) {
if (lockTokens.remove(lt) && notify) {
try {
- wsp.getLockManager().lockTokenRemoved(this, lt);
+ getLockManager().lockTokenRemoved(this, lt);
} catch (RepositoryException e) {
log.error("Lock manager not available.", e);
}
}
}
+ }
+
+ /**
+ * Return the lock manager for this session. In a non-transactional
+ * environment, this is simply the lock manager shared by all sessions
+ * on this workspace.
+ * @return lock manager for this session
+ * @throws RepositoryException if an error occurs
+ */
+ public LockManager getLockManager() throws RepositoryException {
+ return wsp.getLockManager();
}
//-------------------------------------------------------------< Dumpable >
Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java?rev=354456&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java Tue Dec 6 08:07:53 2005
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed 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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents the transaction on behalf of the component that wants to
+ * explictely demarcate transcation boundaries.
+ */
+public class TransactionContext {
+
+ /**
+ * Transaction attributes
+ */
+ private final Map attributes = new HashMap();
+
+ /**
+ * Set an attribute on this transaction. If the value specified is
+ * <code>null</code>, it is semantically equivalent to
+ * {@link #removeAttribute}.
+ *
+ * @param name attribute name
+ * @param value attribute value
+ */
+ public void setAttribute(String name, Object value) {
+ if (value == null) {
+ removeAttribute(name);
+ }
+ attributes.put(name, value);
+ }
+
+ /**
+ * Return an attribute value on this transaction.
+ *
+ * @param name attribute name
+ * @return attribute value, <code>null</code> if no attribute with that
+ * name exists
+ */
+ public Object getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ /**
+ * Remove an attribute on this transaction.
+ *
+ * @param name attribute name
+ */
+ public void removeAttribute(String name) {
+ attributes.remove(name);
+ }
+}
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java?rev=354456&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java Tue Dec 6 08:07:53 2005
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed 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;
+
+import org.apache.jackrabbit.BaseException;
+
+/**
+ * TransactionException is thrown when some operation inside the transaction
+ * fails.
+ */
+public class TransactionException extends BaseException {
+
+ /**
+ * Creates an instance of this class. Takes a detail message as parameter.
+ *
+ * @param message message
+ */
+ public TransactionException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates an instance of this class. Takes a root throwable as parameter.
+ *
+ * @param rootCause root throwable
+ */
+ public TransactionException(Throwable rootCause) {
+ super(rootCause);
+ }
+
+ /**
+ * Creates an instance of this class. Takes a message and a root throwable
+ * as parameter.
+ *
+ * @param message message
+ * @param rootCause root throwable
+ */
+ public TransactionException(String message, Throwable rootCause) {
+ super(message, rootCause);
+ }
+}
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionException.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java?rev=354456&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java Tue Dec 6 08:07:53 2005
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed 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;
+
+import org.apache.jackrabbit.core.TransactionContext;
+
+/**
+ * Listener on a transaction. Will receive notifications about commit
+ * and rollback actions.
+ *
+ * @see org.apache.jackrabbit.core.TransactionContext
+ */
+public interface TransactionListener {
+
+ /**
+ * Transaction was committed
+ *
+ * @param tx transaction that was committed
+ */
+ void transactionCommitted(TransactionContext tx);
+
+ /**
+ * Transaction was rolled back
+ *
+ * @param tx transaction that was rolled back
+ */
+ void transactionRolledBack(TransactionContext tx);
+}
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionListener.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/WorkspaceImpl.java Tue Dec 6 08:07:53 2005
@@ -254,7 +254,7 @@
BatchedItemOperations ops =
new BatchedItemOperations(stateMgr, rep.getNodeTypeRegistry(),
- getLockManager(), session, hierMgr,
+ session.getLockManager(), session, hierMgr,
session.getNamespaceResolver());
try {
@@ -481,7 +481,7 @@
BatchedItemOperations ops =
new BatchedItemOperations(stateMgr, rep.getNodeTypeRegistry(),
- getLockManager(), session, hierMgr,
+ session.getLockManager(), session, hierMgr,
session.getNamespaceResolver());
try {
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XASessionImpl.java Tue Dec 6 08:07:53 2005
@@ -17,11 +17,12 @@
package org.apache.jackrabbit.core;
import org.apache.jackrabbit.core.config.WorkspaceConfig;
-import org.apache.jackrabbit.core.observation.EventStateCollection;
import org.apache.jackrabbit.core.security.AuthContext;
-import org.apache.jackrabbit.core.state.TransactionContext;
-import org.apache.jackrabbit.core.state.TransactionException;
-import org.apache.jackrabbit.core.state.TransactionListener;
+import org.apache.jackrabbit.core.lock.LockManager;
+import org.apache.jackrabbit.core.lock.TxLockManager;
+import org.apache.jackrabbit.core.lock.SharedLockManager;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.TransactionalItemStateManager;
import org.apache.log4j.Logger;
import javax.jcr.AccessDeniedException;
@@ -50,6 +51,16 @@
private static final Map txGlobal = new HashMap();
/**
+ * Known attribute name.
+ */
+ private static final String ATTRIBUTE_CHANGE_LOG = "ChangeLog";
+
+ /**
+ * Known attribute name.
+ */
+ private static final String ATTRIBUTE_LOCK_MANAGER = "LockManager";
+
+ /**
* Currently associated transaction
*/
private TransactionContext tx;
@@ -227,7 +238,33 @@
if (tx == null) {
throw new XAException(XAException.XAER_NOTA);
}
- return XA_OK;
+
+ TransactionalItemStateManager stateMgr = wsp.getItemStateManager();
+ stateMgr.setChangeLog(getChangeLog(tx), true);
+
+ try {
+ // 1. Prepare state manager
+ try {
+ stateMgr.prepare();
+ } catch (TransactionException e) {
+ throw new ExtendedXAException(XAException.XA_RBOTHER, e);
+ }
+
+ // 2. Prepare lock manager
+ try {
+ TxLockManager lockMgr = getTxLockManager(tx);
+ if (lockMgr != null) {
+ lockMgr.prepare();
+ }
+ } catch (TransactionException e) {
+ stateMgr.rollback();
+ throw new ExtendedXAException(XAException.XA_RBOTHER, e);
+ }
+ return XA_OK;
+
+ } finally {
+ stateMgr.setChangeLog(null, true);
+ }
}
/**
@@ -238,7 +275,23 @@
if (tx == null) {
throw new XAException(XAException.XAER_NOTA);
}
- wsp.getItemStateManager().rollback(tx);
+
+ TransactionalItemStateManager stateMgr = wsp.getItemStateManager();
+ stateMgr.setChangeLog(getChangeLog(tx), true);
+
+ try {
+ // 1. Rollback changes on lock manager
+ TxLockManager lockMgr = getTxLockManager(tx);
+ if (lockMgr != null) {
+ lockMgr.rollback();
+ }
+
+ // 2. Rollback changes on state manager
+ stateMgr.rollback();
+
+ } finally {
+ stateMgr.setChangeLog(null, true);
+ }
}
/**
@@ -250,10 +303,28 @@
throw new XAException(XAException.XAER_NOTA);
}
+ TransactionalItemStateManager stateMgr = wsp.getItemStateManager();
+ stateMgr.setChangeLog(getChangeLog(tx), true);
+
+ TxLockManager lockMgr = getTxLockManager(tx);
+
try {
- wsp.getItemStateManager().commit(tx);
- } catch (TransactionException e) {
- throw new ExtendedXAException(XAException.XA_RBOTHER, e);
+ // 1. Commit changes on state manager
+ try {
+ stateMgr.commit();
+ } catch (TransactionException e) {
+ if (lockMgr != null) {
+ lockMgr.rollback();
+ }
+ throw new ExtendedXAException(XAException.XA_RBOTHER, e);
+ }
+
+ // 2. Commit changes on lock manager
+ if (lockMgr != null) {
+ lockMgr.commit();
+ }
+ } finally {
+ stateMgr.setChangeLog(null, true);
}
}
@@ -282,7 +353,12 @@
void associate(TransactionContext tx) {
this.tx = tx;
- wsp.getItemStateManager().setTransactionContext(tx);
+ ChangeLog txLog = getChangeLog(tx);
+ if (txLog == null) {
+ txLog = new ChangeLog();
+ tx.setAttribute(ATTRIBUTE_CHANGE_LOG, txLog);
+ }
+ wsp.getItemStateManager().setChangeLog(txLog, false);
}
/**
@@ -303,59 +379,7 @@
void disassociate() {
tx = null;
- wsp.getItemStateManager().setTransactionContext(null);
- }
-
- /**
- * {@inheritDoc}
- * <p/>
- * If we are currently associated with a transaction, the dispatch operation
- * will be postponed until commit.
- */
- protected void dispatch(EventStateCollection events) {
- if (tx != null) {
- tx.addListener(new EventDispatcher(events));
- return;
- }
- super.dispatch(events);
- }
-
- /**
- * Internal {@link TransactionListener} implementation that will dispatch
- * events only when a transaction has actually been committed.
- */
- static class EventDispatcher implements TransactionListener {
-
- /**
- * Events to dispatch if transaction is committed
- */
- private final EventStateCollection events;
-
- /**
- * Create a new instance of this class.
- *
- * @param events events to dispatch on commit
- */
- public EventDispatcher(EventStateCollection events) {
- this.events = events;
- }
-
- /**
- * {@inheritDoc}
- * <p/>
- * Dispatch events.
- */
- public void transactionCommitted(TransactionContext tx) {
- events.dispatch();
- }
-
- /**
- * {@inheritDoc}
- * <p/>
- * Nothing to do.
- */
- public void transactionRolledBack(TransactionContext tx) {
- }
+ wsp.getItemStateManager().setChangeLog(null, false);
}
/**
@@ -391,5 +415,48 @@
initCause(cause);
}
}
+ }
+
+ //-------------------------------------------------------< locking support >
+
+ /**
+ * Return the lock manager for this session. In a transactional environment,
+ * this is a session-local object that records locking/unlocking operations
+ * until final commit.
+ *
+ * @return lock manager for this session
+ * @throws javax.jcr.RepositoryException if an error occurs
+ */
+ public LockManager getLockManager() throws RepositoryException {
+ if (tx != null) {
+ TxLockManager lockMgr = (TxLockManager) tx.getAttribute(ATTRIBUTE_LOCK_MANAGER);
+ if (lockMgr == null) {
+ lockMgr = new TxLockManager(
+ (SharedLockManager) super.getLockManager());
+ tx.setAttribute(ATTRIBUTE_LOCK_MANAGER, lockMgr);
+ }
+ return lockMgr;
+ }
+ return super.getLockManager();
+ }
+
+ /**
+ * Return the transactional change log for this session.
+ *
+ * @param tx transactional context
+ * @return change log for this session, may be <code>null</code>
+ */
+ private static ChangeLog getChangeLog(TransactionContext tx) {
+ return (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG);
+ }
+
+ /**
+ * Return the transactional lock manager for this session. Returns
+ * <code>null</code> if no lock manager has been used yet.
+ *
+ * @return lock manager for this session
+ */
+ private static TxLockManager getTxLockManager(TransactionContext tx) {
+ return (TxLockManager) tx.getAttribute(ATTRIBUTE_LOCK_MANAGER);
}
}
Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java?rev=354456&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java Tue Dec 6 08:07:53 2005
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed 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.SessionImpl;
+
+import javax.jcr.Session;
+
+/**
+ * Common information about a lock.
+ */
+abstract class AbstractLockInfo {
+
+ /**
+ * Lock token
+ */
+ protected final LockToken lockToken;
+
+ /**
+ * Flag indicating whether lock is session scoped
+ */
+ protected final boolean sessionScoped;
+
+ /**
+ * Flag indicating whether lock is deep
+ */
+ protected final boolean deep;
+
+ /**
+ * Lock owner, determined on creation time
+ */
+ protected final String lockOwner;
+
+ /**
+ * Session currently holding lock
+ */
+ protected SessionImpl lockHolder;
+
+ /**
+ * Flag indicating whether this lock is live
+ */
+ protected boolean live;
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param lockToken lock token
+ * @param sessionScoped whether lock token is session scoped
+ * @param deep whether lock is deep
+ * @param lockOwner owner of lock
+ */
+ public AbstractLockInfo(LockToken lockToken, boolean sessionScoped, boolean deep,
+ String lockOwner) {
+ this.lockToken = lockToken;
+ this.sessionScoped = sessionScoped;
+ this.deep = deep;
+ this.lockOwner = lockOwner;
+ }
+
+ /**
+ * Set the live flag
+ * @param live live flag
+ */
+ public void setLive(boolean live) {
+ this.live = live;
+ }
+
+ /**
+ * Return the UUID of the lock holding node
+ * @return uuid
+ */
+ public String getUUID() {
+ return lockToken.uuid;
+ }
+
+ /**
+ * Return the session currently holding the lock
+ *
+ * @return session currently holding the lock
+ */
+ public SessionImpl getLockHolder() {
+ return lockHolder;
+ }
+
+ /**
+ * Set the session currently holding the lock
+ *
+ * @param lockHolder session currently holding the lock
+ */
+ public void setLockHolder(SessionImpl lockHolder) {
+ this.lockHolder = lockHolder;
+ }
+
+ /**
+ * Return the lock token as seen by the session passed as parameter. If
+ * this session is currently holding the lock, it will get the lock token
+ * itself, otherwise a <code>null</code> string
+ */
+ public String getLockToken(Session session) {
+ if (session.equals(lockHolder)) {
+ return lockToken.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Return a flag indicating whether the lock is live
+ *
+ * @return <code>true</code> if the lock is live; otherwise <code>false</code>
+ */
+ public boolean isLive() {
+ return live;
+ }
+
+ /**
+ * Return a flag indicating whether the lock is session-scoped
+ *
+ * @return <code>true</code> if the lock is session-scoped;
+ * otherwise <code>false</code>
+ */
+ public boolean isSessionScoped() {
+ return sessionScoped;
+ }
+}
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/AbstractLockInfo.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/LockImpl.java Tue Dec 6 08:07:53 2005
@@ -30,7 +30,7 @@
/**
* Lock info containing latest information
*/
- private final LockInfo info;
+ private final AbstractLockInfo info;
/**
* Node holding lock
@@ -43,7 +43,7 @@
* @param info lock information
* @param node node holding lock
*/
- public LockImpl(LockInfo info, Node node) {
+ public LockImpl(AbstractLockInfo info, Node node) {
this.info = info;
this.node = node;
}
Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java?rev=354456&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java Tue Dec 6 08:07:53 2005
@@ -0,0 +1,973 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed 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.commons.collections.map.LinkedMap;
+import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+import org.apache.jackrabbit.core.observation.EventImpl;
+import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.QName;
+import org.apache.log4j.Logger;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import EDU.oswego.cs.dl.util.concurrent.ReentrantLock;
+
+
+/**
+ * Provides the functionality needed for locking and unlocking nodes.
+ */
+public class SharedLockManager implements LockManager, SynchronousEventListener {
+
+ /**
+ * Logger
+ */
+ private static final Logger log = Logger.getLogger(SharedLockManager.class);
+
+ /**
+ * Name of the lock file
+ */
+ private static final String LOCKS_FILE = "locks";
+
+ /**
+ * Path map containing all locks at the leaves.
+ */
+ private final PathMap lockMap = new PathMap();
+
+ /**
+ * Lock to path map.
+ */
+ private final ReentrantLock lockMapLock = new ReentrantLock();
+
+ /**
+ * System session
+ */
+ private final SessionImpl session;
+
+ /**
+ * Locks file
+ */
+ private final FileSystemResource locksFile;
+
+ /**
+ * Flag indicating whether automatic saving is disabled.
+ */
+ private boolean savingDisabled;
+
+ /**
+ * Monitor used when modifying content, too, in order to make modifications
+ * in the lock map and modifications in the content atomic.
+ */
+ private final Object contentMonitor = new Object();
+
+ /**
+ * Namespace resolver
+ */
+ private final NamespaceResolver nsResolver;
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param session system session
+ * @param fs file system for persisting locks
+ * @throws RepositoryException if an error occurs
+ */
+ public SharedLockManager(SessionImpl session, FileSystem fs)
+ throws RepositoryException {
+
+ this.session = session;
+ this.nsResolver = session.getNamespaceResolver();
+ this.locksFile = new FileSystemResource(fs, FileSystem.SEPARATOR + LOCKS_FILE);
+
+ session.getWorkspace().getObservationManager().
+ addEventListener(this, Event.NODE_ADDED | Event.NODE_REMOVED,
+ "/", true, null, null, true);
+
+ try {
+ if (locksFile.exists()) {
+ load();
+ }
+ } catch (FileSystemException e) {
+ throw new RepositoryException("I/O error while reading locks from '"
+ + locksFile.getPath() + "'", e);
+ }
+ }
+
+ /**
+ * Close this lock manager. Writes back all changes.
+ */
+ public void close() {
+ save();
+ }
+
+ /**
+ * Read locks from locks file and populate path map
+ */
+ private void load() throws FileSystemException {
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(
+ new InputStreamReader(locksFile.getInputStream()));
+ while (true) {
+ String s = reader.readLine();
+ if (s == null || s.equals("")) {
+ break;
+ }
+ reapplyLock(LockToken.parse(s));
+ }
+ } catch (IOException e) {
+ throw new FileSystemException("error while reading locks file", e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e2) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ /**
+ * Reapply a lock given a lock token that was read from the locks file
+ *
+ * @param lockToken lock token to apply
+ */
+ private void reapplyLock(LockToken lockToken) {
+ try {
+ NodeId id = new NodeId(lockToken.uuid);
+
+ NodeImpl node = (NodeImpl) session.getItemManager().getItem(id);
+ Path path = getPath(node.getId());
+
+ LockInfo info = new LockInfo(lockToken, false,
+ node.getProperty(QName.JCR_LOCKISDEEP).getBoolean(),
+ node.getProperty(QName.JCR_LOCKOWNER).getString());
+ info.setLive(true);
+ lockMap.put(path, info);
+ } catch (RepositoryException e) {
+ log.warn("Unable to recreate lock '" + lockToken
+ + "': " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * Write locks to locks file
+ */
+ private void save() {
+ if (savingDisabled) {
+ return;
+ }
+
+ final ArrayList list = new ArrayList();
+
+ lockMap.traverse(new PathMap.ElementVisitor() {
+ public void elementVisited(PathMap.Element element) {
+ LockInfo info = (LockInfo) element.get();
+ if (!info.sessionScoped) {
+ list.add(info);
+ }
+ }
+ }, false);
+
+
+ BufferedWriter writer = null;
+
+ try {
+ writer = new BufferedWriter(
+ new OutputStreamWriter(locksFile.getOutputStream()));
+ for (int i = 0; i < list.size(); i++) {
+ AbstractLockInfo info = (AbstractLockInfo) list.get(i);
+ writer.write(info.lockToken.toString());
+ writer.newLine();
+ }
+ } catch (FileSystemException fse) {
+ log.warn("I/O error while saving locks to '"
+ + locksFile.getPath() + "': " + fse.getMessage());
+ log.debug("Root cause: ", fse);
+ } catch (IOException ioe) {
+ log.warn("I/O error while saving locks to '"
+ + locksFile.getPath() + "': " + ioe.getMessage());
+ log.debug("Root cause: ", ioe);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Internal <code>lock</code> implementation that takes the same parameters
+ * as the public method but will not modify content.
+ * @param node node to lock
+ * @param isDeep whether the lock applies to this node only
+ * @param isSessionScoped whether the lock is session scoped
+ * @return lock
+ * @throws LockException if the node is already locked
+ * @throws RepositoryException if another error occurs
+ */
+ Lock internalLock(NodeImpl node, boolean isDeep, boolean isSessionScoped)
+ throws LockException, RepositoryException {
+
+ SessionImpl session = (SessionImpl) node.getSession();
+ LockInfo info = new LockInfo(new LockToken(node.internalGetUUID()),
+ isSessionScoped, isDeep, session.getUserID());
+
+ acquire();
+
+ try {
+ // check whether node is already locked
+ Path path = getPath(node.getId());
+ PathMap.Element element = lockMap.map(path, false);
+
+ LockInfo other = (LockInfo) element.get();
+ if (other != null) {
+ if (element.hasPath(path)) {
+ throw new LockException("Node already locked: " + node.safeGetJCRPath());
+ } else if (other.deep) {
+ throw new LockException("Parent node has deep lock.");
+ }
+ }
+ if (info.deep && element.hasPath(path) &&
+ element.getChildrenCount() > 0) {
+ throw new LockException("Some child node is locked.");
+ }
+
+ // create lock token
+ info.setLockHolder(session);
+ info.setLive(true);
+ session.addListener(info);
+ session.addLockToken(info.lockToken.toString(), false);
+ lockMap.put(path, info);
+
+ if (!info.sessionScoped) {
+ save();
+ }
+ return new LockImpl(info, node);
+
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Unlock a node (internal implementation)
+ * @param node node to unlock
+ * @throws LockException if the node can not be unlocked
+ * @throws RepositoryException if another error occurs
+ */
+ void internalUnlock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ acquire();
+
+ try {
+ SessionImpl session = (SessionImpl) node.getSession();
+
+ // check whether node is locked by this session
+ PathMap.Element element = lockMap.map(
+ getPath(node.getId()), true);
+ if (element == null) {
+ throw new LockException("Node not locked: " + node.safeGetJCRPath());
+ }
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info == null) {
+ throw new LockException("Node not locked: " + node.safeGetJCRPath());
+ }
+ if (!session.equals(info.getLockHolder())) {
+ throw new LockException("Node not locked by session: " + node.safeGetJCRPath());
+ }
+
+ element.set(null);
+ info.setLive(false);
+
+ if (!info.sessionScoped) {
+ save();
+ }
+
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Unlock a node given by its info. Invoked when a session logs out and
+ * all session scoped locks of that session must be unlocked.<p>
+ * In order to prevent deadlocks from within the synchronous dispatching of
+ * events, content modifications should not be made from within code
+ * sections that hold monitors. (see #JCR-194)
+ *
+ * @param info lock info
+ */
+ void unlock(LockInfo info) {
+ // if no session currently holds lock, take system session
+ SessionImpl session = info.getLockHolder();
+ if (session == null) {
+ session = this.session;
+ }
+ try {
+ synchronized (contentMonitor) {
+ // get node's path and remove child in path map
+ NodeImpl node = (NodeImpl) session.getItemManager().getItem(
+ new NodeId(info.getUUID()));
+ Path path = getPath(node.getId());
+
+ acquire();
+
+ try {
+ PathMap.Element element = lockMap.map(path, true);
+ if (element != null) {
+ element.set(null);
+ }
+ info.setLive(false);
+ if (info.sessionScoped) {
+ save();
+ }
+
+ } finally {
+ release();
+ }
+
+ // remove properties in content
+ node.internalSetProperty(QName.JCR_LOCKOWNER, (InternalValue) null);
+ node.internalSetProperty(QName.JCR_LOCKISDEEP, (InternalValue) null);
+ node.save();
+ }
+ } catch (RepositoryException e) {
+ log.warn("Unable to unlock session-scoped lock on node '"
+ + info.lockToken + "': " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * Return the most appropriate lock information for a node. This is either
+ * the lock info for the node itself, if it is locked, or a lock info for one
+ * of its parents, if that is deep locked.
+ * @return lock info or <code>null</code> if node is not locked
+ * @throws RepositoryException if an error occurs
+ */
+ public AbstractLockInfo getLockInfo(String uuid) throws RepositoryException {
+ acquire();
+
+ try {
+ Path path = getPath(new NodeId(uuid));
+
+ PathMap.Element element = lockMap.map(path, false);
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info != null) {
+ if (element.hasPath(path) || info.deep) {
+ return info;
+ }
+ }
+ return null;
+ } catch (ItemNotFoundException e) {
+ return null;
+ } finally {
+ release();
+ }
+ }
+
+ //----------------------------------------------------------< LockManager >
+
+ /**
+ * {@inheritDoc}
+ */
+ public Lock lock(NodeImpl node, boolean isDeep, boolean isSessionScoped)
+ throws LockException, RepositoryException {
+
+ synchronized (contentMonitor) {
+ Lock lock = internalLock(node, isDeep, isSessionScoped);
+
+ // add properties to content
+ node.internalSetProperty(QName.JCR_LOCKOWNER,
+ InternalValue.create(node.getSession().getUserID()));
+ node.internalSetProperty(QName.JCR_LOCKISDEEP,
+ InternalValue.create(isDeep));
+ node.save();
+
+ return lock;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Lock getLock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ acquire();
+
+ try {
+ SessionImpl session = (SessionImpl) node.getSession();
+ Path path = getPath(node.getId());
+
+ PathMap.Element element = lockMap.map(path, false);
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info == null) {
+ throw new LockException("Node not locked: " + node.safeGetJCRPath());
+ }
+ if (element.hasPath(path) || info.deep) {
+ Node lockHolder = (Node) session.getItemManager().getItem(
+ new NodeId(info.getUUID()));
+ return new LockImpl(info, lockHolder);
+ } else {
+ throw new LockException("Node not locked: " + node.safeGetJCRPath());
+ }
+ } catch (ItemNotFoundException e) {
+ throw new LockException("Node not locked: " + node.safeGetJCRPath());
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * In order to prevent deadlocks from within the synchronous dispatching of
+ * events, content modifications should not be made from within code
+ * sections that hold monitors. (see #JCR-194)
+ */
+ public void unlock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ synchronized (contentMonitor) {
+ internalUnlock(node);
+
+ // remove properties in content
+ node.internalSetProperty(QName.JCR_LOCKOWNER, (InternalValue) null);
+ node.internalSetProperty(QName.JCR_LOCKISDEEP, (InternalValue) null);
+ node.save();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean holdsLock(NodeImpl node) throws RepositoryException {
+ acquire();
+
+ try {
+ PathMap.Element element = lockMap.map(getPath(node.getId()), true);
+ if (element == null) {
+ return false;
+ }
+ return element.get() != null;
+ } catch (ItemNotFoundException e) {
+ return false;
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isLocked(NodeImpl node) throws RepositoryException {
+ acquire();
+
+ try {
+ Path path = getPath(node.getId());
+
+ PathMap.Element element = lockMap.map(path, false);
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info == null) {
+ return false;
+ }
+ if (element.hasPath(path)) {
+ return true;
+ } else {
+ return info.deep;
+ }
+ } catch (ItemNotFoundException e) {
+ return false;
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkLock(NodeImpl node)
+ throws LockException, RepositoryException {
+
+ SessionImpl session = (SessionImpl) node.getSession();
+ checkLock(getPath(node.getId()), session);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkLock(Path path, Session session)
+ throws LockException, RepositoryException {
+
+ PathMap.Element element = lockMap.map(path, false);
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info != null) {
+ if (element.hasPath(path) || info.deep) {
+ if (!session.equals(info.getLockHolder())) {
+ throw new LockException("Node locked.");
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void lockTokenAdded(SessionImpl session, String lt) {
+ try {
+ LockToken lockToken = LockToken.parse(lt);
+
+ NodeImpl node = (NodeImpl) this.session.getItemManager().
+ getItem(new NodeId(lockToken.uuid));
+ PathMap.Element element = lockMap.map(node.getPrimaryPath(), true);
+ if (element != null) {
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info != null) {
+ if (info.getLockHolder() == null) {
+ info.setLockHolder(session);
+ } else {
+ log.warn("Adding lock token has no effect: "
+ + "lock already held by other session.");
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("Bad lock token: " + e.getMessage());
+ } catch (RepositoryException e) {
+ log.warn("Unable to set lock holder: " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void lockTokenRemoved(SessionImpl session, String lt) {
+ try {
+ LockToken lockToken = LockToken.parse(lt);
+
+ NodeImpl node = (NodeImpl) this.session.getItemManager().
+ getItem(new NodeId(lockToken.uuid));
+ PathMap.Element element = lockMap.map(node.getPrimaryPath(), true);
+ if (element != null) {
+ AbstractLockInfo info = (AbstractLockInfo) element.get();
+ if (info != null) {
+ if (session.equals(info.getLockHolder())) {
+ info.setLockHolder(null);
+ } else {
+ log.warn("Removing lock token has no effect: "
+ + "lock held by other session.");
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("Bad lock token: " + e.getMessage());
+ } catch (RepositoryException e) {
+ log.warn("Unable to reset lock holder: " + e.getMessage());
+ log.debug("Root cause: ", e);
+ }
+ }
+
+ /**
+ * Return the path of an item given its id. This method will lookup the
+ * item inside the systme session.
+ */
+ private Path getPath(ItemId id) throws RepositoryException {
+ return session.getHierarchyManager().getPath(id);
+ }
+
+ /**
+ * Acquire lock on the lock map.
+ */
+ private void acquire() {
+ for (;;) {
+ try {
+ lockMapLock.acquire();
+ break;
+ } catch (InterruptedException e) {}
+ }
+ }
+
+ /**
+ * Release lock on the lock map.
+ */
+ private void release() {
+ lockMapLock.release();
+ }
+
+ /**
+ * Start an update operation. This will acquire the lock on the lock map
+ * and disable saving the lock map file.
+ */
+ public void beginUpdate() {
+ acquire();
+ savingDisabled = true;
+ }
+
+ /**
+ * End an update operation. This will save the lock map file and release
+ * the lock on the lock map.
+ */
+ public void endUpdate() {
+ savingDisabled = false;
+ save();
+ release();
+ }
+
+ /**
+ * Cancel an update operation. This will release the lock on the lock map.
+ */
+ public void cancelUpdate() {
+ savingDisabled = false;
+ release();
+ }
+
+ //----------------------------------------------< SynchronousEventListener >
+
+ /**
+ * Internal event class that holds old and new paths for moved nodes
+ */
+ private class HierarchyEvent {
+
+ /**
+ * UUID recorded in event
+ */
+ public final String uuid;
+
+ /**
+ * Path recorded in event
+ */
+ public final Path path;
+
+ /**
+ * Old path in move operation
+ */
+ private Path oldPath;
+
+ /**
+ * New path in move operation
+ */
+ private Path newPath;
+
+ /**
+ * Event type, may be {@link Event#NODE_ADDED},
+ * {@link Event#NODE_REMOVED} or a combination of both
+ */
+ private int type;
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param uuid uuid
+ * @param path path
+ * @param type event type
+ */
+ public HierarchyEvent(String uuid, Path path, int type) {
+ this.uuid = uuid;
+ this.path = path;
+ this.type = type;
+ }
+
+ /**
+ * Merge this event with another event. The result will be stored in
+ * this event
+ *
+ * @param event other event to merge with
+ */
+ public void merge(HierarchyEvent event) {
+ type |= event.type;
+ if (event.type == Event.NODE_ADDED) {
+ newPath = event.path;
+ oldPath = path;
+ } else {
+ oldPath = event.path;
+ newPath = path;
+ }
+ }
+
+ /**
+ * Return the event type. May be {@link Event#NODE_ADDED},
+ * {@link Event#NODE_REMOVED} or a combination of both.\
+ *
+ * @return event type
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Return the old path if this is a move operation
+ *
+ * @return old path
+ */
+ public Path getOldPath() {
+ return oldPath;
+ }
+
+ /**
+ * Return the new path if this is a move operation
+ *
+ * @return new path
+ */
+ public Path getNewPath() {
+ return newPath;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onEvent(EventIterator events) {
+ Iterator iter = consolidateEvents(events);
+ while (iter.hasNext()) {
+ HierarchyEvent event = (HierarchyEvent) iter.next();
+ switch (event.type) {
+ case Event.NODE_ADDED:
+ nodeAdded(event.path);
+ break;
+ case Event.NODE_REMOVED:
+ nodeRemoved(event.path);
+ break;
+ case Event.NODE_ADDED | Event.NODE_REMOVED:
+ nodeMoved(event.getOldPath(), event.getNewPath());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Consolidate an event iterator obtained from observation, merging
+ * add and remove operations on nodes with the same UUID into a move
+ * operation.
+ */
+ private Iterator consolidateEvents(EventIterator events) {
+ LinkedMap eventMap = new LinkedMap();
+
+ while (events.hasNext()) {
+ EventImpl event = (EventImpl) events.nextEvent();
+ HierarchyEvent he;
+
+ try {
+ he = new HierarchyEvent(event.getChildUUID(),
+ Path.create(event.getPath(), nsResolver, true),
+ event.getType());
+ } catch (MalformedPathException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ continue;
+ } catch (RepositoryException e) {
+ log.info("Unable to get event's path: " + e.getMessage());
+ continue;
+ }
+
+ HierarchyEvent heExisting = (HierarchyEvent) eventMap.get(he.uuid);
+ if (heExisting != null) {
+ heExisting.merge(he);
+ } else {
+ eventMap.put(he.uuid, he);
+ }
+ }
+ return eventMap.values().iterator();
+ }
+
+ /**
+ * Refresh a non-empty path element whose children might have changed
+ * its position.
+ */
+ private void refresh(PathMap.Element element) {
+ final ArrayList infos = new ArrayList();
+ boolean needsSave = false;
+
+ // save away non-empty children
+ element.traverse(new PathMap.ElementVisitor() {
+ public void elementVisited(PathMap.Element element) {
+ LockInfo info = (LockInfo) element.get();
+ infos.add(info);
+ }
+ }, false);
+
+ // remove all children
+ element.removeAll();
+
+ // now re-insert at appropriate location or throw away if node
+ // does no longer exist
+ for (int i = 0; i < infos.size(); i++) {
+ LockInfo info = (LockInfo) infos.get(i);
+ try {
+ NodeImpl node = (NodeImpl) session.getItemManager().getItem(
+ new NodeId(info.getUUID()));
+ lockMap.put(node.getPrimaryPath(), info);
+ } catch (RepositoryException e) {
+ info.setLive(false);
+ if (!info.sessionScoped) {
+ needsSave = true;
+ }
+ }
+ }
+
+ // save if required
+ if (needsSave) {
+ save();
+ }
+ }
+
+ /**
+ * Invoked when some node has been added. If the parent of that node
+ * exists, shift all name siblings of the new node having an index greater
+ * or equal.
+ *
+ * @param path path of added node
+ */
+ private void nodeAdded(Path path) {
+ acquire();
+
+ try {
+ PathMap.Element parent = lockMap.map(path.getAncestor(1), true);
+ if (parent != null) {
+ refresh(parent);
+ }
+ } catch (PathNotFoundException e) {
+ log.warn("Unable to determine path of added node's parent.", e);
+ return;
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Invoked when some node has been moved. Relink the child inside our
+ * map to the new parent.
+ *
+ * @param oldPath old path
+ * @param newPath new path
+ */
+ private void nodeMoved(Path oldPath, Path newPath) {
+ acquire();
+
+ try {
+ PathMap.Element parent = lockMap.map(oldPath.getAncestor(1), true);
+ if (parent != null) {
+ refresh(parent);
+ }
+ } catch (PathNotFoundException e) {
+ log.warn("Unable to determine path of moved node's parent.", e);
+ return;
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Invoked when some node has been removed. Remove the child from our
+ * path map. Disable all locks contained in that subtree.
+ *
+ * @param path path of removed node
+ */
+ private void nodeRemoved(Path path) {
+ acquire();
+
+ try {
+ PathMap.Element parent = lockMap.map(path.getAncestor(1), true);
+ if (parent != null) {
+ refresh(parent);
+ }
+ } catch (PathNotFoundException e) {
+ log.warn("Unable to determine path of removed node's parent.", e);
+ return;
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Contains information about a lock and gets placed inside the child
+ * information of a {@link org.apache.jackrabbit.core.PathMap}.
+ */
+ class LockInfo extends AbstractLockInfo implements SessionListener {
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param lockToken lock token
+ * @param sessionScoped whether lock token is session scoped
+ * @param deep whether lock is deep
+ * @param lockOwner owner of lock
+ */
+ public LockInfo(LockToken lockToken, boolean sessionScoped,
+ boolean deep, String lockOwner) {
+ super(lockToken, sessionScoped, deep, lockOwner);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * When the owning session is logging out, we have to perform some
+ * operations depending on the lock type.
+ * (1) If the lock was session-scoped, we unlock the node.
+ * (2) If the lock was open-scoped, we remove the lock token
+ * from the session and set the lockHolder field to <code>null</code>.
+ */
+ public void loggingOut(SessionImpl session) {
+ if (live) {
+ if (sessionScoped) {
+ unlock(this);
+ } else {
+ if (session.equals(lockHolder)) {
+ session.removeLockToken(lockToken.toString());
+ lockHolder = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void loggedOut(SessionImpl session) {
+ }
+ }
+}
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/SharedLockManager.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java?rev=354456&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java Tue Dec 6 08:07:53 2005
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ * as applicable.
+ *
+ * Licensed 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.core.NodeId;
+import org.apache.jackrabbit.core.TransactionException;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.QName;
+import org.apache.log4j.Logger;
+
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 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 TxLockManager implements LockManager {
+
+ /**
+ * Logger instance for this class
+ */
+ private static final Logger log = Logger.getLogger(TxLockManager.class);
+
+ /**
+ * Shared lock manager.
+ */
+ private final SharedLockManager shared;
+
+ /**
+ * Map of locked nodes, indexed by their (internal) id.
+ */
+ private final Map lockedNodesMap = new HashMap();
+
+ /**
+ * Map of unlocked nodes, indexed by their (internal) id.
+ */
+ private final Map unlockedNodesMap = new HashMap();
+
+ /**
+ * List of lock/unlock operations.
+ */
+ private final List operations = new ArrayList();
+
+ /**
+ * Operation index.
+ */
+ private int opIndex;
+
+ /**
+ * Create a new instance of this class. Takes a <code>SharedLockManager</code>
+ * as parameter.
+ * @param shared shared lock manager
+ */
+ public TxLockManager(SharedLockManager shared) {
+ this.shared = shared;
+ }
+
+ /**
+ * Clear this object, releasing all its resources.
+ */
+ public void clear() {
+ lockedNodesMap.clear();
+ unlockedNodesMap.clear();
+ operations.clear();
+ opIndex = 0;
+ }
+
+ //----------------------------------------------------------< LockManager >
+
+ /**
+ * {@inheritDoc}
+ */
+ public Lock lock(NodeImpl node, boolean isDeep, boolean isSessionScoped)
+ throws LockException, RepositoryException {
+
+ String uuid = node.internalGetUUID();
+
+ // check negative set first
+ LockInfo info = (LockInfo) unlockedNodesMap.get(uuid);
+ if (info != null) {
+
+ // if settings are compatible, this is effectively a no-op
+ if (info.deep == isDeep && info.sessionScoped == isSessionScoped) {
+ unlockedNodesMap.remove(uuid);
+ operations.remove(info);
+ return getLock(node);
+ }
+ }
+
+ // verify node is not already locked.
+ if (isLocked(node)) {
+ throw new LockException("Node locked.");
+ }
+
+ // create a new lock info for this node
+ info = new LockInfo(node, new LockToken(node.internalGetUUID()),
+ isSessionScoped, isDeep, node.getSession().getUserID());
+ SessionImpl session = (SessionImpl) node.getSession();
+ info.setLockHolder(session);
+ info.setLive(true);
+ session.addLockToken(info.lockToken.toString(), false);
+ lockedNodesMap.put(uuid, info);
+ operations.add(info);
+
+ // add properties to content
+ node.internalSetProperty(QName.JCR_LOCKOWNER,
+ InternalValue.create(node.getSession().getUserID()));
+ node.internalSetProperty(QName.JCR_LOCKISDEEP,
+ InternalValue.create(info.deep));
+ node.save();
+ return new LockImpl(info, node);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Lock getLock(NodeImpl node) throws LockException, RepositoryException {
+ LockInfo info = getLockInfo(node);
+ if (info == null) {
+ throw new LockException("Node not locked: " + node.safeGetJCRPath());
+ }
+ SessionImpl session = (SessionImpl) node.getSession();
+ NodeImpl holder = (NodeImpl) session.getItemManager().getItem(
+ new NodeId(info.getUUID()));
+ return new LockImpl(info, holder);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void unlock(NodeImpl node) throws LockException, RepositoryException {
+ String uuid = node.internalGetUUID();
+
+ // check positive set first
+ LockInfo info = (LockInfo) lockedNodesMap.get(uuid);
+ if (info != null) {
+ lockedNodesMap.remove(uuid);
+ operations.remove(info);
+ info.setLive(false);
+ } else {
+ info = getLockInfo(node);
+ if (info == null || info.getUUID() != uuid) {
+ throw new LockException("Node not locked.");
+ } else if (info.getLockHolder() != node.getSession()) {
+ throw new LockException("Node not locked by this session.");
+ }
+ unlockedNodesMap.put(uuid, info);
+ info.setUnlock(true);
+ operations.add(info);
+ }
+
+ // remove properties in content
+ node.internalSetProperty(QName.JCR_LOCKOWNER, (InternalValue) null);
+ node.internalSetProperty(QName.JCR_LOCKISDEEP, (InternalValue) null);
+ node.save();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean holdsLock(NodeImpl node) throws RepositoryException {
+ String uuid = node.internalGetUUID();
+
+ if (lockedNodesMap.containsKey(uuid)) {
+ return true;
+ }
+ return shared.holdsLock(node);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isLocked(NodeImpl node) throws RepositoryException {
+ LockInfo info = getLockInfo(node);
+ return info != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkLock(NodeImpl node) throws LockException, RepositoryException {
+ LockInfo info = getLockInfo(node);
+ if (info != null && info.getLockHolder() != node.getSession()) {
+ throw new LockException("Node locked.");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void checkLock(Path path, Session session)
+ throws LockException, RepositoryException {
+
+ SessionImpl sessionImpl = (SessionImpl) session;
+ checkLock((NodeImpl) sessionImpl.getItemManager().getItem(path));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void lockTokenAdded(SessionImpl session, String lt) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void lockTokenRemoved(SessionImpl session, String lt) {
+ }
+
+ //-----------------------------------------------------------< transaction >
+
+ /**
+ * Prepare transaction. This will lock the shared lock manager and feed
+ * all locks.
+ */
+ public void prepare() throws TransactionException {
+ if (operations.isEmpty()) {
+ return;
+ }
+
+ shared.beginUpdate();
+
+ try {
+ while (opIndex < operations.size()) {
+ try {
+ LockInfo info = (LockInfo) operations.get(opIndex);
+ info.update();
+ } catch (RepositoryException e) {
+ throw new TransactionException("Unable to update.", e);
+ }
+ opIndex++;
+ }
+ } finally {
+ if (opIndex < operations.size()) {
+ while (opIndex > 0) {
+ try {
+ LockInfo info = (LockInfo) operations.get(opIndex - 1);
+ info.undo();
+ } catch (RepositoryException e) {
+ log.error("Unable to undo lock operation.", e);
+ }
+ opIndex--;
+ }
+ }
+ }
+ }
+
+ /**
+ * Commit transaction. This will finish the update and unlock the shared
+ * lock manager.
+ */
+ public void commit() {
+ if (!operations.isEmpty()) {
+ shared.endUpdate();
+ }
+ clear();
+ }
+
+ /**
+ * Rollback transaction. This will undo all updates and unlock the shared
+ * lock manager.
+ */
+ public void rollback() {
+ if (!operations.isEmpty() && opIndex == operations.size()) {
+ while (opIndex > 0) {
+ try {
+ LockInfo info = (LockInfo) operations.get(opIndex - 1);
+ info.undo();
+ } catch (RepositoryException e) {
+ log.error("Unable to undo lock operation.", e);
+ }
+ opIndex--;
+ }
+ shared.cancelUpdate();
+ }
+ clear();
+ }
+
+ //--------------------------------------------------------------< internal >
+
+ /**
+ * Return the most appropriate lock information for a node. This is either
+ * the lock info for the node itself, if it is locked, or a lock info for
+ * one of its parents, if that one is deep locked.
+ * @param node node
+ * @return LockInfo lock info or <code>null</code> if node is not locked
+ * @throws RepositoryException if an error occurs
+ */
+ private LockInfo getLockInfo(NodeImpl node) throws RepositoryException {
+ String uuid = node.internalGetUUID();
+
+ // check negative set
+ if (unlockedNodesMap.containsKey(uuid)) {
+ return null;
+ }
+
+ // check positive set, iteratively ascending in hierarchy
+ if (!lockedNodesMap.isEmpty()) {
+ NodeImpl current = node;
+ for (;;) {
+ LockInfo info = (LockInfo) lockedNodesMap.get(current.internalGetUUID());
+ if (info != null) {
+ if (info.getUUID() == uuid || info.deep) {
+ return info;
+ }
+ break;
+ }
+ if (current.getDepth() == 0) {
+ break;
+ }
+ current = (NodeImpl) current.getParent();
+ }
+ }
+
+ // ask parent and return a copy of its information
+ AbstractLockInfo info = shared.getLockInfo(uuid);
+ if (info == null) {
+ return null;
+ }
+ return new LockInfo(node, info.lockToken, info.sessionScoped,
+ info.deep, info.lockOwner);
+ }
+
+ /**
+ * Information about a lock used inside transactions.
+ */
+ class LockInfo extends AbstractLockInfo {
+
+ /**
+ * Node being locked/unlocked.
+ */
+ private final NodeImpl node;
+
+ /**
+ * Flag indicating whether this info belongs to a unlock operation.
+ */
+ private boolean isUnlock;
+
+ /**
+ * Create a new instance of this class.
+ * @param lockToken lock token
+ * @param sessionScoped whether lock token is session scoped
+ * @param deep whether lock is deep
+ * @param lockOwner owner of lock
+ */
+ public LockInfo(NodeImpl node, LockToken lockToken,
+ boolean sessionScoped, boolean deep, String lockOwner) {
+
+ super(lockToken, sessionScoped, deep, lockOwner);
+
+ this.node = node;
+ }
+
+ /**
+ * Return a flag indicating whether this info belongs to a unlock operation.
+ * @return <code>true</code> if this info belongs to an unlock operation;
+ * otherwise <code>false</code>
+ */
+ public boolean isUnlock() {
+ return isUnlock;
+ }
+
+ /**
+ * Set a flag indicating whether this info belongs to an unlock operation.
+ * @param isUnlock <code>true</code> if this info belongs to an unlock operation;
+ * otherwise <code>false</code>
+ */
+ public void setUnlock(boolean isUnlock) {
+ this.isUnlock = isUnlock;
+ }
+
+ /**
+ * Do operation.
+ */
+ public void update() throws LockException, RepositoryException {
+ if (isUnlock) {
+ shared.internalUnlock(node);
+ } else {
+ shared.internalLock(node, deep, sessionScoped);
+ }
+ }
+
+ /**
+ * Undo operation.
+ */
+ public void undo() throws LockException, RepositoryException {
+ if (isUnlock) {
+ shared.internalLock(node, deep, sessionScoped);
+ } else {
+ shared.internalUnlock(node);
+ }
+ }
+ }
+}
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/TxLockManager.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java?rev=354456&r1=354455&r2=354456&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/TransactionalItemStateManager.java Tue Dec 6 08:07:53 2005
@@ -18,6 +18,8 @@
import org.apache.jackrabbit.core.ItemId;
import org.apache.jackrabbit.core.WorkspaceImpl;
+import org.apache.jackrabbit.core.TransactionContext;
+import org.apache.jackrabbit.core.TransactionException;
import org.apache.log4j.Logger;
import javax.jcr.ReferentialIntegrityException;
@@ -30,12 +32,12 @@
public class TransactionalItemStateManager extends LocalItemStateManager {
/**
- * Logger instance
+ * Logger instance.
*/
private static Logger log = Logger.getLogger(TransactionalItemStateManager.class);
/**
- * Known attribute name
+ * Known attribute name inside the {@link TransactionContext}.
*/
private static final String ATTRIBUTE_CHANGE_LOG = "ChangeLog";
@@ -50,7 +52,7 @@
};
/**
- * Current transactional change log
+ * Current instance-local change log
*/
private transient ChangeLog txLog;
@@ -59,45 +61,41 @@
*
* @param sharedStateMgr shared state manager
*/
- public TransactionalItemStateManager(SharedItemStateManager sharedStateMgr, WorkspaceImpl wspImpl) {
+ public TransactionalItemStateManager(SharedItemStateManager sharedStateMgr,
+ WorkspaceImpl wspImpl) {
super(sharedStateMgr, wspImpl);
}
/**
- * Set transaction context.
- *
- * @param tx transaction context.
- */
- public void setTransactionContext(TransactionContext tx) {
- txLog = null;
-
- if (tx != null) {
- txLog = (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG);
- if (txLog == null) {
- txLog = new ChangeLog();
- tx.setAttribute(ATTRIBUTE_CHANGE_LOG, txLog);
- }
+ * Set transactional change log to use.
+ * @param txLog change log, may be <code>null</code>.
+ * @param threadLocal if <code>true</code> set thread-local change log;
+ * otherwise set instance-local change log
+ */
+ public void setChangeLog(ChangeLog txLog, boolean threadLocal) {
+ if (threadLocal) {
+ ((CommitLog) commitLog.get()).setChanges(txLog);
+ } else {
+ this.txLog = txLog;
}
}
/**
- * Prepare a transaction
- *
- * @param tx transaction context
+ * Prepare a transaction.
* @throws TransactionException if an error occurs
*/
- public void prepare(TransactionContext tx) throws TransactionException {
- ChangeLog changeLog = (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG);
- if (changeLog != null) {
+ public void prepare() throws TransactionException {
+ ChangeLog txLog = ((CommitLog) commitLog.get()).getChanges();
+ if (txLog != null) {
try {
- sharedStateMgr.checkReferentialIntegrity(changeLog);
+ sharedStateMgr.checkReferentialIntegrity(txLog);
} catch (ReferentialIntegrityException rie) {
log.error(rie);
- changeLog.undo(sharedStateMgr);
+ txLog.undo(sharedStateMgr);
throw new TransactionException("Unable to prepare transaction.", rie);
} catch (ItemStateException ise) {
log.error(ise);
- changeLog.undo(sharedStateMgr);
+ txLog.undo(sharedStateMgr);
throw new TransactionException("Unable to prepare transaction.", ise);
}
}
@@ -105,44 +103,34 @@
/**
* Commit changes made within a transaction
- *
- * @param tx transaction context
* @throws TransactionException if an error occurs
*/
- public void commit(TransactionContext tx) throws TransactionException {
- ChangeLog changeLog = (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG);
- if (changeLog != null) {
+ public void commit() throws TransactionException {
+ ChangeLog txLog = ((CommitLog) commitLog.get()).getChanges();
+ if (txLog != null) {
try {
- // set changeLog in ThreadLocal
- ((CommitLog) commitLog.get()).setChanges(changeLog);
- super.update(changeLog);
+ super.update(txLog);
} catch (ReferentialIntegrityException rie) {
log.error(rie);
- changeLog.undo(sharedStateMgr);
+ txLog.undo(sharedStateMgr);
throw new TransactionException("Unable to commit transaction.", rie);
} catch (ItemStateException ise) {
log.error(ise);
- changeLog.undo(sharedStateMgr);
+ txLog.undo(sharedStateMgr);
throw new TransactionException("Unable to commit transaction.", ise);
- } finally {
- ((CommitLog) commitLog.get()).setChanges(null);
}
- changeLog.reset();
- tx.notifyCommitted();
+ txLog.reset();
}
}
/**
* Rollback changes made within a transaction
- *
- * @param tx transaction context
*/
- public void rollback(TransactionContext tx) {
- ChangeLog changeLog = (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG);
- if (changeLog != null) {
- changeLog.undo(sharedStateMgr);
+ public void rollback() {
+ ChangeLog txLog = ((CommitLog) commitLog.get()).getChanges();
+ if (txLog != null) {
+ txLog.undo(sharedStateMgr);
}
- tx.notifyRolledBack();
}
//-----------------------------------------------------< ItemStateManager >