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 2006/01/11 15:24:32 UTC

svn commit: r368026 [1/3] - 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/observation/ main/java/org/apache/jackrabbit/core/state/ ma...

Author: dpfister
Date: Wed Jan 11 06:22:57 2006
New Revision: 368026

URL: http://svn.apache.org/viewcvs?rev=368026&view=rev
Log:
Make versioning transactional
- Added specialized XAVersion and XAVersionHistory objects that refresh their internal state when needed
- Defined new observation interface in order to have VersionManagerImpl use standard event dispatching
  instead of writing its own
- Added test cases verifying isolation of versioning operations in transactions

Added:
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/AbstractVersion.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/AbstractVersionHistory.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/AbstractVersionManager.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersion.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionHistory.java   (with props)
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java   (with props)
Modified:
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/InternalXAResource.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/ItemManager.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/NodeImpl.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/TransactionContext.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/XALockManager.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/DelegatingObservationDispatcher.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalFreezeImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalFrozenNodeImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalFrozenVHImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionItemImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionHistoryImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionItemStateProvider.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionIteratorImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManager.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualNodeState.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/InternalXAResource.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/InternalXAResource.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/InternalXAResource.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/InternalXAResource.java Wed Jan 11 06:22:57 2006
@@ -30,6 +30,13 @@
     public void associate(TransactionContext tx);
 
     /**
+     * Invoked before one of the {@link #prepare}, {@link #commit} or
+     * {@link #rollback} method is called.
+     * @param tx transaction context
+     */
+    public void beforeOperation(TransactionContext tx);
+
+    /**
      * Prepare transaction. The transaction is identified by a transaction
      * context. 
      * @param tx transaction context
@@ -52,4 +59,11 @@
      * @param tx transaction context.
      */
     public void rollback(TransactionContext tx) throws TransactionException;
+
+    /**
+     * Invoked after one of the {@link #prepare}, {@link #commit} or
+     * {@link #rollback} method has been called.
+     * @param tx transaction context
+     */
+    public void afterOperation(TransactionContext tx);
 }

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/ItemManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/ItemManager.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/ItemManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/ItemManager.java Wed Jan 11 06:22:57 2006
@@ -32,6 +32,8 @@
 import org.apache.jackrabbit.core.version.InternalVersionHistory;
 import org.apache.jackrabbit.core.version.VersionHistoryImpl;
 import org.apache.jackrabbit.core.version.VersionImpl;
+import org.apache.jackrabbit.core.version.AbstractVersion;
+import org.apache.jackrabbit.core.version.AbstractVersionHistory;
 import org.apache.jackrabbit.core.util.Dumpable;
 import org.apache.jackrabbit.name.NoPrefixDeclaredException;
 import org.apache.jackrabbit.name.Path;
@@ -81,7 +83,7 @@
     private final NodeDefinition rootNodeDef;
     private final NodeId rootNodeId;
 
-    private final SessionImpl session;
+    protected final SessionImpl session;
 
     private final ItemStateManager itemStateProvider;
     private final HierarchyManager hierMgr;
@@ -100,9 +102,9 @@
      * @param rootNodeDef       the definition of the root node
      * @param rootNodeUUID      the UUID of the root node
      */
