From jackrabbit-commits-return-1617-apmail-incubator-jackrabbit-commits-archive=www.apache.org@incubator.apache.org Tue Dec 06 16:09:32 2005 Return-Path: Delivered-To: apmail-incubator-jackrabbit-commits-archive@www.apache.org Received: (qmail 20419 invoked from network); 6 Dec 2005 16:09:31 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 6 Dec 2005 16:09:31 -0000 Received: (qmail 36562 invoked by uid 500); 6 Dec 2005 16:09:20 -0000 Mailing-List: contact jackrabbit-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: jackrabbit-dev@incubator.apache.org Delivered-To: mailing list jackrabbit-commits@incubator.apache.org Received: (qmail 36502 invoked by uid 500); 6 Dec 2005 16:09:19 -0000 Delivered-To: apmail-incubator-jackrabbit-cvs@incubator.apache.org Received: (qmail 36316 invoked by uid 99); 6 Dec 2005 16:09:18 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 06 Dec 2005 08:09:18 -0800 X-ASF-Spam-Status: No, hits=-8.6 required=10.0 tests=ALL_TRUSTED,INFO_TLD,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Tue, 06 Dec 2005 08:09:15 -0800 Received: (qmail 20164 invoked by uid 65534); 6 Dec 2005 16:08:54 -0000 Message-ID: <20051206160854.20161.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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/... Date: Tue, 06 Dec 2005 16:08:43 -0000 To: jackrabbit-cvs@incubator.apache.org From: dpfister@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N 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 WorkspaceInfo 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 + * null, 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, null 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} - *

- * 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} - *

- * Dispatch events. - */ - public void transactionCommitted(TransactionContext tx) { - events.dispatch(); - } - - /** - * {@inheritDoc} - *

- * 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 null + */ + private static ChangeLog getChangeLog(TransactionContext tx) { + return (ChangeLog) tx.getAttribute(ATTRIBUTE_CHANGE_LOG); + } + + /** + * Return the transactional lock manager for this session. Returns + * null 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 null 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 true if the lock is live; otherwise false + */ + public boolean isLive() { + return live; + } + + /** + * Return a flag indicating whether the lock is session-scoped + * + * @return true if the lock is session-scoped; + * otherwise false + */ + 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 lock 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.

+ * 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 null 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} + *

+ * 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} + *

+ * 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 null. + */ + 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 SharedLockManager + * 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 null 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 true if this info belongs to an unlock operation; + * otherwise false + */ + public boolean isUnlock() { + return isUnlock; + } + + /** + * Set a flag indicating whether this info belongs to an unlock operation. + * @param isUnlock true if this info belongs to an unlock operation; + * otherwise false + */ + 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 null. + * @param threadLocal if true 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 >