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 [3/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...

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java Wed Jan 11 06:22:57 2006
@@ -20,12 +20,11 @@
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.PropertyId;
-import org.apache.jackrabbit.core.PropertyImpl;
 import org.apache.jackrabbit.core.SessionImpl;
-import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.observation.DelegatingObservationDispatcher;
-import org.apache.jackrabbit.core.observation.EventState;
+import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
+import org.apache.jackrabbit.core.observation.EventStateCollection;
 import org.apache.jackrabbit.core.state.ChangeLog;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.LocalItemStateManager;
@@ -37,17 +36,12 @@
 import org.apache.jackrabbit.core.state.SharedItemStateManager;
 import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
-import org.apache.jackrabbit.name.Path;
 import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.uuid.UUID;
 import org.apache.log4j.Logger;
 
-import javax.jcr.NodeIterator;
-import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.Value;
 import javax.jcr.ReferentialIntegrityException;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionException;
@@ -56,12 +50,13 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.LinkedList;
+import java.util.Collection;
 
 /**
  * This Class implements a VersionManager.
  */
-public class VersionManagerImpl implements VersionManager {
+public class VersionManagerImpl extends AbstractVersionManager
+        implements EventStateCollectionFactory {
 
     /**
      * the default logger
@@ -79,9 +74,9 @@
     private final PersistenceManager pMgr;
 
     /**
-     * the state manager for the version storage
+     * the shared state manager for the version storage
      */
-    private LocalItemStateManager stateMgr;
+    private SharedItemStateManager sharedStateMgr;
 
     /**
      * the virtual item state provider that exposes the version storage
@@ -89,11 +84,6 @@
     private final VersionItemStateProvider versProvider;
 
     /**
-     * the persistent root node of the version histories
-     */
-    private final NodeStateEx historyRoot;
-
-    /**
      * the node type manager
      */
     private NodeTypeRegistry ntReg;
@@ -106,12 +96,12 @@
     /**
      * Map of returned items. this is kept for invalidating
      */
-    //private ReferenceMap items = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
+    private ReferenceMap versionItems = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
 
     /**
-     * Map of returned items. this is kept for invalidating
+     * Session to be used when creating observation events.
      */
-    private ReferenceMap versionItems = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
+    private transient SessionImpl eventSource;
 
     /**
      * Creates a bew vesuion manager
@@ -145,9 +135,9 @@
                 cl.added(pt);
                 pMgr.store(cl);
             }
-            SharedItemStateManager sharedStateMgr =
+            sharedStateMgr =
                     new VersionItemStateManager(pMgr, VERSION_STORAGE_NODE_UUID, ntReg);
-            stateMgr = new LocalItemStateManager(sharedStateMgr, null);
+            stateMgr = new LocalItemStateManager(sharedStateMgr, this);
             NodeState nodeState = (NodeState) stateMgr.getItemState(new NodeId(VERSION_STORAGE_NODE_UUID));
             historyRoot = new NodeStateEx(stateMgr, ntReg, nodeState, QName.JCR_VERSIONSTORAGE);
 
@@ -167,212 +157,47 @@
     }
 
     /**
-     * Close this version manager. After having closed a persistence
-     * manager, further operations on this object are treated as illegal
-     * and throw
-     *
-     * @throws Exception if an error occurs
+     * {@inheritDoc}
      */
     public void close() throws Exception {
         pMgr.close();
     }
 
     /**
-     * Creates a new version history. This action is needed either when creating
-     * a new 'mix:versionable' node or when adding the 'mix:versionalbe' mixin
-     * to a node.
-     *
-     * @param node
-     * @return
-     * @throws javax.jcr.RepositoryException
+     * {@inheritDoc}
+     * <p/>
+     * This method needs to be synchronized since it sets the event source
+     * to be used when creating the events to be dispatched later on.
      */
-    public VersionHistory createVersionHistory(Session session, NodeState node)
+    public synchronized VersionHistory createVersionHistory(Session session,
+                                                            NodeState node)
             throws RepositoryException {
 
-        List created = new LinkedList();
-        InternalVersionHistory history = createVersionHistory(created, node);
+        eventSource = (SessionImpl) session;
+
+        InternalVersionHistory history = createVersionHistory(node);
         if (history == null) {
             throw new VersionException("History already exists for node " + node.getUUID());
         }
-        VersionHistoryImpl vh = (VersionHistoryImpl) session.getNodeByUUID(history.getId());
-
-        // generate observation events
-        List events = new ArrayList();
-        Iterator iter = created.iterator();
-        while (iter.hasNext()) {
-            String uuid = (String) iter.next();
-            NodeImpl child = (NodeImpl) ((SessionImpl) session).getItemManager().getItem(new NodeId(uuid));
-            generateAddedEvents(events, (NodeImpl) child.getParent(), child, false);
-        }
-        obsMgr.dispatch(events, (SessionImpl) session);
-        return vh;
+        return (VersionHistory) session.getNodeByUUID(history.getId());
     }
 
     /**
      * {@inheritDoc}
      */
-    public VersionHistory getVersionHistory(Session session, NodeState node)
-            throws RepositoryException {
-
-        String vhId = getVersionHistoryId(node);
-        if (vhId == null) {
-            return null;
-        }
-        return (VersionHistoryImpl) session.getNodeByUUID(vhId);
-    }
-
-    /**
-     * Creates a new Version History.
-     *
-     * @param created a list for adding the uuids of the newly created nodes
-     * @param node the node for which the version history is to be initialized
-     * @return the newly created version history.
-     * @throws RepositoryException
-     */
-    private InternalVersionHistory createVersionHistory(List created, NodeState node)
-            throws RepositoryException {
-
-        try {
-            stateMgr.edit();
-        } catch (IllegalStateException e) {
-            throw new RepositoryException("Unable to start edit operation", e);
-        }
-
-        boolean succeeded = false;
-
-        try {
-            // create deep path
-            String uuid = node.getUUID();
-            NodeStateEx root = historyRoot;
-            for (int i = 0; i < 3; i++) {
-                QName name = new QName(QName.NS_DEFAULT_URI, uuid.substring(i * 2, i * 2 + 2));
-                if (!root.hasNode(name)) {
-                    NodeStateEx n = root.addNode(name, QName.REP_VERSIONSTORAGE, null, false);
-                    created.add(n.getUUID());
-                    root.store();
-                }
-                root = root.getNode(name, 1);
-            }
-            QName historyNodeName = new QName(QName.NS_DEFAULT_URI, uuid);
-            if (root.hasNode(historyNodeName)) {
-                // already exists
-                return null;
-            }
-
-            // create new history node in the persistent state
-            InternalVersionHistoryImpl hist = InternalVersionHistoryImpl.create(this, root, UUID.randomUUID().toString(), historyNodeName, node, created);
-
-            // end update
-            stateMgr.update();
-            succeeded = true;
-
-            log.info("Created new version history " + hist.getId() + " for " + node + ".");
-            return hist;
-
-        } catch (ItemStateException e) {
-            throw new RepositoryException(e);
-        } finally {
-            if (!succeeded) {
-                // update operation failed, cancel all modifications
-                stateMgr.cancel();
-            }
-        }
-    }
-
-    /**
-     * Returns the id of the version history associated with the given node
-     * or <code>null</code> if that node doesn't have a version history.
-     *
-     * @param node the node whose version history's id is to be returned.
-     * @return the the id of the version history associated with the given node
-     *         or <code>null</code> if that node doesn't have a version history.
-     * @throws RepositoryException if an error occurs
-     */
-    private String getVersionHistoryId(NodeState node)
-            throws RepositoryException {
-
-        // build and traverse path
-        String uuid = node.getUUID();
-        NodeStateEx n = historyRoot;
-        for (int i = 0; i < 3; i++) {
-            QName name = new QName(QName.NS_DEFAULT_URI, uuid.substring(i * 2, i * 2 + 2));
-            if (!n.hasNode(name)) {
-                return null;
-            }
-            n = n.getNode(name, 1);
-        }
-        QName historyNodeName = new QName(QName.NS_DEFAULT_URI, uuid);
-        if (!n.hasNode(historyNodeName)) {
-            return null;
-        }
-        return n.getNode(historyNodeName, 1).getUUID();
-    }
-
-    /**
-     * Checks if the version history with the given id exists
-     *
-     * @param id
-     * @return
-     */
-    public boolean hasVersionHistory(String id) {
-        // todo: probably need to check if this item is really a history
-        return hasItem(id);
-    }
-
-    /**
-     * Returns the version history with the given id
-     *
-     * @param id
-     * @return
-     * @throws RepositoryException
-     */
-    public InternalVersionHistory getVersionHistory(String id) throws RepositoryException {
-        return (InternalVersionHistory) getItem(id);
-    }
-
-    /**
-     * Checks if the version with the given id exists
-     *
-     * @param id
-     * @return
-     */
-    public boolean hasVersion(String id) {
-        // todo: probably need to check if this item is really a version
-        return hasItem(id);
-    }
-
-    /**
-     * Returns the version with the given id
-     *
-     * @param id
-     * @return
-     * @throws RepositoryException
-     */
-    public InternalVersion getVersion(String id) throws RepositoryException {
-        return (InternalVersion) getItem(id);
-    }
-
-    /**
-     * checks, if the node with the given id exists
-     *
-     * @param id
-     * @return
-     */
     public boolean hasItem(String id) {
         return versionItems.containsKey(id) || stateMgr.hasItemState(new NodeId(id));
     }
 
     /**
-     * Returns the item with the given persistent id
-     *
-     * @param uuid
-     * @return
-     * @throws RepositoryException
+     * {@inheritDoc}
      */
-    synchronized InternalVersionItem getItem(String uuid) throws RepositoryException {
+    protected synchronized InternalVersionItem getItem(String uuid)
+            throws RepositoryException {
+
         NodeId id = new NodeId(uuid);
         try {
-            InternalVersionItem item = (InternalVersionItem) versionItems.get(id);
+            InternalVersionItem item = (InternalVersionItem) versionItems.get(uuid);
             if (item == null) {
                 if (stateMgr.hasItemState(id)) {
                     NodeState state = (NodeState) stateMgr.getItemState(id);
@@ -394,7 +219,7 @@
                     }
                 }
                 if (item != null) {
-                    versionItems.put(id, item);
+                    versionItems.put(uuid, item);
                 }
             }
             return item;
@@ -404,310 +229,113 @@
     }
 
     /**
-     * invokes the checkin() on the persistent version manager and remaps the
-     * newly created version objects.
-     *
-     * @param node
-     * @return
-     * @throws RepositoryException
+     * {@inheritDoc}
+     * <p/>
+     * This method needs to be synchronized since it sets the event source
+     * to be used when creating the events to be dispatched later on.
      */
-    public Version checkin(NodeImpl node) throws RepositoryException {
-        SessionImpl session = (SessionImpl) node.getSession();
-        InternalVersion version = internalCheckin(node);
-
-        VersionImpl v = (VersionImpl) session.getNodeByUUID(version.getId());
+    public synchronized Version checkin(NodeImpl node) throws RepositoryException {
+        eventSource = (SessionImpl) node.getSession();
 
-        // generate observation events
-        List events = new ArrayList();
+        String histUUID = node.getProperty(QName.JCR_VERSIONHISTORY).getString();
+        InternalVersion version = checkin(
+                (InternalVersionHistoryImpl) getVersionHistory(histUUID), node);
 
-        generateAddedEvents(events, (NodeImpl) v.getParent(), v, true);
+        AbstractVersion v = (AbstractVersion) eventSource.getNodeByUUID(version.getId());
 
-        // invalidate predecessors successor property
+        // invalidate predecessors successor properties
         InternalVersion[] preds = version.getPredecessors();
-        for (int i=0; i<preds.length; i++) {
+        for (int i = 0; i < preds.length; i++) {
             PropertyId propId = new PropertyId(preds[i].getId(), QName.JCR_SUCCESSORS);
             versProvider.onPropertyChanged(propId);
         }
-        obsMgr.dispatch(events, session);
-
         return v;
     }
 
     /**
-     * Checks in a node
-     *
-     * @param node
-     * @return
-     * @throws RepositoryException
-     * @see javax.jcr.Node#checkin()
-     */
-    private synchronized InternalVersion internalCheckin(NodeImpl node) throws RepositoryException {
-        // assuming node is versionable and checkout (check in nodeimpl)
-        // To create a new version of a versionable node N, the client calls N.checkin.
-        // This causes the following series of events:
-        String histUUID = node.getProperty(QName.JCR_VERSIONHISTORY).getString();
-        InternalVersionHistoryImpl history = (InternalVersionHistoryImpl) getVersionHistory(histUUID);
-
-        // 0. resolve the predecessors
-        Value[] values = node.getProperty(QName.JCR_PREDECESSORS).getValues();
-        InternalVersion[] preds = new InternalVersion[values.length];
-        for (int i = 0; i < values.length; i++) {
-            preds[i] = history.getVersion(values[i].getString());
-        }
-
-        // 0.1 search a predecessor, suitable for generating the new name
-        String versionName = null;
-        int maxDots = Integer.MAX_VALUE;
-        for (int i = 0; i < preds.length; i++) {
-            // take the first pred. without a successor
-            if (preds[i].getSuccessors().length == 0) {
-                versionName = preds[i].getName().getLocalName(); //assuming no namespaces in version names
-                // need to count the dots
-                int pos = -1;
-                int numDots = 0;
-                while (versionName.indexOf('.', pos + 1) >= 0) {
-                    pos = versionName.indexOf('.', pos + 1);
-                    numDots++;
-                }
-                if (numDots < maxDots) {
-                    maxDots = numDots;
-                    if (pos < 0) {
-                        versionName = "1.0";
-                    } else {
-                        versionName = versionName.substring(0, pos + 1)
-                                + (Integer.parseInt(versionName.substring(pos + 1)) + 1);
-                    }
-                }
-                break;
-            }
-        }
-        // if no empty found, generate new name
-        if (versionName == null) {
-            versionName = preds[0].getName().getLocalName();
-            do {
-                versionName += ".1";
-            } while (history.hasVersion(new QName("", versionName)));
-        }
-
-        try {
-            stateMgr.edit();
-        } catch (IllegalStateException e) {
-            throw new RepositoryException("Unable to start edit operation.");
-        }
-
-        boolean succeeded = false;
-
-        try {
-            InternalVersionImpl v = history.checkin(new QName("", versionName), node);
-            stateMgr.update();
-            succeeded = true;
-
-            return v;
-        } catch (ItemStateException e) {
-            throw new RepositoryException(e);
-        } finally {
-            if (!succeeded) {
-                // update operation failed, cancel all modifications
-                stateMgr.cancel();
-            }
-        }
-    }
-
-
-    /**
-     * Removes the specified version from the history
-     *
-     * @param history the version history from where to remove the version.
-     * @param name the name of the version to remove.
-     * @throws VersionException if the version <code>history</code> does
-     *  not have a version with <code>name</code>.
-     * @throws RepositoryException if any other error occurs.
+     * {@inheritDoc}
+     * <p/>
+     * This method needs to be synchronized since it sets the event source
+     * to be used when creating the events to be dispatched later on.
      */
-    public void removeVersion(VersionHistory history, QName name)
+    public synchronized void removeVersion(VersionHistory history, QName name)
             throws VersionException, RepositoryException {
-        if (!((VersionHistoryImpl) history).hasNode(name)) {
+
+        AbstractVersionHistory historyImpl = (AbstractVersionHistory) history;
+        if (!historyImpl.hasNode(name)) {
             throw new VersionException("Version with name " + name.toString()
                     + " does not exist in this VersionHistory");
         }
-        // generate observation events
-        SessionImpl session = (SessionImpl) history.getSession();
-        VersionImpl version = (VersionImpl) ((VersionHistoryImpl) history).getNode(name);
-        List events = new ArrayList();
-        generateRemovedEvents(events, (NodeImpl) history, version, true);
+        eventSource = (SessionImpl) history.getSession();
 
-        InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
-                ((VersionHistoryImpl) history).getInternalVersionHistory();
+        // save away predecessors before removing version
+        AbstractVersion version = (AbstractVersion) historyImpl.getNode(name);
+        InternalVersion preds[] = version.getInternalVersion().getPredecessors();
 
-        try {
-            stateMgr.edit();
-        } catch (IllegalStateException e) {
-            throw new VersionException("Unable to start edit operation", e);
-        }
-        boolean succeeded = false;
-        try {
-            vh.removeVersion(name);
-            stateMgr.update();
-            succeeded = true;
-        } catch (ItemStateException e) {
-            log.error("Error while storing: " + e.toString());
-        } finally {
-            if (!succeeded) {
-                // update operation failed, cancel all modifications
-                stateMgr.cancel();
-            }
-        }
+        InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
+                historyImpl.getInternalVersionHistory();
+        removeVersion(vh, name);
 
         // invalidate predecessors successor properties
-        InternalVersion preds[] = version.getInternalVersion().getPredecessors();
-        for (int i=0; i<preds.length; i++) {
+        for (int i = 0; i < preds.length; i++) {
             PropertyId propId = new PropertyId(preds[i].getId(), QName.JCR_SUCCESSORS);
             versProvider.onPropertyChanged(propId);
         }
-        obsMgr.dispatch(events, session);
     }
 
     /**
      * {@inheritDoc}
-     */
-    public Version setVersionLabel(VersionHistory history, QName version,
-                                   QName label, boolean move)
+     * <p/>
+     * This method needs to be synchronized since it sets the event source
+     * to be used when creating the events to be dispatched later on.
+     */
+    public synchronized Version setVersionLabel(VersionHistory history,
+                                                QName version, QName label,
+                                                boolean move)
             throws RepositoryException {
-        SessionImpl session = (SessionImpl) history.getSession();
-
-        InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
-                ((VersionHistoryImpl) history).getInternalVersionHistory();
-        NodeImpl labelNode = ((VersionHistoryImpl) history).getNode(QName.JCR_VERSIONLABELS);
 
-        try {
-            stateMgr.edit();
-        } catch (IllegalStateException e) {
-            throw new VersionException("Unable to start edit operation", e);
-        }
-        InternalVersion v = null;
-        boolean success = false;
-        try {
-            v = vh.setVersionLabel(version, label, move);
-            stateMgr.update();
-            success = true;
-        } catch(ItemStateException e) {
-            log.error("Error while storing: " + e.toString());
-        } finally {
-            if (!success) {
-                // update operation failed, cancel all modifications
-                stateMgr.cancel();
-            }
-        }
+        AbstractVersionHistory historyImpl = (AbstractVersionHistory) history;
+        eventSource = (SessionImpl) history.getSession();
 
-        // collect observation events
-        List events = new ArrayList();
-        if (version == null && v != null) {
-            // label removed
-            events.add(EventState.propertyRemoved(
-                    labelNode.internalGetUUID(),
-                    labelNode.getPrimaryPath(),
-                    Path.PathElement.fromString(label.toString()),
-                    (NodeTypeImpl) labelNode.getPrimaryNodeType(),
-                    labelNode.getMixinTypeNames(),
-                    labelNode.getSession()
-            ));
-        } else if (v == null) {
-            // label added
-            events.add(EventState.propertyAdded(
-                    labelNode.internalGetUUID(),
-                    labelNode.getPrimaryPath(),
-                    Path.PathElement.fromString(label.toString()),
-                    (NodeTypeImpl) labelNode.getPrimaryNodeType(),
-                    labelNode.getMixinTypeNames(),
-                    labelNode.getSession()
-            ));
-        } else {
-            // label modified
-            events.add(EventState.propertyChanged(
-                    labelNode.internalGetUUID(),
-                    labelNode.getPrimaryPath(),
-                    Path.PathElement.fromString(label.toString()),
-                    (NodeTypeImpl) labelNode.getPrimaryNodeType(),
-                    labelNode.getMixinTypeNames(),
-                    labelNode.getSession()
-            ));
-        }
-        obsMgr.dispatch(events, session);
+        InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
+                historyImpl.getInternalVersionHistory();
+        InternalVersion v = setVersionLabel(vh, version, label, move);
         if (v == null) {
             return null;
         } else {
-            return (VersionImpl) session.getNodeByUUID(v.getId());
+            return (Version) eventSource.getNodeByUUID(v.getId());
         }
     }
 
     /**
-     * Adds a subtree of itemstates as 'added' to a list of events
-     *
-     * @param events
-     * @param parent
-     * @param node
-     * @throws RepositoryException
+     * Invoked by some external source to indicate that some items in the
+     * versions tree were updated. Version manager should flush its own
+     * caches.
+     * @param items items updated
      */
-    private void generateAddedEvents(List events, NodeImpl parent, NodeImpl node,
-                                     boolean recursive)
-            throws RepositoryException {
-
-        events.add(EventState.childNodeAdded(
-                parent.internalGetUUID(),
-                parent.getPrimaryPath(),
-                node.internalGetUUID(),
-                node.getPrimaryPath().getNameElement(),
-                (NodeTypeImpl) parent.getPrimaryNodeType(),
-                parent.getMixinTypeNames(),
-                node.getSession()
-        ));
-
-        PropertyIterator iter = node.getProperties();
+    public void itemsUpdated(Collection items) {
+        Iterator iter = items.iterator();
         while (iter.hasNext()) {
-            PropertyImpl prop = (PropertyImpl) iter.nextProperty();
-            events.add(EventState.propertyAdded(
-                    node.internalGetUUID(),
-                    node.getPrimaryPath(),
-                    prop.getPrimaryPath().getNameElement(),
-                    (NodeTypeImpl) node.getPrimaryNodeType(),
-                    node.getMixinTypeNames(),
-                    node.getSession()
-            ));
-        }
-        if (recursive) {
-            NodeIterator niter = node.getNodes();
-            while (niter.hasNext()) {
-                NodeImpl n = (NodeImpl) niter.nextNode();
-                generateAddedEvents(events, node, n, true);
-            }
+            itemUpdated((InternalVersionItem) iter.next());
         }
     }
 
     /**
-     * Adds a subtree of itemstates as 'removed' to a list of events
-     *
-     * @param events
-     * @param parent
-     * @param node
-     * @throws RepositoryException
+     * Update internal version item. Version histories are reloaded if possible.
+     * Matching items are removed from the cache.
+     * @param item item updated
      */
-    private void generateRemovedEvents(List events, NodeImpl parent,
-                                       NodeImpl node, boolean recursive)
-            throws RepositoryException {
-
-        events.add(EventState.childNodeRemoved(
-                parent.internalGetUUID(),
-                parent.getPrimaryPath(),
-                node.internalGetUUID(),
-                node.getPrimaryPath().getNameElement(),
-                (NodeTypeImpl) parent.getPrimaryNodeType(),
-                parent.getMixinTypeNames(),
-                node.getSession()
-        ));
-        if (recursive) {
-            NodeIterator niter = node.getNodes();
-            while (niter.hasNext()) {
-                NodeImpl n = (NodeImpl) niter.nextNode();
-                generateRemovedEvents(events, node, n, true);
+    private void itemUpdated(InternalVersionItem item) {
+        InternalVersionItem cached = (InternalVersionItem) versionItems.remove(item.getId());
+        if (cached != null) {
+            if (cached instanceof InternalVersionHistoryImpl) {
+                InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl) cached;
+                try {
+                    vh.reload();
+                    versionItems.put(vh.getId(), vh);
+                } catch (RepositoryException e) {
+                    log.warn("Unable to update version history: " + e.toString());
+                }
             }
         }
     }
@@ -742,7 +370,7 @@
     /**
      * {@inheritDoc}
      */
-    public List getItemReferences(InternalVersionItem item) {
+    protected List getItemReferences(InternalVersionItem item) {
         try {
             NodeReferences refs = pMgr.load(new NodeReferencesId(item.getId()));
             return refs.getReferences();
@@ -776,6 +404,32 @@
         return (NodeId) historyRoot.getState().getId();
     }
 
+    /**
+     * Return the shared item state manager.
+     */
+    SharedItemStateManager getSharedStateMgr() {
+        return sharedStateMgr;
+    }
+
+    //------------------------------------------< EventStateCollectionFactory >
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This object uses one instance of a <code>LocalItemStateManager</code>
+     * to update data on behalf of many sessions. In order to maintain the
+     * association between update operation and session who actually invoked
+     * the update, an internal event source is used.
+     */
+    public synchronized EventStateCollection createEventStateCollection()
+            throws RepositoryException {
+
+        if (eventSource == null) {
+            throw new RepositoryException("Unknown event source.");
+        }
+        return obsMgr.createEventStateCollection(eventSource);
+    }
+
     //--------------------------------------------------------< inner classes >
     /**
      * Spezialized SharedItemStateManager that filters out NodeReferences to
@@ -787,7 +441,7 @@
                                        String rootNodeUUID,
                                        NodeTypeRegistry ntReg)
                 throws ItemStateException {
-            super(persistMgr, rootNodeUUID, ntReg);
+            super(persistMgr, rootNodeUUID, ntReg, false);
         }
 
         protected void checkReferentialIntegrity(ChangeLog changes)

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersion.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersion.java?rev=368026&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersion.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersion.java Wed Jan 11 06:22:57 2006
@@ -0,0 +1,95 @@
+/*
+ * 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.version;
+
+import org.apache.jackrabbit.core.ItemLifeCycleListener;
+import org.apache.jackrabbit.core.ItemManager;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.state.NodeState;
+
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.RepositoryException;
+import javax.jcr.InvalidItemStateException;
+
+/**
+ * Implementation of a {@link javax.jcr.version.Version} that works in an
+ * XA environment.
+ */
+public class XAVersion extends AbstractVersion {
+
+    /**
+     * Internal version. Gets fetched again from the version manager if
+     * needed.
+     */
+    private InternalVersion version;
+
+    /**
+     * XA Version manager.
+     */
+    private final XAVersionManager vMgr;
+
+    /**
+     * Create a new instance of this class.
+     * @param itemMgr item manager
+     * @param session session
+     * @param id node id
+     * @param state node state
+     * @param definition node definition
+     * @param listeners life cycle listeners
+     */
+    public XAVersion(ItemManager itemMgr, SessionImpl session, NodeId id,
+                     NodeState state, NodeDefinition definition,
+                     ItemLifeCycleListener[] listeners,
+                     InternalVersion version) {
+        super(itemMgr, session, id, state, definition, listeners);
+
+        this.version = version;
+        this.vMgr = (XAVersionManager) session.getVersionManager();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected InternalVersion getInternalVersion() throws RepositoryException {
+        ensureUpToDate();
+        sanityCheck();
+        return version;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void sanityCheck() throws RepositoryException {
+        super.sanityCheck();
+
+        if (version == null) {
+            throw new InvalidItemStateException(id + ": the item does not exist anymore");
+        }
+    }
+
+    /**
+     * Ensure the internal version is up-to-date.
+     */
+    private synchronized void ensureUpToDate() throws RepositoryException {
+        if (version != null) {
+            if (vMgr.differentXAEnv((InternalVersionImpl) version)) {
+                version = vMgr.getVersion(version.getId());
+            }
+        }
+    }
+}

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

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

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

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionHistory.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionHistory.java?rev=368026&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionHistory.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionHistory.java Wed Jan 11 06:22:57 2006
@@ -0,0 +1,98 @@
+/*
+ * 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.version;
+
+import org.apache.jackrabbit.core.ItemLifeCycleListener;
+import org.apache.jackrabbit.core.ItemManager;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.state.NodeState;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.nodetype.NodeDefinition;
+
+/**
+ * Implementation of a {@link javax.jcr.version.VersionHistory} that works in an
+ * XA environment.
+ */
+public class XAVersionHistory extends AbstractVersionHistory {
+
+    /**
+     * Internal version history. Gets fetched again from the version manager if
+     * needed.
+     */
+    private InternalVersionHistory history;
+
+    /**
+     * XA Version manager.
+     */
+    private final XAVersionManager vMgr;
+
+    /**
+     * Create a new instance of this class.
+     * @param itemMgr item manager
+     * @param session session
+     * @param id node id
+     * @param state node state
+     * @param definition node definition
+     * @param listeners life cycle listeners
+     * @param history internal version history
+     */
+    public XAVersionHistory(ItemManager itemMgr, SessionImpl session, NodeId id,
+                            NodeState state, NodeDefinition definition,
+                            ItemLifeCycleListener[] listeners,
+                            InternalVersionHistory history) {
+        super(itemMgr, session, id, state, definition, listeners);
+
+        this.history = history;
+        this.vMgr = (XAVersionManager) session.getVersionManager();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected InternalVersionHistory getInternalVersionHistory()
+            throws RepositoryException {
+
+        ensureUpToDate();
+        sanityCheck();
+        return history;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void sanityCheck() throws RepositoryException {
+        super.sanityCheck();
+
+        if (history == null) {
+            throw new InvalidItemStateException(id + ": the item does not exist anymore");
+        }
+    }
+
+    /**
+     * Ensure the internal version is up-to-date.
+     */
+    private synchronized void ensureUpToDate() throws RepositoryException {
+        if (history != null) {
+            if (vMgr.differentXAEnv(((InternalVersionHistoryImpl) history))) {
+                history = vMgr.getVersionHistory(history.getId());
+            }
+        }
+    }
+}

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

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

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

Added: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java?rev=368026&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java (added)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java Wed Jan 11 06:22:57 2006
@@ -0,0 +1,487 @@
+/*
+ * 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.version;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.TransactionContext;
+import org.apache.jackrabbit.core.TransactionException;
+import org.apache.jackrabbit.core.InternalXAResource;
+import org.apache.jackrabbit.core.observation.EventStateCollectionFactory;
+import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.XAItemStateManager;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
+import org.apache.jackrabbit.core.virtual.VirtualPropertyState;
+import org.apache.jackrabbit.core.virtual.VirtualNodeState;
+import org.apache.jackrabbit.name.QName;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Implementation of a {@link VersionManager} that works in an XA environment.
+ * Works as a filter between a version manager client and the global version
+ * manager.
+ */
+public class XAVersionManager extends AbstractVersionManager
+        implements VirtualItemStateProvider, InternalXAResource {
+
+    /**
+     * Attribute name for associated change log.
+     */
+    private static final String CHANGE_LOG_ATTRIBUTE_NAME = "XAVersionManager.ChangeLog";
+
+    /**
+     * Attribute name for items.
+     */
+    private static final String ITEMS_ATTRIBUTE_NAME = "VersionItems";
+
+    /**
+     * Repository version manager.
+     */
+    private final VersionManagerImpl vMgr;
+
+    /**
+     * Node type registry.
+     */
+    private NodeTypeRegistry ntReg;
+
+    /**
+     * Items that have been modified and are part of the XA environment.
+     */
+    private Map xaItems;
+
+    /**
+     * Creates a new instance of this class.
+     */
+    public XAVersionManager(VersionManagerImpl vMgr, NodeTypeRegistry ntReg,
+                            EventStateCollectionFactory factory)
+            throws RepositoryException {
+
+        this.vMgr = vMgr;
+        this.ntReg = ntReg;
+        this.stateMgr = new XAItemStateManager(vMgr.getSharedStateMgr(),
+                factory, CHANGE_LOG_ATTRIBUTE_NAME);
+
+        NodeState state;
+        try {
+            state = (NodeState) stateMgr.getItemState(vMgr.getHistoryRootId());
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Unable to retrieve history root", e);
+        }
+        this.historyRoot = new NodeStateEx(stateMgr, ntReg, state, QName.JCR_VERSIONSTORAGE);
+    }
+
+    //-------------------------------------------------------< VersionManager >
+
+    /**
+     * {@inheritDoc}
+     */
+    public VirtualItemStateProvider getVirtualItemStateProvider() {
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public VersionHistory createVersionHistory(Session session, NodeState node)
+            throws RepositoryException {
+
+        if (isInXA()) {
+            InternalVersionHistory history = createVersionHistory(node);
+            xaItems.put(history.getId(), history);
+            return (VersionHistory) session.getNodeByUUID(history.getId());
+        }
+        return vMgr.createVersionHistory(session, node);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Version checkin(NodeImpl node) throws RepositoryException {
+        if (isInXA()) {
+            String histUUID = node.getProperty(QName.JCR_VERSIONHISTORY).getString();
+            InternalVersion version = checkin(
+                    (InternalVersionHistoryImpl) getVersionHistory(histUUID), node);
+            return (Version) node.getSession().getNodeByUUID(version.getId());
+        }
+        return vMgr.checkin(node);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeVersion(VersionHistory history, QName versionName)
+            throws RepositoryException {
+
+        if (isInXA()) {
+            InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
+                    ((AbstractVersionHistory) history).getInternalVersionHistory();
+            removeVersion(vh, versionName);
+            return;
+        }
+        vMgr.removeVersion(history, versionName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Version setVersionLabel(VersionHistory history, QName version,
+                                   QName label, boolean move)
+            throws RepositoryException {
+
+        if (isInXA()) {
+            InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
+                    ((AbstractVersionHistory) history).getInternalVersionHistory();
+            InternalVersion v = setVersionLabel(vh, version, label, move);
+            if (v == null) {
+                return null;
+            } else {
+                return (Version) history.getSession().getNodeByUUID(v.getId());
+            }
+        }
+        return vMgr.setVersionLabel(history, version, label, move);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close() throws Exception {
+    }
+
+    //---------------------------------------------< VirtualItemStateProvider >
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isVirtualRoot(ItemId id) {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeId getVirtualRootId() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public VirtualPropertyState createPropertyState(VirtualNodeState parent,
+                                                    QName name, int type,
+                                                    boolean multiValued)
+            throws RepositoryException {
+
+        throw new IllegalStateException("Read-only");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public VirtualNodeState createNodeState(VirtualNodeState parent, QName name,
+                                            String uuid, QName nodeTypeName)
+            throws RepositoryException {
+
+        throw new IllegalStateException("Read-only");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean setNodeReferences(NodeReferences refs) {
+        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
+        if (changeLog != null) {
+            changeLog.modified(refs);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Return item states for changes only. Global version manager will return
+     * other items.
+     */
+    public ItemState getItemState(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
+        if (changeLog != null) {
+            return changeLog.get(id);
+        }
+        throw new NoSuchItemStateException("State not in change log: " + id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasItemState(ItemId id) {
+        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
+        if (changeLog != null) {
+            return changeLog.has(id);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeReferences getNodeReferences(NodeReferencesId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
+        if (changeLog != null) {
+            return changeLog.get(id);
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasNodeReferences(NodeReferencesId id) {
+        ChangeLog changeLog = ((XAItemStateManager) stateMgr).getChangeLog();
+        if (changeLog != null) {
+            return changeLog.get(id) != null;
+        }
+        return false;
+    }
+
+    //-----------------------------------------------< AbstractVersionManager >
+
+    /**
+     * {@inheritDoc}
+     */
+     protected InternalVersionItem getItem(String uuid) throws RepositoryException {
+        InternalVersionItem item = null;
+        if (xaItems != null) {
+            item = (InternalVersionItem) xaItems.get(uuid);
+        }
+        if (item == null) {
+            item = vMgr.getItem(uuid);
+        }
+        return item;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected boolean hasItem(String uuid) {
+        if (xaItems != null && xaItems.containsKey(uuid)) {
+            return true;
+        }
+        return vMgr.hasItem(uuid);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List getItemReferences(InternalVersionItem item) {
+        return vMgr.getItemReferences(item);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Before modifying version history given, make a local copy of it.
+     */
+    protected InternalVersion checkin(InternalVersionHistoryImpl history,
+                                      NodeImpl node)
+            throws RepositoryException {
+
+        if (history.getVersionManager() != this) {
+            history = makeLocalCopy(history);
+            xaItems.put(history.getId(), history);
+        }
+        return super.checkin(history, node);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Before modifying version history given, make a local copy of it.
+     */
+    protected void removeVersion(InternalVersionHistoryImpl history, QName name)
+            throws VersionException, RepositoryException {
+
+        if (history.getVersionManager() != this) {
+            history = makeLocalCopy(history);
+            xaItems.put(history.getId(), history);
+        }
+        super.removeVersion(history, name);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Before modifying version history given, make a local copy of it.
+     */
+    protected InternalVersion setVersionLabel(InternalVersionHistoryImpl history,
+                                              QName version, QName label,
+                                              boolean move)
+            throws RepositoryException {
+
+        if (history.getVersionManager() != this) {
+            history = makeLocalCopy(history);
+            xaItems.put(history.getId(), history);
+        }
+        return super.setVersionLabel(history, version, label, move);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Put the version object into our cache.
+     */
+    protected void versionCreated(InternalVersion version) {
+        xaItems.put(version.getId(), version);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Remove the version object from our cache.
+     */
+    protected void versionDestroyed(InternalVersion version) {
+        xaItems.remove(version.getId());
+    }
+
+    //-------------------------------------------------------------------< XA >
+
+    /**
+     * {@inheritDoc}
+     */
+    public void associate(TransactionContext tx) {
+        ((XAItemStateManager) stateMgr).associate(tx);
+
+        Map xaItems = null;
+        if (tx != null) {
+            xaItems = (Map) tx.getAttribute(ITEMS_ATTRIBUTE_NAME);
+            if (xaItems == null) {
+                xaItems = new HashMap();
+                tx.setAttribute(ITEMS_ATTRIBUTE_NAME, xaItems);
+            }
+        }
+        this.xaItems = xaItems;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Delegate the call to our XA item state manager.
+     */
+    public void beforeOperation(TransactionContext tx) {
+        ((XAItemStateManager) stateMgr).beforeOperation(tx);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Delegate the call to our XA item state manager.
+     */
+    public void prepare(TransactionContext tx) throws TransactionException {
+        ((XAItemStateManager) stateMgr).prepare(tx);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Delegate the call to our XA item state manager. If successful, inform
+     * global repository manager to update its caches.
+     */
+    public void commit(TransactionContext tx) throws TransactionException {
+        ((XAItemStateManager) stateMgr).commit(tx);
+
+        Map xaItems = (Map) tx.getAttribute(ITEMS_ATTRIBUTE_NAME);
+        vMgr.itemsUpdated(xaItems.values());
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Delegate the call to our XA item state manager.
+     */
+    public void rollback(TransactionContext tx) {
+        ((XAItemStateManager) stateMgr).rollback(tx);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Delegate the call to our XA item state manager.
+     */
+    public void afterOperation(TransactionContext tx) {
+        ((XAItemStateManager) stateMgr).afterOperation(tx);
+    }
+
+    //-------------------------------------------------------< implementation >
+
+    /**
+     * Return a flag indicating whether this version manager is currently
+     * associated with an XA transaction.
+     */
+    private boolean isInXA() {
+        return xaItems != null;
+    }
+
+    /**
+     * Make a local copy of an internal version item. This will recreate the
+     * (global) version item with state information from our own state
+     * manager.
+     */
+    private InternalVersionHistoryImpl makeLocalCopy(InternalVersionHistoryImpl history)
+            throws RepositoryException {
+
+        NodeState state;
+        try {
+            state = (NodeState) stateMgr.getItemState(new NodeId(history.getId()));
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Unable to make local copy", e);
+        }
+        NodeStateEx stateEx = new NodeStateEx(stateMgr, ntReg, state, null);
+        return new InternalVersionHistoryImpl(this, stateEx);
+    }
+
+    /**
+     * Return a flag indicating whether an internal version item belongs to
+     * a different XA environment.
+     */
+    boolean differentXAEnv(InternalVersionItemImpl item) {
+        if (item.getVersionManager() == this) {
+            if (xaItems == null || !xaItems.containsKey(item.getId())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

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

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

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

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java Wed Jan 11 06:22:57 2006
@@ -32,6 +32,7 @@
 import org.apache.jackrabbit.core.state.NodeReferencesId;
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.ItemStateReferenceMap;
+import org.apache.jackrabbit.core.state.ItemStateListener;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.uuid.UUID;
 import org.apache.log4j.Logger;
@@ -44,7 +45,7 @@
  * This Class implements a virtual item state provider, in order to expose the
  * versions to the version storage.
  */
-public abstract class AbstractVISProvider implements VirtualItemStateProvider {
+public abstract class AbstractVISProvider implements VirtualItemStateProvider, ItemStateListener {
     /**
      * the default logger
      */

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java Wed Jan 11 06:22:57 2006
@@ -28,7 +28,7 @@
 /**
  * This Interface defines a virtual item state provider.
  */
-public interface VirtualItemStateProvider extends ItemStateManager, ItemStateListener {
+public interface VirtualItemStateProvider extends ItemStateManager {
 
     /**
      * Checks if the id refers to the root of a virtual tree.

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualNodeState.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualNodeState.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualNodeState.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualNodeState.java Wed Jan 11 06:22:57 2006
@@ -56,7 +56,7 @@
      * @param mixins
      * @throws RepositoryException
      */
-    public VirtualNodeState(VirtualItemStateProvider stateMgr,
+    public VirtualNodeState(AbstractVISProvider stateMgr,
                             String parentUUID,
                             String uuid,
                             QName nodeTypeName,

Modified: incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java?rev=368026&r1=368025&r2=368026&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java Wed Jan 11 06:22:57 2006
@@ -22,6 +22,9 @@
 import javax.jcr.Node;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Session;
+import javax.jcr.RepositoryException;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.Version;
 import javax.jcr.lock.Lock;
 import javax.transaction.UserTransaction;
 import javax.transaction.RollbackException;
@@ -33,6 +36,30 @@
 public class XATest extends AbstractJCRTest {
 
     /**
+     * Other superuser.
+     */
+    private Session otherSuperuser;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        otherSuperuser = helper.getSuperuserSession();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void tearDown() throws Exception {
+        if (otherSuperuser != null) {
+            otherSuperuser.logout();
+        }
+        super.tearDown();
+    }
+
+    /**
      * @see junit.framework#runTest
      *
      * Make sure that tested repository supports transactions
@@ -44,28 +71,6 @@
         }
     }
 
-    public void testCheckin() throws Exception {
-        // get user transaction object
-        UserTransaction utx = new UserTransactionImpl(superuser);
-
-        // start transaction
-        utx.begin();
-
-        // add node and save
-        Node n = testRootNode.addNode(nodeName1, testNodeType);
-        n.addMixin(mixVersionable);
-        testRootNode.save();
-
-        n.checkin();
-
-        assertFalse("Node must be checked-in", n.isCheckedOut());
-
-        // commit
-        utx.commit();
-
-        assertFalse("Node must be checked-in", n.isCheckedOut());
-    }
-
     /**
      * Add a node inside a transaction and commit changes. Make sure
      * node exists for other sessions only after commit.
@@ -557,6 +562,8 @@
         otherSuperuser.logout();
     }
 
+    //--------------------------------------------------------------< locking >
+
     /**
      * Test locking a node in one session. Verify that node is not locked
      * in other session until commit.
@@ -692,7 +699,7 @@
      * Test locking a new node inside a transaction.
      * @throws Exception
      */
-    public void testLockNewNode() throws Exception {
+    public void xxxtestLockNewNode() throws Exception {
         // get user transaction object, start
         UserTransaction utx = new UserTransactionImpl(superuser);
         utx.begin();
@@ -802,5 +809,162 @@
 
         // verify lock is live again
         assertTrue("Lock live", lock.isLive());
+    }
+
+    //-----------------------------------------------------------< versioning >
+
+    /**
+     * Checkin inside tx should not be visible to other users.
+     */
+    public void testCheckin() throws Exception {
+        // get user transaction object
+        UserTransaction utx = new UserTransactionImpl(superuser);
+
+        // add node and save
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixVersionable);
+        testRootNode.save();
+
+        // reference node in other session
+        Node nOther = otherSuperuser.getNodeByUUID(n.getUUID());
+
+        // start transaction
+        utx.begin();
+
+        // checkin node
+        n.checkin();
+
+        // assert: base versions must differ
+        assertNotSame("Base versions must differ",
+                n.getBaseVersion().getName(), nOther.getBaseVersion().getName());
+
+        // assert: version must not be visible to other session
+        try {
+            nOther.getVersionHistory().getVersion(n.getBaseVersion().getName());
+            fail("Version must not be visible to other session.");
+        } catch (VersionException e) {
+            // expected.
+        }
+
+        // commit
+        utx.commit();
+
+        // assert: base versions must be equal
+        assertSame("Base versions must be equal",
+                n.getBaseVersion().getName(), nOther.getBaseVersion().getName());
+    }
+
+    /**
+     * Checkin from two sessions simultaneously should throw when committing.
+     * @throws Exception
+     */
+    public void testConflictingCheckin() throws Exception {
+        // get user transaction object
+        UserTransaction utx = new UserTransactionImpl(superuser);
+
+        // add node and save
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixVersionable);
+        testRootNode.save();
+
+        // reference node in other session
+        Node nOther = otherSuperuser.getNodeByUUID(n.getUUID());
+
+        // start transaction
+        utx.begin();
+
+        // checkin node inside tx
+        n.checkin();
+
+        // checkin node outside tx
+        nOther.checkin();
+
+        // commit
+        try {
+            utx.commit();
+            fail("Commit failing with modified version history.");
+        } catch (RollbackException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Test removed version gets invalid for other users on commit.
+     */
+    public void testRemoveVersion() throws Exception {
+        // get user transaction object
+        UserTransaction utx = new UserTransactionImpl(superuser);
+
+        // add node and save
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixVersionable);
+        testRootNode.save();
+
+        // reference node in other session
+        Node nOther = otherSuperuser.getNodeByUUID(n.getUUID());
+
+        // create two versions, reference first version in other session
+        n.checkin();
+        Version vOther = nOther.getBaseVersion();
+        n.checkout();
+        n.checkin();
+
+        // start transaction
+        utx.begin();
+
+        // remove version and commit
+        n.getVersionHistory().removeVersion(vOther.getName());
+
+        // commit
+        utx.commit();
+
+        // assert: version has become invalid
+        try {
+            vOther.getPredecessors();
+            fail("Removed version still operational.");
+        } catch (RepositoryException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Test new version label becomes available to other sessions on commit.
+     */
+    public void testSetVersionLabel() throws Exception {
+        final String versionLabel = "myVersion";
+
+        // get user transaction object
+        UserTransaction utx = new UserTransactionImpl(superuser);
+
+        // add node and save
+        Node n = testRootNode.addNode(nodeName1, testNodeType);
+        n.addMixin(mixVersionable);
+        testRootNode.save();
+
+        // reference node in other session
+        Node nOther = otherSuperuser.getNodeByUUID(n.getUUID());
+
+        // create another version
+        Version v = n.checkin();
+
+        // start transaction
+        utx.begin();
+
+        // add new version label
+        n.getVersionHistory().addVersionLabel(v.getName(), versionLabel, false);
+
+        // assert: version label unknown in other session
+        try {
+            nOther.getVersionHistory().getVersionByLabel(versionLabel);
+            fail("Version label visible outside tx.");
+        } catch (VersionException e) {
+            // expected
+        }
+
+        // commit
+        utx.commit();
+
+        // assert: version label known in other session
+        nOther.getVersionHistory().getVersionByLabel(versionLabel);
     }
 }