-    ItemManager(ItemStateManager itemStateProvider, HierarchyManager hierMgr,
-                SessionImpl session, NodeDefinition rootNodeDef,
-                String rootNodeUUID) {
+    protected ItemManager(ItemStateManager itemStateProvider, HierarchyManager hierMgr,
+                          SessionImpl session, NodeDefinition rootNodeDef,
+                          String rootNodeUUID) {
         this.itemStateProvider = itemStateProvider;
         this.hierMgr = hierMgr;
         this.session = session;
@@ -493,18 +495,17 @@
         if (state.getNodeTypeName().equals(QName.NT_VERSION)) {
             InternalVersion version =
                     session.getVersionManager().getVersion(state.getUUID());
-            return new VersionImpl(this, session, id, state, def, listeners, version);
+            return createVersionInstance(id, state, def, listeners);
 
         } else if (state.getNodeTypeName().equals(QName.NT_VERSIONHISTORY)) {
             InternalVersionHistory history =
                     session.getVersionManager().getVersionHistory(state.getUUID());
-            return new VersionHistoryImpl(this, session, id, state, def, listeners, history);
+            return createVersionHistoryInstance(id, state, def, listeners);
 
         } else {
             // create node object
             return new NodeImpl(this, session, id, state, def, listeners);
         }
-
     }
 
     NodeImpl createNodeInstance(NodeState state) throws RepositoryException {
@@ -531,6 +532,42 @@
         PropertyDefinition def = getDefinition(state);
         // 2. create instance
         return createPropertyInstance(state, def);
+    }
+
+    /**
+     * Create a version instance.
+     * @param id node id
+     * @param state node state
+     * @param def node definition
+     * @param listeners listeners
+     * @return version instance
+     * @throws RepositoryException if an error occurs
+     */
+    protected AbstractVersion createVersionInstance(
+            NodeId id, NodeState state, NodeDefinition def,
+            ItemLifeCycleListener[] listeners) throws RepositoryException {
+
+        InternalVersion version =
+                session.getVersionManager().getVersion(id.getUUID());
+        return new VersionImpl(this, session, id, state, def, listeners, version);
+    }
+
+    /**
+     * Create a version history instance.
+     * @param id node id
+     * @param state node state
+     * @param def node definition
+     * @param listeners listeners
+     * @return version instance
+     * @throws RepositoryException if an error occurs
+     */
+    protected AbstractVersionHistory createVersionHistoryInstance(
+            NodeId id, NodeState state, NodeDefinition def,
+            ItemLifeCycleListener[] listeners) throws RepositoryException {
+
+        InternalVersionHistory history =
+                session.getVersionManager().getVersionHistory(id.getUUID());
+        return new VersionHistoryImpl(this, session, id, state, def, listeners, history);
     }
 
     //---------------------------------------------------< item cache methods >

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=368026&r1=368025&r2=368026&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 Wed Jan 11 06:22:57 2006
@@ -38,10 +38,8 @@
 import org.apache.jackrabbit.core.version.InternalFreeze;
 import org.apache.jackrabbit.core.version.InternalFrozenNode;
 import org.apache.jackrabbit.core.version.InternalFrozenVersionHistory;
-import org.apache.jackrabbit.core.version.InternalVersion;
-import org.apache.jackrabbit.core.version.VersionHistoryImpl;
-import org.apache.jackrabbit.core.version.VersionImpl;
 import org.apache.jackrabbit.core.version.VersionSelector;
+import org.apache.jackrabbit.core.version.AbstractVersion;
 import org.apache.jackrabbit.core.lock.LockManager;
 import org.apache.jackrabbit.name.IllegalNameException;
 import org.apache.jackrabbit.name.MalformedPathException;
@@ -2869,7 +2867,7 @@
             NodeImpl node;
             try {
                 // check if versionable node exists
-                InternalFrozenNode fn = ((VersionImpl) version).getFrozenNode();
+                InternalFrozenNode fn = ((AbstractVersion) version).getFrozenNode();
                 node = (NodeImpl) session.getNodeByUUID(fn.getFrozenUUID());
                 if (removeExisting) {
                     try {
@@ -2888,7 +2886,7 @@
                 }
             } catch (ItemNotFoundException e) {
                 // not found, create new one
-                node = addNode(relPath, ((VersionImpl) version).getFrozenNode());
+                node = addNode(relPath, ((AbstractVersion) version).getFrozenNode());
             }
 
             // recreate node from frozen state
@@ -3075,14 +3073,14 @@
             return null;
         }
         // test versions
-        InternalVersion v = ((VersionImpl) getBaseVersion()).getInternalVersion();
-        InternalVersion vp = ((VersionImpl) srcNode.getBaseVersion()).getInternalVersion();
+        AbstractVersion v = (AbstractVersion) getBaseVersion();
+        AbstractVersion vp = (AbstractVersion) srcNode.getBaseVersion();
         if (vp.isMoreRecent(v) && !isCheckedOut()) {
             // I f V' is a successor (to any degree) of V, then the merge result for
             // N is update. This case can be thought of as the case where N' is
             // "newer" than N and therefore N should be updated to reflect N'.
             return srcNode;
-        } else if (v.equals(vp) || v.isMoreRecent(vp)) {
+        } else if (v.isSame(vp) || v.isMoreRecent(vp)) {
             // If V' is a predecessor (to any degree) of V or if V and V' are
             // identical (i.e., are actually the same version), then the merge
             // result for N is leave. This case can be thought of as the case where
@@ -3488,7 +3486,7 @@
             throws UnsupportedRepositoryOperationException, RepositoryException {
 
         try {
-            internalRestore(((VersionImpl) version).getInternalVersion(), vsel, removeExisting);
+            internalRestore((AbstractVersion) version, vsel, removeExisting);
         } catch (RepositoryException e) {
             // revert session
             try {
@@ -3511,8 +3509,8 @@
      * @param removeExisting
      * @throws RepositoryException
      */
-    protected InternalVersion[] internalRestore(InternalVersion version, VersionSelector vsel,
-                                                boolean removeExisting)
+    protected Version[] internalRestore(AbstractVersion version, VersionSelector vsel,
+                                        boolean removeExisting)
             throws RepositoryException {
 
         // fail if root version
@@ -3531,7 +3529,8 @@
         restored.add(version);
 
         // 2. N's jcr:baseVersion property will be changed to point to V.
-        internalSetProperty(QName.JCR_BASEVERSION, InternalValue.create(new UUID(version.getId())));
+        UUID uuid = new UUID(((NodeId) version.getId()).getUUID());
+        internalSetProperty(QName.JCR_BASEVERSION, InternalValue.create(uuid));
 
         // 4. N's jcr:predecessor property is set to null
         internalSetProperty(QName.JCR_PREDECESSORS, InternalValue.EMPTY_ARRAY, PropertyType.REFERENCE);
@@ -3542,7 +3541,7 @@
         // 3. N's jcr:isCheckedOut property is set to false.
         internalSetProperty(QName.JCR_ISCHECKEDOUT, InternalValue.create(false));
 
-        return (InternalVersion[]) restored.toArray(new InternalVersion[restored.size()]);
+        return (Version[]) restored.toArray(new Version[restored.size()]);
     }
 
     /**
@@ -3654,7 +3653,7 @@
 
             } else if (child instanceof InternalFrozenVersionHistory) {
                 InternalFrozenVersionHistory f = (InternalFrozenVersionHistory) child;
-                VersionHistoryImpl history = (VersionHistoryImpl) session.getNodeByUUID(f.getVersionHistoryId());
+                VersionHistory history = (VersionHistory) session.getNodeByUUID(f.getVersionHistoryId());
                 String nodeId = history.getVersionableUUID();
 
                 // check if representing versionable already exists somewhere
@@ -3673,7 +3672,7 @@
                     }
                 } else {
                     // get desired version from version selector
-                    InternalVersion v = ((VersionImpl) vsel.select(history)).getInternalVersion();
+                    AbstractVersion v = (AbstractVersion) vsel.select(history);
                     NodeImpl node = addNode(child.getName(), v.getFrozenNode());
                     node.internalRestore(v, vsel, removeExisting);
                     // add this version to set

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=368026&r1=368025&r2=368026&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 Wed Jan 11 06:22:57 2006
@@ -1397,7 +1397,7 @@
             // create item state manager
             try {
                 itemStateMgr =
-                        new SharedItemStateManager(persistMgr, rootNodeUUID, ntReg);
+                        new SharedItemStateManager(persistMgr, rootNodeUUID, ntReg, true);
                 try {
                     itemStateMgr.addVirtualItemStateProvider(
                             vMgr.getVirtualItemStateProvider());

Modified: 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=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/TransactionContext.java Wed Jan 11 06:22:57 2006
@@ -112,6 +112,7 @@
      */
     public synchronized void prepare() throws XAException {
         status = Status.STATUS_PREPARING;
+        beforeOperation();
 
         TransactionException txe = null;
         for (int i = 0; i < resources.length; i++) {
@@ -130,6 +131,7 @@
                 }
             }
         }
+        afterOperation();
         status = Status.STATUS_PREPARED;
 
         Thread rollbackThread = new Thread(this, "RollbackThread");
@@ -154,6 +156,7 @@
             throw new XAException(XAException.XA_RBTIMEOUT);
         }
         status = Status.STATUS_COMMITTING;
+        beforeOperation();
 
         TransactionException txe = null;
         for (int i = 0; i < resources.length; i++) {
@@ -172,6 +175,7 @@
                 }
             }
         }
+        afterOperation();
         status = Status.STATUS_COMMITTED;
 
         if (txe != null) {
@@ -191,6 +195,7 @@
             throw new XAException(XAException.XA_RBTIMEOUT);
         }
         status = Status.STATUS_ROLLING_BACK;
+        beforeOperation();
 
         int errors = 0;
         for (int i = 0; i < resources.length; i++) {
@@ -202,7 +207,9 @@
                 errors++;
             }
         }
+        afterOperation();
         status = Status.STATUS_ROLLEDBACK;
+
         if (errors != 0) {
             throw new XAException(XAException.XA_RBOTHER);
         }
@@ -231,6 +238,26 @@
                 }
                 log.warn("Transaction rolled back because timeout expired.");
             }
+        }
+    }
+
+    /**
+     * Invoke all of the registered resources' {@link InternalXAResource#beforeOperation}
+     * methods.
+     */
+    private void beforeOperation() {
+        for (int i = 0; i < resources.length; i++) {
+            resources[i].beforeOperation(this);
+        }
+    }
+
+    /**
+     * Invoke all of the registered resources' {@link InternalXAResource#afterOperation}
+     * methods.
+     */
+    private void afterOperation() {
+        for (int i = 0; i < resources.length; i++) {
+            resources[i].afterOperation(this);
         }
     }
 }

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=368026&r1=368025&r2=368026&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 Wed Jan 11 06:22:57 2006
@@ -20,13 +20,14 @@
 import org.apache.jackrabbit.core.lock.LockManager;
 import org.apache.jackrabbit.core.observation.ObservationManagerFactory;
 import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
+import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
+import org.apache.jackrabbit.core.observation.EventStateCollection;
 import org.apache.jackrabbit.core.query.QueryManagerImpl;
 import org.apache.jackrabbit.core.state.SharedItemStateManager;
 import org.apache.jackrabbit.core.state.LocalItemStateManager;
 import org.apache.jackrabbit.core.version.GenericVersionSelector;
-import org.apache.jackrabbit.core.version.InternalVersion;
-import org.apache.jackrabbit.core.version.VersionImpl;
 import org.apache.jackrabbit.core.version.VersionSelector;
+import org.apache.jackrabbit.core.version.AbstractVersion;
 import org.apache.jackrabbit.core.xml.ImportHandler;
 import org.apache.jackrabbit.core.xml.Importer;
 import org.apache.jackrabbit.core.xml.WorkspaceImporter;
@@ -67,7 +68,7 @@
 /**
  * A <code>WorkspaceImpl</code> ...
  */
-public class WorkspaceImpl implements Workspace {
+public class WorkspaceImpl implements Workspace, EventStateCollectionFactory {
 
     private static Logger log = Logger.getLogger(WorkspaceImpl.class);
 
@@ -123,9 +124,8 @@
      * @param session   The session
      */
     protected WorkspaceImpl(WorkspaceConfig wspConfig,
-                  SharedItemStateManager stateMgr,
-                  RepositoryImpl rep,
-                  SessionImpl session) {
+                            SharedItemStateManager stateMgr, RepositoryImpl rep,
+                            SessionImpl session) {
         this.wspConfig = wspConfig;
         this.rep = rep;
         this.stateMgr = createItemStateManager(stateMgr);
@@ -573,7 +573,7 @@
         // add all versions to map of versions to restore
         final HashMap toRestore = new HashMap();
         for (int i = 0; i < versions.length; i++) {
-            VersionImpl v = (VersionImpl) versions[i];
+            AbstractVersion v = (AbstractVersion) versions[i];
             VersionHistory vh = v.getContainingHistory();
             // check for collision
             if (toRestore.containsKey(vh.getUUID())) {
@@ -606,16 +606,16 @@
             // now restore all versions that have a node in the ws
             int numRestored = 0;
             while (toRestore.size() > 0) {
-                InternalVersion[] restored = null;
+                Version[] restored = null;
                 Iterator iter = toRestore.values().iterator();
                 while (iter.hasNext()) {
-                    VersionImpl v = (VersionImpl) iter.next();
+                    AbstractVersion v = (AbstractVersion) iter.next();
                     try {
                         NodeImpl node = (NodeImpl) session.getNodeByUUID(v.getFrozenNode().getFrozenUUID());
-                        restored = node.internalRestore(v.getInternalVersion(), vsel, removeExisting);
+                        restored = node.internalRestore(v, vsel, removeExisting);
                         // remove restored versions from set
                         for (int i = 0; i < restored.length; i++) {
-                            toRestore.remove(restored[i].getVersionHistory().getId());
+                            toRestore.remove(restored[i].getContainingHistory().getUUID());
                         }
                         numRestored += restored.length;
                         break;
@@ -728,6 +728,20 @@
      */
     protected LocalItemStateManager createItemStateManager(SharedItemStateManager shared) {
         return new LocalItemStateManager(shared, this);
+    }
+
+    //------------------------------------------< EventStateCollectionFactory >
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Implemented in this object and forwarded rather than {@link #obsMgr}
+     * since creation of the latter is lazy.
+     */
+    public EventStateCollection createEventStateCollection()
+            throws RepositoryException {
+
+        return ((ObservationManagerImpl) getObservationManager()).createEventStateCollection();
     }
 }
 

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java?rev=368026&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java Wed Jan 11 06:22:57 2006
@@ -0,0 +1,84 @@
+/*
+ * 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.state.ItemStateManager;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.version.InternalVersion;
+import org.apache.jackrabbit.core.version.AbstractVersion;
+import org.apache.jackrabbit.core.version.XAVersion;
+import org.apache.jackrabbit.core.version.AbstractVersionHistory;
+import org.apache.jackrabbit.core.version.InternalVersionHistory;
+import org.apache.jackrabbit.core.version.XAVersionHistory;
+import org.apache.jackrabbit.core.version.XAVersionManager;
+import org.apache.log4j.Logger;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.nodetype.NodeDefinition;
+
+/**
+ * Extended <code>ItemManager</code> that works in an XA environment.
+ */
+public class XAItemManager extends ItemManager {
+
+    /**
+     * Logger instance.
+     */
+    private static Logger log = Logger.getLogger(XAItemManager.class);
+
+    /**
+     * Create a new instance of this class.
+     * @param itemStateProvider the item state provider associated with
+     *                          the new instance
+     * @param session           the session associated with the new instance
+     * @param rootNodeDef       the definition of the root node
+     * @param rootNodeUUID      the UUID of the root node
+     */
+    protected XAItemManager(ItemStateManager itemStateProvider, HierarchyManager hierMgr,
+                SessionImpl session, NodeDefinition rootNodeDef,
+                String rootNodeUUID) {
+
+        super(itemStateProvider, hierMgr, session, rootNodeDef, rootNodeUUID);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected AbstractVersion createVersionInstance(
+            NodeId id, NodeState state, NodeDefinition def,
+            ItemLifeCycleListener[] listeners) throws RepositoryException {
+
+        InternalVersion version =
+                session.getVersionManager().getVersion(id.getUUID());
+        return new XAVersion(this, session, id, state, def, listeners, version);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected AbstractVersionHistory createVersionHistoryInstance(
+            NodeId id, NodeState state, NodeDefinition def,
+            ItemLifeCycleListener[] listeners) throws RepositoryException {
+
+        InternalVersionHistory history =
+                session.getVersionManager().getVersionHistory(id.getUUID());
+        return new XAVersionHistory(this, session, id, state, def, listeners, history);
+    }
+}

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/XAItemManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

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=368026&r1=368025&r2=368026&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 Wed Jan 11 06:22:57 2006
@@ -22,8 +22,11 @@
 import org.apache.jackrabbit.core.lock.XALockManager;
 import org.apache.jackrabbit.core.lock.LockManagerImpl;
 import org.apache.jackrabbit.core.state.XAItemStateManager;
-import org.apache.jackrabbit.core.state.SessionItemStateManager;
 import org.apache.jackrabbit.core.state.SharedItemStateManager;
+import org.apache.jackrabbit.core.state.SessionItemStateManager;
+import org.apache.jackrabbit.core.version.VersionManager;
+import org.apache.jackrabbit.core.version.VersionManagerImpl;
+import org.apache.jackrabbit.core.version.XAVersionManager;
 import org.apache.log4j.Logger;
 
 import javax.jcr.AccessDeniedException;
@@ -93,10 +96,7 @@
 
         super(rep, loginContext, wspConfig);
 
-        txResources = new InternalXAResource[] {
-            (XAItemStateManager) wsp.getItemStateManager(),
-            (XALockManager) getLockManager()
-        };
+        init();
     }
 
     /**
@@ -115,10 +115,21 @@
 
         super(rep, subject, wspConfig);
 
+        init();
+    }
+
+    /**
+     * Initialize this object.
+     */
+    private void init() throws RepositoryException {
+        XAItemStateManager stateMgr = (XAItemStateManager) wsp.getItemStateManager();
+        XALockManager lockMgr = (XALockManager) getLockManager();
+        XAVersionManager versionMgr = (XAVersionManager) getVersionManager();
+
         txResources = new InternalXAResource[] {
-            (XAItemStateManager) wsp.getItemStateManager(),
-            (XALockManager) getLockManager()
+            stateMgr, lockMgr, versionMgr
         };
+        stateMgr.setVirtualProvider(versionMgr);
     }
 
     /**
@@ -129,6 +140,25 @@
                                                     RepositoryImpl rep,
                                                     SessionImpl session) {
         return new XAWorkspace(wspConfig, stateMgr, rep, session);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected VersionManager createVersionManager(RepositoryImpl rep)
+            throws RepositoryException {
+
+        VersionManagerImpl vMgr = (VersionManagerImpl) rep.getVersionManager();
+        return new XAVersionManager(vMgr, rep.getNodeTypeRegistry(), wsp);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected ItemManager createItemManager(SessionItemStateManager itemStateMgr,
+                                            HierarchyManager hierMgr) {
+        return new XAItemManager(itemStateMgr, hierMgr, this,
+                ntMgr.getRootNodeDefinition(), rep.getRootNodeUUID());
     }
 
     /**

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/XALockManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/XALockManager.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/XALockManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/lock/XALockManager.java Wed Jan 11 06:22:57 2006
@@ -215,6 +215,12 @@
     /**
      * {@inheritDoc}
      */
+    public void beforeOperation(TransactionContext tx) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public void prepare(TransactionContext tx) throws TransactionException {
         XAEnvironment xaEnv = (XAEnvironment) tx.getAttribute(XA_ENV_ATTRIBUTE_NAME);
         if (xaEnv != null) {
@@ -244,6 +250,12 @@
         if (xaEnv != null) {
             xaEnv.rollback();
         }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void afterOperation(TransactionContext tx) {
     }
 
     /**

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/DelegatingObservationDispatcher.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/DelegatingObservationDispatcher.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/DelegatingObservationDispatcher.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/DelegatingObservationDispatcher.java Wed Jan 11 06:22:57 2006
@@ -17,8 +17,8 @@
 package org.apache.jackrabbit.core.observation;
 
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.state.ChangeLog;
 
-import javax.jcr.RepositoryException;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -27,7 +27,7 @@
  * This Class implements an observation dispatcher, that delegates events to
  * a set of underlying dispatchers.
  */
-public class DelegatingObservationDispatcher {
+public class DelegatingObservationDispatcher extends EventDispatcher {
 
     /**
      * the set of dispatchers
@@ -53,15 +53,48 @@
     }
 
     /**
+     * Creates an <code>EventStateCollection</code> tied to the session
+     * given as argument.
+     *
+     * @param session event source
+     * @return new <code>EventStateCollection</code> instance
+     */
+    public EventStateCollection createEventStateCollection(SessionImpl session) {
+        return new EventStateCollection(this, session);
+    }
+
+    //------------------------------------------------------< EventDispatcher >
+
+    /**
+     * {@inheritDoc}
+     */
+    void prepareEvents(EventStateCollection events) {
+        // events will get prepared on dispatch
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    void prepareDeleted(EventStateCollection events, ChangeLog changes) {
+        // events will get prepared on dispatch
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    void dispatchEvents(EventStateCollection events) {
+        dispatch(events.getEvents(), events.getSession());
+    }
+
+    /**
      * Dispatchers a list of events to all registered dispatchers. A new
      * {@link EventStateCollection} is created for every dispatcher, fille with
      * the given event list and then dispatched.
      *
      * @param eventList
      * @param session
-     * @throws RepositoryException
      */
-    public void dispatch(List eventList, SessionImpl session) throws RepositoryException {
+    public void dispatch(List eventList, SessionImpl session) {
         Iterator iter = dispatchers.iterator();
         while (iter.hasNext()) {
             ObservationManagerFactory fac = (ObservationManagerFactory) iter.next();

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java?rev=368026&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java Wed Jan 11 06:22:57 2006
@@ -0,0 +1,50 @@
+/*
+ * 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.observation;
+
+import org.apache.jackrabbit.core.state.ChangeLog;
+
+/**
+ * Defines an object that prepares and dispatches events. Made into an abstract
+ * class rather than an interface in order not to exhibit internal methods
+ * that should not be visible to everybody.
+ */
+abstract class EventDispatcher {
+
+    /**
+     * Gives this dispatcher the oportunity to prepare the events for
+     * dispatching.
+     *
+     * @param events the {@link EventState}s to prepare.
+     */
+    abstract void prepareEvents(EventStateCollection events);
+
+    /**
+     * Prepares changes that involve deleted item states.
+     *
+     * @param events the event state collection.
+     * @param changes the changes.
+     */
+    abstract void prepareDeleted(EventStateCollection events, ChangeLog changes);
+
+    /**
+     * Dispatches the {@link EventStateCollection events}.
+     *
+     * @param events the {@link EventState}s to dispatch.
+     */
+    abstract void dispatchEvents(EventStateCollection events);
+}

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventDispatcher.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollection.java Wed Jan 11 06:22:57 2006
@@ -41,6 +41,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.Collections;
 
 /**
  * The <code>EventStateCollection</code> class implements how {@link EventState}
@@ -70,9 +71,9 @@
     private final List events = new ArrayList();
 
     /**
-     * The <code>ObservationManagerFactory</code> that notifies the EventListeners.
+     * Event dispatcher.
      */
-    private final ObservationManagerFactory dispatcher;
+    private final EventDispatcher dispatcher;
 
     /**
      * The session that created these events
@@ -82,9 +83,10 @@
     /**
      * Creates a new empty <code>EventStateCollection</code>.
      *
+     * @param dispatcher event dispatcher
      * @param session the session that created these events.
      */
-    EventStateCollection(ObservationManagerFactory dispatcher,
+    EventStateCollection(EventDispatcher dispatcher,
                          SessionImpl session) {
         this.dispatcher = dispatcher;
         this.session = session;
@@ -420,6 +422,22 @@
      */
     Iterator iterator() {
         return events.iterator();
+    }
+
+    /**
+     * Return the list of events.
+     * @return list of events
+     */
+    List getEvents() {
+        return Collections.unmodifiableList(events);
+    }
+
+    /**
+     * Return the session who is the origin of this events.
+     * @return event source
+     */
+    SessionImpl getSession() {
+        return session;
     }
 
     /**

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java?rev=368026&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java Wed Jan 11 06:22:57 2006
@@ -0,0 +1,33 @@
+/*
+ * 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.observation;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Defines methods to create an {@link EventStateCollection}
+ */
+public interface EventStateCollectionFactory {
+
+    /**
+     * Creates an <code>EventStateCollection</code>.
+     *
+     * @return a new <code>EventStateCollection</code>
+     * @throws RepositoryException if creation fails for some reason
+     */
+    public EventStateCollection createEventStateCollection() throws RepositoryException;
+}

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/EventStateCollectionFactory.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerFactory.java Wed Jan 11 06:22:57 2006
@@ -35,7 +35,8 @@
  * creates new {@link EventStateCollection}s that can be dispatched
  * to registered {@link javax.jcr.observation.EventListener}s.
  */
-public final class ObservationManagerFactory implements Runnable {
+public final class ObservationManagerFactory extends EventDispatcher
+        implements Runnable {
 
     /**
      * Logger instance for this class
@@ -171,10 +172,10 @@
     }
 
     /**
+     * {@inheritDoc}
+     * <p/>
      * Gives this observation manager the oportunity to
      * prepare the events for dispatching.
-     *
-     * @param events the {@link EventState}s to prepare.
      */
     void prepareEvents(EventStateCollection events) {
         Set consumers = new HashSet();
@@ -187,10 +188,7 @@
     }
 
     /**
-     * Prepares changes that involve deleted item states.
-     *
-     * @param events the event state collection.
-     * @param changes the changes.
+     * {@inheritDoc}
      */
     void prepareDeleted(EventStateCollection events, ChangeLog changes) {
         Set consumers = new HashSet();
@@ -203,10 +201,10 @@
     }
 
     /**
+     * {@inheritDoc}
+     * <p/>
      * Dispatches the {@link EventStateCollection events} to all
      * registered {@link javax.jcr.observation.EventListener}s.
-     *
-     * @param events the {@link EventState}s to dispatch.
      */
     void dispatchEvents(EventStateCollection events) {
         // notify synchronous listeners

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/observation/ObservationManagerImpl.java Wed Jan 11 06:22:57 2006
@@ -31,7 +31,7 @@
  * instance. The class <code>SessionLocalObservationManager</code> implements
  * this behaviour.
  */
-public class ObservationManagerImpl implements ObservationManager {
+public class ObservationManagerImpl implements ObservationManager, EventStateCollectionFactory {
 
     /**
      * The logger instance of this class
@@ -153,16 +153,6 @@
     }
 
     /**
-     * Creates an <code>EventStateCollection</code> tied to the session
-     * which is attached this <code>ObservationManager</code> instance.
-     *
-     * @return a new <code>EventStateCollection</code>.
-     */
-    public EventStateCollection createEventStateCollection() {
-        return new EventStateCollection(obsMgrFactory, session);
-    }
-
-    /**
      * Unregisters all EventListeners.
      */
     public void dispose() {
@@ -179,4 +169,15 @@
 
     }
 
+    //------------------------------------------< EventStateCollectionFactory >
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Creates an <code>EventStateCollection</code> tied to the session
+     * which is attached to this <code>ObservationManager</code> instance.
+     */
+    public EventStateCollection createEventStateCollection() {
+        return new EventStateCollection(obsMgrFactory, session);
+    }
 }

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java Wed Jan 11 06:22:57 2006
@@ -19,13 +19,11 @@
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
-import org.apache.jackrabbit.core.WorkspaceImpl;
-import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
+import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
 import org.apache.jackrabbit.name.QName;
 import org.apache.log4j.Logger;
 
 import javax.jcr.ReferentialIntegrityException;
-import javax.jcr.RepositoryException;
 import java.util.Iterator;
 
 /**
@@ -52,9 +50,9 @@
     protected final SharedItemStateManager sharedStateMgr;
 
     /**
-     * Local WorkspaceImpl instance.
+     * Event state collection factory.
      */
-    protected final WorkspaceImpl wspImpl;
+    protected final EventStateCollectionFactory factory;
 
     /**
      * Flag indicating whether this item state manager is in edit mode
@@ -68,20 +66,14 @@
 
     /**
      * Creates a new <code>LocalItemStateManager</code> instance.
-     * todo LocalItemStateManager without a wspImpl will not generate observation events!
-     *
      * @param sharedStateMgr shared state manager
-     * @param wspImpl        the workspace instance where this item state manager
-     *                       belongs to, or <code>null</code> if this item state manager is not
-     *                       associated with a workspace. This is the case for the version item state
-     *                       manager. Version item states are not associated with a specific workspace
-     *                       instance.
+     * @param factory event state collection factory
      */
     public LocalItemStateManager(SharedItemStateManager sharedStateMgr,
-                                 WorkspaceImpl wspImpl) {
+                                 EventStateCollectionFactory factory) {
         cache = new ItemStateReferenceCache();
         this.sharedStateMgr = sharedStateMgr;
-        this.wspImpl = wspImpl;
+        this.factory = factory;
     }
 
     /**
@@ -325,23 +317,9 @@
      * @throws ItemStateException            if an error occurs
      */
     protected void update(ChangeLog changeLog)
-            throws ReferentialIntegrityException, StaleItemStateException,
-            ItemStateException {
-
-        ObservationManagerImpl obsMgr = null;
-
-        try {
-            if (wspImpl != null) {
-                obsMgr = (ObservationManagerImpl) wspImpl.getObservationManager();
-            }
-        } catch (RepositoryException e) {
-            // should never get here
-            String msg = "ObservationManager unavailable";
-            log.error(msg);
-            throw new ItemStateException(msg, e);
-        }
+            throws ReferentialIntegrityException, StaleItemStateException, ItemStateException {
 
-        sharedStateMgr.store(changeLog, obsMgr);
+        sharedStateMgr.update(changeLog, factory);
         changeLog.persisted();
     }
 

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java Wed Jan 11 06:22:57 2006
@@ -27,7 +27,7 @@
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.nodetype.PropDef;
 import org.apache.jackrabbit.core.observation.EventStateCollection;
-import org.apache.jackrabbit.core.observation.ObservationManagerImpl;
+import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
 import org.apache.jackrabbit.core.util.Dumpable;
 import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
@@ -36,6 +36,7 @@
 
 import javax.jcr.PropertyType;
 import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.RepositoryException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import java.io.PrintStream;
@@ -130,6 +131,12 @@
     private final NodeTypeRegistry ntReg;
 
     /**
+     * Flag indicating whether this item state manager uses node references to
+     * verify integrity of its reference properties.
+     */
+    private final boolean usesReferences;
+
+    /**
      * uuid of root node
      */
     private final String rootNodeUUID;
@@ -164,11 +171,13 @@
      */
     public SharedItemStateManager(PersistenceManager persistMgr,
                                   String rootNodeUUID,
-                                  NodeTypeRegistry ntReg)
+                                  NodeTypeRegistry ntReg,
+                                  boolean usesReferences)
             throws ItemStateException {
         cache = new ItemStateReferenceCache();
         this.persistMgr = persistMgr;
         this.ntReg = ntReg;
+        this.usesReferences = usesReferences;
         this.rootNodeUUID = rootNodeUUID;
         // create root node state if it doesn't yet exist
         if (!hasNonVirtualItemState(new NodeId(rootNodeUUID))) {
@@ -378,69 +387,89 @@
     }
 
     /**
-     * Store modifications registered in a <code>ChangeLog</code>. The items
-     * contained in the <tt>ChangeLog</tt> are not states returned by this
-     * item state manager but rather must be reconnected to items provided
-     * by this state manager.<p/>
-     * After successfully storing the states the observation manager is informed
-     * about the changes, if an observation manager is passed to this method.<p/>
-     * NOTE: This method is not synchronized, because all methods it invokes
-     * on instance members (such as {@link PersistenceManager#store} are
-     * considered to be thread-safe. Should this ever change, the
-     * synchronization status has to be re-examined.
-     *
-     * @param local  change log containing local items
-     * @param obsMgr the observation manager to inform, or <code>null</code> if
-     *               no observation manager should be informed.
-     * @throws ReferentialIntegrityException if a new or modified REFERENCE
-     *                                       property refers to a non-existent
-     *                                       target or if a removed node is still
-     *                                       being referenced
-     * @throws StaleItemStateException       if at least one of the affected item
-     *                                       states has become stale
-     * @throws ItemStateException            if another error occurs
+     * Object representing a single update operation.
      */
-    public void store(ChangeLog local, ObservationManagerImpl obsMgr)
-            throws ReferentialIntegrityException, StaleItemStateException,
-            ItemStateException {
+    class Update {
+
+        /**
+         * Local change log.
+         */
+        private final ChangeLog local;
+
+        /**
+         * Event state collection factory.
+         */
+        private final EventStateCollectionFactory factory;
+
+        /**
+         * Virtual provider containing references to be left out when updating
+         * references.
+         */
+        private final VirtualItemStateProvider virtualProvider;
+
+        /**
+         * Shared change log.
+         */
+        private ChangeLog shared;
 
-        ChangeLog shared = new ChangeLog();
+        /**
+         * Virtual node references.
+         */
+        private List[] virtualNodeReferences;
 
         /**
-         * array of lists of dirty virtual node references per virtual provider.
-         * since NV-type references must be persisted via the respective VISP
-         * and not by the SISM, they are filtered out below.
-         *
-         * todo: FIXME handling of virtual node references is erm...  messy
-         *       VISP are eventually replaced by a more general 'mounting'
-         *       mechanism, probably on the API level and not on the item state
-         *       layer.
+         * Events to dispatch.
          */
-        List[] virtualNodeReferences = new List[virtualProviders.length];
+        private EventStateCollection events;
 
-        EventStateCollection events = null;
-        if (obsMgr != null) {
-            events = obsMgr.createEventStateCollection();
-            events.prepareDeleted(local);
+        /**
+         * Create a new instance of this class.
+         */
+        public Update(ChangeLog local, EventStateCollectionFactory factory,
+                      VirtualItemStateProvider virtualProvider) {
+            this.local = local;
+            this.factory = factory;
+            this.virtualProvider = virtualProvider;
         }
 
-        acquireWriteLock();
-        boolean holdingWriteLock = true;
+        /**
+         * Begin update operation. Prepares everything upto the point where
+         * the persistence manager's <code>store</code> method may be invoked.
+         * If this method succeeds, a write lock will have been acquired on the
+         * item state manager and either {@link #end()} or {@link #cancel()} has
+         * to be called in order to release it.
+         */
+        public void begin() throws ItemStateException, ReferentialIntegrityException {
+            shared = new ChangeLog();
 
-        try {
-            /**
-             * Update node references based on modifications in change log
-             * (added/modified/removed REFERENCE properties)
-             */
-            updateReferences(local);
-            /**
-             * Check whether reference targets exist/were not removed
-             */
-            checkReferentialIntegrity(local);
+            virtualNodeReferences = new List[virtualProviders.length];
+
+            try {
+                events = factory.createEventStateCollection();
+            } catch (RepositoryException e) {
+                String msg = "Unable to create event state collection.";
+                log.error(msg);
+                throw new ItemStateException(msg, e);
+            }
+
+            acquireWriteLock();
 
             boolean succeeded = false;
 
             try {
+                if (usesReferences) {
+                    /**
+                     * Update node references based on modifications in change log
+                     * (added/modified/removed REFERENCE properties)
+                     */
+                    updateReferences(local, virtualProvider);
+                }
+
+                /**
+                 * Check whether reference targets exist/were not removed
+                 */
+                checkReferentialIntegrity(local);
+
                 /**
                  * Reconnect all items contained in the change log to their
                  * respective shared item and add the shared items to a
@@ -502,13 +531,32 @@
                 }
 
                 /* create event states */
-                if (events != null) {
-                    events.createEventStates(rootNodeUUID, local, this);
-                }
+                events.createEventStates(rootNodeUUID, local,
+                        SharedItemStateManager.this);
 
                 /* Push all changes from the local items to the shared items */
                 local.push();
 
+                succeeded = true;
+
+            } finally {
+                if (!succeeded) {
+                    cancel();
+                }
+            }
+        }
+
+        /**
+         * End update operation. This will store the changes to the associated
+         * <code>PersistenceManager</code>. At the end of this operation, an
+         * eventual read or write lock on the item state manager will have
+         * been released.
+         * @throws ItemStateException if some error occurs
+         */
+        public void end() throws ItemStateException {
+            boolean succeeded = false;
+
+            try {
                 /* Store items in the underlying persistence manager */
                 long t0 = System.currentTimeMillis();
                 persistMgr.store(shared);
@@ -517,73 +565,121 @@
                 if (log.isDebugEnabled()) {
                     log.debug("persisting change log " + shared + " took " + (t1 - t0) + "ms");
                 }
-
             } finally {
-
-                /**
-                 * If some store operation was unsuccessful, we have to reload
-                 * the state of modified and deleted items from persistent
-                 * storage.
-                 */
                 if (!succeeded) {
-                    local.disconnect();
+                    cancel();
+                }
+            }
 
-                    for (Iterator iter = shared.modifiedStates(); iter.hasNext();) {
-                        ItemState state = (ItemState) iter.next();
-                        try {
-                            state.copy(loadItemState(state.getId()));
-                        } catch (ItemStateException e) {
-                            state.discard();
-                        }
-                    }
-                    for (Iterator iter = shared.deletedStates(); iter.hasNext();) {
-                        ItemState state = (ItemState) iter.next();
-                        try {
-                            state.copy(loadItemState(state.getId()));
-                        } catch (ItemStateException e) {
-                            state.discard();
+            boolean holdingWriteLock = true;
+
+            try {
+                /* Let the shared item listeners know about the change */
+                shared.persisted();
+
+                /* notify virtual providers about node references */
+                for (int i = 0; i < virtualNodeReferences.length; i++) {
+                    List virtualRefs = virtualNodeReferences[i];
+                    if (virtualRefs != null) {
+                        for (Iterator iter = virtualRefs.iterator(); iter.hasNext();) {
+                            NodeReferences refs = (NodeReferences) iter.next();
+                            virtualProviders[i].setNodeReferences(refs);
                         }
                     }
-                    for (Iterator iter = shared.addedStates(); iter.hasNext();) {
-                        ItemState state = (ItemState) iter.next();
-                        state.discard();
-                    }
                 }
-            }
 
-            /* Let the shared item listeners know about the change */
-            shared.persisted();
+                // downgrade to read lock
+                acquireReadLock();
+                rwLock.writeLock().release();
+                holdingWriteLock = false;
 
-            /* notify virtual providers about node references */
-            for (int i = 0; i < virtualNodeReferences.length; i++) {
-                List virtualRefs = virtualNodeReferences[i];
-                if (virtualRefs != null) {
-                    for (Iterator iter = virtualRefs.iterator(); iter.hasNext();) {
-                        NodeReferences refs = (NodeReferences) iter.next();
-                        virtualProviders[i].setNodeReferences(refs);
-                    }
+                /* dispatch the events */
+                events.dispatch();
+
+            } finally {
+                if (holdingWriteLock) {
+                    // exception occured before downgrading lock
+                    rwLock.writeLock().release();
+                } else {
+                    rwLock.readLock().release();
                 }
             }
+        }
 
-            // downgrade to read lock
-            acquireReadLock();
-            rwLock.writeLock().release();
-            holdingWriteLock = false;
+        /**
+         * Cancel update operation. At the end of this operation, the write lock
+         * on the item state manager will have been released.
+         */
+        public void cancel() {
+            local.disconnect();
 
-            /* dispatch the events */
-            if (events != null) {
-                events.dispatch();
+            for (Iterator iter = shared.modifiedStates(); iter.hasNext();) {
+                ItemState state = (ItemState) iter.next();
+                try {
+                    state.copy(loadItemState(state.getId()));
+                } catch (ItemStateException e) {
+                    state.discard();
+                }
             }
-        } finally {
-            if (holdingWriteLock) {
-                // exception occured before downgrading lock
-                rwLock.writeLock().release();
-            } else {
-                rwLock.readLock().release();
+            for (Iterator iter = shared.deletedStates(); iter.hasNext();) {
+                ItemState state = (ItemState) iter.next();
+                try {
+                    state.copy(loadItemState(state.getId()));
+                } catch (ItemStateException e) {
+                    state.discard();
+                }
             }
+            for (Iterator iter = shared.addedStates(); iter.hasNext();) {
+                ItemState state = (ItemState) iter.next();
+                state.discard();
+            }
+            rwLock.writeLock().release();
         }
     }
 
+    /**
+     * Begin update operation. This will return an object that can itself be
+     * ended/cancelled.
+     */
+    public Update beginUpdate(ChangeLog local, EventStateCollectionFactory factory,
+                              VirtualItemStateProvider virtualProvider)
+            throws ReferentialIntegrityException, StaleItemStateException,
+                   ItemStateException {
+
+        Update update = new Update(local, factory, virtualProvider);
+        update.begin();
+        return update;
+    }
+
+    /**
+     * Store modifications registered in a <code>ChangeLog</code>. The items
+     * contained in the <tt>ChangeLog</tt> are not states returned by this
+     * item state manager but rather must be reconnected to items provided
+     * by this state manager.<p/>
+     * After successfully storing the states the observation manager is informed
+     * about the changes, if an observation manager is passed to this method.<p/>
+     * NOTE: This method is not synchronized, because all methods it invokes
+     * on instance members (such as {@link PersistenceManager#store} are
+     * considered to be thread-safe. Should this ever change, the
+     * synchronization status has to be re-examined.
+     *
+     * @param local   change log containing local items
+     * @param factory event state collection factory
+     * @throws ReferentialIntegrityException if a new or modified REFERENCE
+     *                                       property refers to a non-existent
+     *                                       target or if a removed node is still
+     *                                       being referenced
+     * @throws StaleItemStateException       if at least one of the affected item
+     *                                       states has become stale
+     * @throws ItemStateException            if another error occurs
+     */
+    public void update(ChangeLog local, EventStateCollectionFactory factory)
+            throws ReferentialIntegrityException, StaleItemStateException,
+                   ItemStateException {
+
+        beginUpdate(local, factory, null).end();
+    }
+
     //-------------------------------------------------------< implementation >
     /**
      * Create a new node state instance
@@ -801,9 +897,13 @@
      * anymore afterwards.
      *
      * @param changes change log
+     * @param virtualProvider virtual provider that may already contain a
+     *                        node references object
      * @throws ItemStateException if an error occurs
      */
-    protected void updateReferences(ChangeLog changes) throws ItemStateException {
+    protected void updateReferences(ChangeLog changes, 
+                                    VirtualItemStateProvider virtualProvider) 
+            throws ItemStateException {
 
         // process added REFERENCE properties
         for (Iterator iter = changes.addedStates(); iter.hasNext();) {
@@ -817,6 +917,10 @@
                     for (int i = 0; vals != null && i < vals.length; i++) {
                         String uuid = vals[i].toString();
                         NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        if (virtualProvider != null && 
+                                virtualProvider.hasNodeReferences(refsId)) {
+                            continue;
+                        }
                         NodeReferences refs =
                                 getOrCreateNodeReferences(refsId, changes);
                         // add reference
@@ -843,6 +947,10 @@
                     for (int i = 0; vals != null && i < vals.length; i++) {
                         String uuid = vals[i].toString();
                         NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        if (virtualProvider != null && 
+                                virtualProvider.hasNodeReferences(refsId)) {
+                            continue;
+                        }
                         // either get node references from change log or load from
                         // persistence manager
                         NodeReferences refs = changes.get(refsId);
@@ -863,6 +971,10 @@
                     for (int i = 0; vals != null && i < vals.length; i++) {
                         String uuid = vals[i].toString();
                         NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        if (virtualProvider != null && 
+                                virtualProvider.hasNodeReferences(refsId)) {
+                            continue;
+                        }
                         NodeReferences refs =
                                 getOrCreateNodeReferences(refsId, changes);
                         // add reference
@@ -886,6 +998,10 @@
                     for (int i = 0; vals != null && i < vals.length; i++) {
                         String uuid = vals[i].toString();
                         NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        if (virtualProvider != null && 
+                                virtualProvider.hasNodeReferences(refsId)) {
+                            continue;
+                        }
                         // either get node references from change log or
                         // load from persistence manager
                         NodeReferences refs = changes.get(refsId);

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java Wed Jan 11 06:22:57 2006
@@ -17,13 +17,19 @@
 package org.apache.jackrabbit.core.state;
 
 import org.apache.jackrabbit.core.ItemId;
-import org.apache.jackrabbit.core.WorkspaceImpl;
 import org.apache.jackrabbit.core.TransactionException;
 import org.apache.jackrabbit.core.TransactionContext;
 import org.apache.jackrabbit.core.InternalXAResource;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
 import org.apache.log4j.Logger;
 
 import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.PropertyType;
+import java.util.Iterator;
 
 /**
  * Extension to <code>LocalItemStateManager</code> that remembers changes on
@@ -59,34 +65,54 @@
     private transient ChangeLog txLog;
 
     /**
+     * Current update operation.
+     */
+    private transient SharedItemStateManager.Update update;
+
+    /**
      * Change log attribute name.
      */
     private final String attributeName;
 
     /**
+     * Optional virtual item state provider.
+     */
+    private VirtualItemStateProvider virtualProvider;
+
+    /**
      * Creates a new instance of this class.
+     *
      * @param sharedStateMgr shared state manager
-     * @param wspImpl workspace
+     * @param factory        event state collection factory
      */
     public XAItemStateManager(SharedItemStateManager sharedStateMgr,
-                              WorkspaceImpl wspImpl) {
-        this(sharedStateMgr, wspImpl, DEFAULT_ATTRIBUTE_NAME);
+                              EventStateCollectionFactory factory) {
+        this(sharedStateMgr, factory, DEFAULT_ATTRIBUTE_NAME);
     }
 
     /**
      * Creates a new instance of this class with a custom attribute name.
+     *
      * @param sharedStateMgr shared state manager
-     * @param wspImpl workspace
-     * @param attributeName attribute name
+     * @param factory        event state collection factory
+     * @param attributeName  attribute name
      */
     public XAItemStateManager(SharedItemStateManager sharedStateMgr,
-                              WorkspaceImpl wspImpl, String attributeName) {
-        super(sharedStateMgr, wspImpl);
+                              EventStateCollectionFactory factory,
+                              String attributeName) {
+        super(sharedStateMgr, factory);
 
         this.attributeName = attributeName;
     }
 
     /**
+     * Set optional virtual item state provider.
+     */
+    public void setVirtualProvider(VirtualItemStateProvider virtualProvider) {
+        this.virtualProvider = virtualProvider;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public void associate(TransactionContext tx) {
@@ -104,12 +130,24 @@
     /**
      * {@inheritDoc}
      */
+    public void beforeOperation(TransactionContext tx) {
+        ChangeLog txLog = (ChangeLog) tx.getAttribute(attributeName);
+        if (txLog != null) {
+            ((CommitLog) commitLog.get()).setChanges(txLog);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public void prepare(TransactionContext tx) throws TransactionException {
         ChangeLog txLog = (ChangeLog) tx.getAttribute(attributeName);
         if (txLog != null) {
             try {
-                ((CommitLog) commitLog.get()).setChanges(txLog);
-                sharedStateMgr.checkReferentialIntegrity(txLog);
+                if (virtualProvider != null) {
+                    updateVirtualReferences(txLog);
+                }
+                update = sharedStateMgr.beginUpdate(txLog, factory, virtualProvider);
             } catch (ReferentialIntegrityException rie) {
                 log.error(rie);
                 txLog.undo(sharedStateMgr);
@@ -118,8 +156,6 @@
                 log.error(ise);
                 txLog.undo(sharedStateMgr);
                 throw new TransactionException("Unable to prepare transaction.", ise);
-            } finally {
-                ((CommitLog) commitLog.get()).setChanges(null);
             }
         }
     }
@@ -131,18 +167,11 @@
         ChangeLog txLog = (ChangeLog) tx.getAttribute(attributeName);
         if (txLog != null) {
             try {
-                ((CommitLog) commitLog.get()).setChanges(txLog);
-                super.update(txLog);
-            } catch (ReferentialIntegrityException rie) {
-                log.error(rie);
-                txLog.undo(sharedStateMgr);
-                throw new TransactionException("Unable to commit transaction.", rie);
+                update.end();
             } catch (ItemStateException ise) {
                 log.error(ise);
                 txLog.undo(sharedStateMgr);
                 throw new TransactionException("Unable to commit transaction.", ise);
-            } finally {
-                ((CommitLog) commitLog.get()).setChanges(null);
             }
             txLog.reset();
         }
@@ -154,16 +183,21 @@
     public void rollback(TransactionContext tx) {
         ChangeLog txLog = (ChangeLog) tx.getAttribute(attributeName);
         if (txLog != null) {
-            try {
-                ((CommitLog) commitLog.get()).setChanges(txLog);
-                txLog.undo(sharedStateMgr);
-            } finally {
-                ((CommitLog) commitLog.get()).setChanges(null);
+            if (update != null) {
+                update.cancel();
             }
+            txLog.undo(sharedStateMgr);
         }
     }
 
     /**
+     * {@inheritDoc}
+     */
+    public void afterOperation(TransactionContext tx) {
+        ((CommitLog) commitLog.get()).setChanges(null);
+    }
+
+    /**
      * Returns the current change log. First tries thread-local change log,
      * then instance-local change log. Returns <code>null</code> if no
      * change log was found.
@@ -188,6 +222,9 @@
     public ItemState getItemState(ItemId id)
             throws NoSuchItemStateException, ItemStateException {
 
+        if (virtualProvider != null && virtualProvider.hasItemState(id)) {
+            return virtualProvider.getItemState(id);
+        }
         ChangeLog changeLog = getChangeLog();
         if (changeLog != null) {
             ItemState state = changeLog.get(id);
@@ -207,6 +244,9 @@
      * class.
      */
     public boolean hasItemState(ItemId id) {
+        if (virtualProvider != null && virtualProvider.hasItemState(id)) {
+            return true;
+        }
         ChangeLog changeLog = getChangeLog();
         if (changeLog != null) {
             try {
@@ -232,6 +272,9 @@
     public NodeReferences getNodeReferences(NodeReferencesId id)
             throws NoSuchItemStateException, ItemStateException {
 
+        if (virtualProvider != null && virtualProvider.hasNodeReferences(id)) {
+            return virtualProvider.getNodeReferences(id);
+        }
         ChangeLog changeLog = getChangeLog();
         if (changeLog != null) {
             NodeReferences refs = changeLog.get(id);
@@ -251,6 +294,9 @@
      * the base class.
      */
     public boolean hasNodeReferences(NodeReferencesId id) {
+        if (virtualProvider != null && virtualProvider.hasNodeReferences(id)) {
+            return true;
+        }
         ChangeLog changeLog = getChangeLog();
         if (changeLog != null) {
             if (changeLog.get(id) != null) {
@@ -274,6 +320,111 @@
             txLog.merge(changeLog);
         } else {
             super.update(changeLog);
+        }
+    }
+
+    //-------------------------------------------------------< implementation >
+
+    /**
+     * Determine all node references whose targets only exist in the view of
+     * this transaction and store the modified view back to the virtual provider.
+     * @param changes change log
+     * @throws ItemStateException if an error occurs
+     */
+    private void updateVirtualReferences(ChangeLog changes) throws ItemStateException {
+        for (Iterator iter = changes.addedStates(); iter.hasNext();) {
+            ItemState state = (ItemState) iter.next();
+            if (!state.isNode()) {
+                PropertyState prop = (PropertyState) state;
+                if (prop.getType() == PropertyType.REFERENCE) {
+                    InternalValue[] vals = prop.getValues();
+                    for (int i = 0; vals != null && i < vals.length; i++) {
+                        String uuid = vals[i].toString();
+                        NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        addVirtualReference((PropertyId) prop.getId(), refsId);
+                    }
+                }
+            }
+        }
+        for (Iterator iter = changes.modifiedStates(); iter.hasNext();) {
+            ItemState state = (ItemState) iter.next();
+            if (!state.isNode()) {
+                PropertyState newProp = (PropertyState) state;
+                PropertyState oldProp =
+                        (PropertyState) getItemState(state.getId());
+                if (oldProp.getType() == PropertyType.REFERENCE) {
+                    InternalValue[] vals = oldProp.getValues();
+                    for (int i = 0; vals != null && i < vals.length; i++) {
+                        String uuid = vals[i].toString();
+                        NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        removeVirtualReference((PropertyId) oldProp.getId(), refsId);
+                    }
+                }
+                if (newProp.getType() == PropertyType.REFERENCE) {
+                    InternalValue[] vals = newProp.getValues();
+                    for (int i = 0; vals != null && i < vals.length; i++) {
+                        String uuid = vals[i].toString();
+                        NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        addVirtualReference((PropertyId) newProp.getId(), refsId);
+                    }
+                }
+            }
+        }
+        for (Iterator iter = changes.deletedStates(); iter.hasNext();) {
+            ItemState state = (ItemState) iter.next();
+            if (!state.isNode()) {
+                PropertyState prop = (PropertyState) state;
+                if (prop.getType() == PropertyType.REFERENCE) {
+                    InternalValue[] vals = prop.getValues();
+                    for (int i = 0; vals != null && i < vals.length; i++) {
+                        String uuid = vals[i].toString();
+                        NodeReferencesId refsId = new NodeReferencesId(uuid);
+                        removeVirtualReference((PropertyId) prop.getId(), refsId);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Add a virtual reference from some reference property to a virtual node.
+     * Ignored if <code>targetId</code> does not actually point to a virtual
+     * node.
+     * @param sourceId property id
+     * @param targetId node references id
+     */
+    private void addVirtualReference(PropertyId sourceId,
+                                     NodeReferencesId targetId)
+            throws NoSuchItemStateException, ItemStateException {
+
+        NodeReferences refs = virtualProvider.getNodeReferences(targetId);
+        if (refs == null && virtualProvider.hasItemState(new NodeId(targetId.getUUID()))) {
+            refs = new NodeReferences(targetId);
+        }
+        if (refs != null) {
+            refs.addReference(sourceId);
+            virtualProvider.setNodeReferences(refs);
+        }
+    }
+
+    /**
+     * Remove a virtual reference from some reference property to a virtual node.
+     * Ignored if <code>targetId</code> does not actually point to a virtual
+     * node.
+     * @param sourceId property id
+     * @param targetId node references id
+     */
+    private void removeVirtualReference(PropertyId sourceId,
+                                        NodeReferencesId targetId)
+            throws NoSuchItemStateException, ItemStateException {
+
+        NodeReferences refs = virtualProvider.getNodeReferences(targetId);
+        if (refs == null && virtualProvider.hasItemState(new NodeId(targetId.getUUID()))) {
+            refs = new NodeReferences(targetId);
+        }
+        if (refs != null) {
+            refs.removeReference(sourceId);
+            virtualProvider.setNodeReferences(refs);
         }
     }