You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2006/10/06 08:55:28 UTC

svn commit: r453514 - in /jackrabbit/trunk/contrib/spi: jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ spi2dav/ spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/

Author: angela
Date: Thu Oct  5 23:55:27 2006
New Revision: 453514

URL: http://svn.apache.org/viewvc?view=rev&rev=453514
Log:
work in progress

- event processing upon saving transient modifications
- extend ItemState.refresh: add Event and ev. ChangeLog as param
- EventImpl: parentId missing
- remove ItemStateListener
- all state changes are covered by ItemStateLifeCycleListener.statusChanged(ItemState, int)

Removed:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateListener.java
Modified:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ItemImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/SessionImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateLifeCycleListener.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java
    jackrabbit/trunk/contrib/spi/spi2dav/project.xml
    jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventImpl.java
    jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ItemImpl.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ItemImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ItemImpl.java Thu Oct  5 23:55:27 2006
@@ -234,9 +234,6 @@
         checkStatus();
 
         if (keepChanges) {
-            /** TODO should reset Item#status field to STATUS_NORMAL
-             * of all descendent non-transient instances; maybe also
-             * have to reset stale ItemState instances */
             return;
         }
 
@@ -276,68 +273,48 @@
 
     //-----------------------------------------< ItemStateLifeCycleListener >---
     /**
-     * {@inheritDoc}
+     *
+     * @param state
+     * @param previousStatus
      */
-    public void stateCreated(ItemState created) {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void stateDestroyed(ItemState destroyed) {
-        // underlying state has been permanently destroyed
-
-        // dispose state
-        if (state == destroyed) {
-            state.removeListener(this);
-            state = null;
-        }
-        /**
-         * notify the listeners that this instance has been
-         * permanently invalidated
-         */
-        notifyDestroyed();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void stateModified(ItemState modified) {
-    }
-
     public void statusChanged(ItemState state, int previousStatus) {
-        // TODO: remove this ItemImpl as listener from ItemState when it is destroyed?
+        // TODO: ev. assert that state is this.state?
+
         switch (state.getStatus()) {
+            /**
+             * Nothing to do for
+             * - ItemState.STATUS_EXISTING : modifications reverted or saved
+             * - ItemState.STATUS_EXISTING_MODIFIED : transient modification
+             * - ItemState.STATUS_STALE_MODIFIED : external modifications while transient changes pending
+             * - ItemState.MODIFIED : externaly modified -> marker for sessionISM states only
+             */
             case ItemState.STATUS_EXISTING:
-                // this item was modified and is now reverted or has been saved
-                // -> nothing to do
-                break;
             case ItemState.STATUS_EXISTING_MODIFIED:
-                // item was modified and is not existing-modified
-                // -> nothing to do
+            case ItemState.STATUS_STALE_MODIFIED:
+            case ItemState.STATUS_MODIFIED:
                 break;
+            /**
+             * Notify listeners that this item is transiently or permanently
+             * destroyed.
+             * - STATUS_EXISTING_REMOVED : transient removal
+             * - STATUS_REMOVED : permanent removal. item will never get back to life
+             * - STATUS_STALE_DESTROYED : permanent removal. item will never get back to life
+             */
             case ItemState.STATUS_EXISTING_REMOVED:
-                // item is transiently removed
-                // notify listeners of this item that this item has been destroyed
                 notifyDestroyed();
                 break;
-            case ItemState.STATUS_NEW:
-                // should never happen. an item cannot change its state to new
-                log.warn("invalid state change to STATUS_NEW");
-                break;
             case ItemState.STATUS_REMOVED:
-                // item has been removed permanently
-                notifyDestroyed();
-                break;
             case ItemState.STATUS_STALE_DESTROYED:
-                // item has been removed permanently while there were transient
-                // changes pending
+                state.removeListener(this);
+                this.state = null;
                 notifyDestroyed();
                 break;
-            case ItemState.STATUS_STALE_MODIFIED:
-                // item has been modified externaly while there were transient
-                // changes pending
-                // -> nothing to do
+            /**
+             * Invalid status. A state can never change its state to 'New'.
+             */
+            case ItemState.STATUS_NEW:
+                // should never happen.
+                log.error("invalid state change to STATUS_NEW");
                 break;
         }
     }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java Thu Oct  5 23:55:27 2006
@@ -864,8 +864,8 @@
         // make sure the specified workspace is visible for the current session.
         session.checkAccessibleWorkspace(srcWorkspaceName);
 
-        Operation op = Update.create(getNodeState(), srcWorkspaceName);
-        session.getSessionItemStateManager().execute(op);
+        Operation op = Update.create((NodeState) getNodeState().getOverlayedState(), srcWorkspaceName);
+        ((WorkspaceImpl)session.getWorkspace()).getUpdatableItemStateManager().execute(op);
     }
 
     /**
@@ -883,7 +883,6 @@
         // make sure the workspace exists and is accessible for this session.
         session.checkAccessibleWorkspace(srcWorkspace);
 
-        // TODO: improve... (and review return value of VM.merge)
         Collection failedIds = session.getVersionManager().merge(getNodeState(), srcWorkspace, bestEffort);
         if (failedIds.isEmpty()) {
             return IteratorHelper.EMPTY;

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/SessionImpl.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/SessionImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/SessionImpl.java Thu Oct  5 23:55:27 2006
@@ -281,7 +281,7 @@
         } catch (NoSuchItemStateException e) {
             throw new ItemNotFoundException(id.toString());
         } catch (ItemStateException e) {
-            String msg = "failed to retrieve item state of item " + id;
+            String msg = "Failed to retrieve item state of item " + id;
             log.error(msg, e);
             throw new RepositoryException(msg, e);
         }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/CachingItemStateManager.java Thu Oct  5 23:55:27 2006
@@ -247,13 +247,13 @@
         return state;
     }
 
-    //------------------------< ItemStateListener >-----------------------------
+    //-----------------------------------------< ItemStateLifeCycleListener >---
 
     private class ISLifeCycleListener implements ItemStateLifeCycleListener {
 
         public void statusChanged(ItemState state, int previousStatus) {
             if (state.getStatus() == ItemState.STATUS_REMOVED ||
-                    state.getStatus() == ItemState.STATUS_STALE_DESTROYED) {
+                state.getStatus() == ItemState.STATUS_STALE_DESTROYED) {
                 recentlyUsed.remove(state);
                 if (state.isNode()) {
                     NodeState nodeState = (NodeState) state;
@@ -262,15 +262,6 @@
                     }
                 }
             }
-        }
-
-        public void stateCreated(ItemState created) {
-        }
-
-        public void stateModified(ItemState modified) {
-        }
-
-        public void stateDestroyed(ItemState destroyed) {
         }
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChangeLog.java Thu Oct  5 23:55:27 2006
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.jcr2spi.state;
 
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
-import org.apache.jackrabbit.spi.EventIterator;
 
 import java.util.Iterator;
 import java.util.Set;
@@ -238,60 +237,24 @@
 
     //-----------------------------< Inform ChangeLog about Success/Failure >---
     /**
-     * ChangeLog has successfully been commited. The given <code>EventIterator</code>
-     * contains information about all modifications. This ChangeLog needs
-     * to notify all transient states involved.
-     *
-     * @param events
-     */
-    public void persist(EventIterator events) {
-        // TODO: events may reveal additional autocreated items and modifications
-        // applied while commiting the changelog (e.g. uuid or nodetype of new nodes).
-        push();
-        persisted();
-    }
-
-    /**
-     * Push all states contained in the various maps of
-     * items we have.
-     */
-    private void push() {
-        Iterator iter = modifiedStates();
-        while (iter.hasNext()) {
-            ((ItemState) iter.next()).push();
-        }
-        iter = deletedStates();
-        while (iter.hasNext()) {
-            ((ItemState) iter.next()).push();
-        }
-        iter = addedStates();
-        while (iter.hasNext()) {
-            ((ItemState) iter.next()).push();
-        }
-    }
-
-    /**
      * After the states have actually been persisted, update their
      * internal states and notify listeners.
      */
-    private void persisted() {
+    public void persisted() {
         Iterator iter = modifiedStates();
         while (iter.hasNext()) {
             ItemState state = (ItemState) iter.next();
             state.setStatus(ItemState.STATUS_EXISTING);
-            state.notifyStateUpdated();  // TODO: is this needed anymore?
         }
         iter = deletedStates();
         while (iter.hasNext()) {
             ItemState state = (ItemState) iter.next();
             state.setStatus(ItemState.STATUS_REMOVED);
-            state.notifyStateDestroyed();  // TODO: is this needed anymore?
         }
         iter = addedStates();
         while (iter.hasNext()) {
             ItemState state = (ItemState) iter.next();
             state.setStatus(ItemState.STATUS_EXISTING);
-            state.notifyStateCreated();  // TODO: is this needed anymore?
         }
     }
 

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java Thu Oct  5 23:55:27 2006
@@ -19,6 +19,7 @@
 import org.apache.jackrabbit.util.WeakIdentityCollection;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.name.Path;
 import org.apache.jackrabbit.name.MalformedPathException;
 import org.apache.jackrabbit.name.QName;
@@ -33,7 +34,7 @@
 /**
  * <code>ItemState</code> represents the state of an <code>Item</code>.
  */
-public abstract class ItemState implements ItemStateListener {
+public abstract class ItemState implements ItemStateLifeCycleListener {
 
     /**
      * Logger instance
@@ -67,9 +68,18 @@
     public static final int STATUS_STALE_DESTROYED = 6;
 
     /**
+     * a state is permanently modified either by saving transient changes or
+     * by wsp operations or be external modification
+     * TODO: improve. status only temporarily used to indicate to a SessionISM-state to pull changes
+     */
+    public static final int STATUS_MODIFIED = 7;
+
+    /**
      * a new state was deleted and is now 'removed'
+     * or an existing item has been removed by a workspace operation or
+     * by an external modification.
      */
-    public static final int STATUS_REMOVED = 7;
+    public static final int STATUS_REMOVED = 8;
 
     /**
      * the internal status of this item state
@@ -146,29 +156,9 @@
     }
 
     /**
-     * Copy state information from another state into this state
-     * 
-     * @param state source state information
+     * Copy state information from overlayed state to this state
      */
-    abstract void copyFrom(ItemState state);
-
-    /**
-     * Pull state information from overlayed state.
-     */
-    void pull() {
-        if (overlayedState != null) {
-            copyFrom(overlayedState);
-        }
-    }
-
-    /**
-     * Push state information into overlayed state.
-     */
-    void push() {
-        if (overlayedState != null) {
-            overlayedState.copyFrom(this);
-        }
-    }
+    protected abstract void pull();
 
     /**
      * Connect this state to some underlying overlayed state.
@@ -183,95 +173,17 @@
         this.overlayedState.addListener(this);
     }
 
-    /**
-     * Reconnect this state to the overlayed state that it has been
-     * disconnected from earlier.
-     */
-    protected void reconnect() {
-        if (this.overlayedState == null) {
-            throw new IllegalStateException("Item state cannot be reconnected because there's no underlying state to reconnect to: " + this);
-        }
-        this.overlayedState.addListener(this);
-    }
-
-    /**
-     * Disconnect this state from the underlying overlayed state.
-     */
-    protected void disconnect() {
-        if (overlayedState != null) {
-            // de-register listener on overlayed state...
-            overlayedState.removeListener(this);
-            overlayedState = null;
-        }
-    }
-
-    /**
-     * Refreshes this item state
-     */
-    protected void refresh() {
-        // TODO: how is this done? where is the new state retrieved from???
-        // TODO: pass in as argument?
-    }
 
-    /**
-     * Notify the listeners that the persistent state this object is
-     * representing has been created.
-     */
-    protected void notifyStateCreated() {
-        // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la;
-        synchronized (listeners) {
-            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
-        }
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].stateCreated(this);
-            }
-        }
-    }
-
-    /**
-     * Notify the listeners that the persistent state this object is
-     * representing has been updated.
-     */
-    protected void notifyStateUpdated() {
-        // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la;
-        synchronized (listeners) {
-            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
-        }
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].stateModified(this);
-            }
-        }
-    }
-
-    /**
-     * Notify the listeners that the persistent state this object is
-     * representing has been destroyed.
-     */
-    protected void notifyStateDestroyed() {
-        // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la;
-        synchronized (listeners) {
-            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
-        }
-        for (int i = 0; i < la.length; i++) {
-            if (la[i] != null) {
-                la[i].stateDestroyed(this);
-            }
-        }
-    }
+    protected abstract void refresh(Event event, ChangeLog changeLog);
 
     /**
      * Notify the life cycle listeners that this state has changed its status.
      */
     private void notifyStatusChanged(int oldStatus) {
         // copy listeners to array to avoid ConcurrentModificationException
-        ItemStateListener[] la;
+        ItemStateLifeCycleListener[] la;
         synchronized (listeners) {
-            la = (ItemStateListener[]) listeners.toArray(new ItemStateListener[listeners.size()]);
+            la = (ItemStateLifeCycleListener[]) listeners.toArray(new ItemStateLifeCycleListener[listeners.size()]);
         }
         for (int i = 0; i < la.length; i++) {
             if (la[i] instanceof ItemStateLifeCycleListener) {
@@ -284,6 +196,11 @@
      * Marks this item state as modified.
      */
     protected void markModified() {
+        // only transient states can be marked-modified
+        if (getStatus() != STATUS_NEW && overlayedState == null) {
+            throw new IllegalStateException("persisted cannot be called on workspace state");
+        }
+
         switch (status) {
             case STATUS_EXISTING:
                 setStatus(STATUS_EXISTING_MODIFIED);
@@ -296,10 +213,10 @@
                 break;
             case STATUS_STALE_DESTROYED:
             case STATUS_STALE_MODIFIED:
-                // should actually get here because item should check before
-                // it modifies an item state. do nothing because item state
-                // is stale anyway.
-                break;
+                // should actually not get here because item should check before
+                // it modifies an item state.
+                throw new IllegalStateException("Cannot mark stale state modified.");
+
             case STATUS_EXISTING_REMOVED:
             default:
                 String msg = "Cannot mark item state with status " + status + " modified.";
@@ -307,7 +224,6 @@
         }
     }
 
-
     //--------------------< public READ methods and package private Setters >---
     /**
      * Determines if this item state represents a node.
@@ -458,6 +374,7 @@
             case STATUS_EXISTING_MODIFIED:
             case STATUS_STALE_MODIFIED:
             case STATUS_STALE_DESTROYED:
+            case STATUS_MODIFIED:
             case STATUS_REMOVED:
                 status = newStatus;
                 break;
@@ -525,11 +442,11 @@
     public abstract void collectTransientStates(Set transientStates);
 
     /**
-     * Add an <code>ItemStateListener</code>
+     * Add an <code>ItemStateLifeCycleListener</code>
      *
      * @param listener the new listener to be informed on modifications
      */
-    public void addListener(ItemStateListener listener) {
+    public void addListener(ItemStateLifeCycleListener listener) {
         synchronized (listeners) {
             assert (!listeners.contains(listener));
             listeners.add(listener);
@@ -537,52 +454,65 @@
     }
 
     /**
-     * Remove an <code>ItemStateListener</code>
+     * Remove an <code>ItemStateLifeCycleListener</code>
      *
      * @param listener an existing listener
      */
-    public void removeListener(ItemStateListener listener) {
+    public void removeListener(ItemStateLifeCycleListener listener) {
         synchronized (listeners) {
             listeners.remove(listener);
         }
     }
 
-    //--------------------------------------------------< ItemStateListener >---
+    //-----------------------------------------< ItemStateLifeCycleListener >---
     /**
-     * {@inheritDoc}
-     */
-    public void stateCreated(ItemState created) {
-        // underlying state has been permanently created
-        pull();
-        setStatus(STATUS_EXISTING);
-    }
-
-    /**
-     * {@inheritDoc}
+     *
+     * @param state
+     * @param previousStatus
      */
-    public void stateDestroyed(ItemState destroyed) {
-        // underlying state has been permanently destroyed
-        if (isTransient()) {
-            setStatus(STATUS_STALE_DESTROYED);
-        } else {
-            setStatus(STATUS_REMOVED);
-            notifyStateDestroyed();
+    public void statusChanged(ItemState state, int previousStatus) {
+        // workspace-states never are listening to another state
+        if (getStatus() != STATUS_NEW && overlayedState == null) {
+            throw new IllegalStateException("statusChanged cannot be called on workspace state");
         }
-    }
 
-    /**
-     * {@inheritDoc}
-     */
-    public void stateModified(ItemState modified) {
-        // underlying state has been modified
-        if (isTransient()) {
-            setStatus(STATUS_STALE_MODIFIED);
-        } else {
-            synchronized (this) {
-                // this instance represents existing state, update it
-                pull();
-                notifyStateUpdated();
-            }
+        switch (state.getStatus()) {
+            case STATUS_EXISTING:
+                // nothing to do
+                break;
+            case STATUS_MODIFIED:
+                if (previousStatus == STATUS_EXISTING) {
+                    // change back
+                    state.status = STATUS_EXISTING;
+                    // underlying state has been modified
+                    if (isTransient()) {
+                        setStatus(STATUS_STALE_MODIFIED);
+                    } else {
+                        synchronized (this) {
+                            // this instance represents existing state, update it
+                            pull();
+                            setStatus(STATUS_EXISTING);
+                        }
+                    }
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException();
+                }
+                break;
+            case STATUS_REMOVED:
+                if (isTransient()) {
+                    setStatus(STATUS_STALE_DESTROYED);
+                } else {
+                    setStatus(STATUS_REMOVED);
+                }
+                break;
+            case STATUS_STALE_MODIFIED:
+            case STATUS_STALE_DESTROYED:
+            case STATUS_EXISTING_REMOVED:
+            case STATUS_EXISTING_MODIFIED:
+            case STATUS_NEW:
+                log.error("Workspace state cannot have its state changed to " + state.getStatus());
+                break;
         }
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateLifeCycleListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateLifeCycleListener.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateLifeCycleListener.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemStateLifeCycleListener.java Thu Oct  5 23:55:27 2006
@@ -20,7 +20,7 @@
  * <code>ItemStateLifeCycleListener</code> allows an implementing class to get
  * notifications about the life cycle of an item state.
  */
-public interface ItemStateLifeCycleListener extends ItemStateListener {
+public interface ItemStateLifeCycleListener {
 
     /**
      * Called after an <code>ItemState</code> has changed its status. The new

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java Thu Oct  5 23:55:27 2006
@@ -25,6 +25,8 @@
 import org.apache.jackrabbit.name.MalformedPathException;
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.Event;
+import org.apache.jackrabbit.spi.PropertyId;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
@@ -191,16 +193,96 @@
     /**
      * {@inheritDoc}
      */
-    protected synchronized void copyFrom(ItemState state) {
-        synchronized (state) {
-            NodeState nodeState = (NodeState) state;
-            name = nodeState.name;
-            uuid = nodeState.uuid;
-            //parent = nodeState.parent; // TODO: parent from wrong ism layer
-            nodeTypeName = nodeState.nodeTypeName;
-            definition = nodeState.definition;
+    protected synchronized void pull() {
+        if (overlayedState != null) {
+            synchronized (overlayedState) {
+                NodeState nodeState = (NodeState) overlayedState;
+                name = nodeState.name;
+                uuid = nodeState.uuid;
+                nodeTypeName = nodeState.nodeTypeName;
+                definition = nodeState.definition;
+
+                init(nodeState.getMixinTypeNames(), nodeState.getChildNodeEntries(), nodeState.getPropertyNames(), nodeState.getNodeReferences());
+            }
+        }
+    }
+
+    protected synchronized void refresh(Event event, ChangeLog changeLog) {
+        NodeId id = getNodeId();
+        switch (event.getType()) {
+            case Event.NODE_ADDED:
+            case Event.PROPERTY_ADDED:
+                if (id.equals(event.getParentId())) {
+                    ItemId evId = event.getItemId();
+                    ItemState newState = null;
+
+                    if (evId.denotesNode()) {
+                        QName name = event.getQPath().getNameElement().getName();
+                        String uuid = (((NodeId)evId).getRelativePath() != null) ? null : ((NodeId)evId).getUUID();
+                        ChildNodeEntry cne = childNodeEntries.add(name, uuid);
+                        try {
+                            newState = cne.getNodeState();
+                        } catch (ItemStateException e) {
+                            log.error("Internal error", e);
+                        }
+                    } else {
+                        PropertyId pId = (PropertyId) event.getItemId();
+                        PropertyReference re = new PropertyReference(this, pId.getQName(), isf, idFactory);
+                        properties.put(pId.getQName(), re);
+                        try {
+                            newState = re.getPropertyState();
+                        } catch (ItemStateException e) {
+                            log.error("Internal error", e);
+                        }
+                    }
+
+                    // connect the transient state to this state and make
+                    // sure its data are updated
+                    if (newState != null && changeLog != null) {
+                        for (Iterator it = changeLog.addedStates(); it.hasNext();) {
+                            ItemState added = (ItemState) it.next();
+                            if (added.getId().equals(evId)) {
+                                added.connect(newState);
+                                added.pull();
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
+
+            case Event.NODE_REMOVED:
+                if (id.equals(event.getParentId())) {
+                    QName qName = event.getQPath().getNameElement().getName();
+                    int index = event.getQPath().getNameElement().getNormalizedIndex();
+                    childNodeEntries.remove(qName, index);
+                    setStatus(STATUS_MODIFIED);
+                } else if (id.equals(event.getItemId())) {
+                    setStatus(STATUS_REMOVED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
+
+            case Event.PROPERTY_REMOVED:
+                if (id.equals(event.getParentId())) {
+                    PropertyId pId = (PropertyId) event.getItemId();
+                    properties.remove(pId.getQName());
+                    setStatus(STATUS_MODIFIED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
+                }
+                break;
 
-            init(nodeState.getMixinTypeNames(), nodeState.getChildNodeEntries(), nodeState.getPropertyNames(), nodeState.getNodeReferences());
+            case Event.PROPERTY_CHANGED:
+            default:
+                // ILLEGAL
+                throw new IllegalArgumentException("Illegal event type " + event.getType() + " for NodeState.");
         }
     }
 
@@ -680,7 +762,6 @@
      * <code>QNames</code> objects.
      *
      * @return set of <code>QNames</code> objects
-     * @see #addPropertyName
      */
     public synchronized Collection getPropertyNames() {
         Collection names;
@@ -700,7 +781,6 @@
      * Returns the complete collection of {@link ChildPropertyEntry}s.
      *
      * @return unmodifiable collection of <code>ChildPropertyEntry</code> objects
-     * @see #addPropertyName
      */
     public synchronized Collection getPropertyEntries() {
         Collection props;

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java Thu Oct  5 23:55:27 2006
@@ -26,6 +26,7 @@
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.ItemId;
 import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.value.QValue;
 import org.apache.jackrabbit.jcr2spi.nodetype.ValueConstraint;
 import org.slf4j.Logger;
@@ -178,20 +179,50 @@
                 break;
             default:
                 // should never occur. status is validated upon setStatus(int)
+                log.error("Internal error: Invalid state " + getStatus());
         }
     }
 
     /**
      * {@inheritDoc}
      */
-    protected synchronized void copyFrom(ItemState state) {
-        synchronized (state) {
-            PropertyState propState = (PropertyState) state;
-            name = propState.name;
-            //parent = propState.parent; // TODO: parent from wrong layer
-            type = propState.type;
-            def = propState.def;
-            values = propState.values;
+    protected synchronized void pull() {
+        if (overlayedState != null) {
+            synchronized (overlayedState) {
+                PropertyState propState = (PropertyState) overlayedState;
+                name = propState.name;
+                type = propState.type;
+                def = propState.def;
+                values = propState.values;
+            }
+        }
+    }
+
+    protected synchronized void refresh(Event event, ChangeLog changeLog) {
+        switch (event.getType()) {
+            case Event.PROPERTY_REMOVED:
+                if (event.getItemId().equals(getId())) {
+                    setStatus(STATUS_REMOVED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
+                }
+                break;
+
+            case Event.PROPERTY_CHANGED:
+                if (event.getItemId().equals(getId())) {
+                    setStatus(STATUS_MODIFIED);
+                } else {
+                    // ILLEGAL
+                    throw new IllegalArgumentException("EventId " + event.getItemId() + " does not match id of this property state.");
+                }
+                break;
+
+            case Event.PROPERTY_ADDED:
+            case Event.NODE_ADDED:
+            case Event.NODE_REMOVED:
+            default:
+                throw new IllegalArgumentException("Event type " + event.getType() + " cannot be applied to a PropertyState");
         }
     }
 

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/SessionItemStateManager.java Thu Oct  5 23:55:27 2006
@@ -265,6 +265,8 @@
 
         // remove operations just processed
         transientStateMgr.disposeOperations(changeLog.getOperations());
+        // now its save to clear the changeLog
+        changeLog.reset();
     }
 
     /**
@@ -303,7 +305,6 @@
         Iterator it = refTracker.getReferences();
         while (it.hasNext()) {
             PropertyState propState = (PropertyState) it.next();
-            // DIFF JR: remove check (already asserted on processReference)
             boolean modified = false;
             QValue[] values = propState.getValues();
             QValue[] newVals = new QValue[values.length];
@@ -349,8 +350,6 @@
     }
 
     /**
-     * DIFF JACKRABBIT: copied and adapted from ItemImpl.getTransientStates()
-     * <p/>
      * Builds a <code>ChangeLog</code> of transient (i.e. new, modified or
      * deleted) item states that are within the scope of <code>state</code>.
      *
@@ -670,8 +669,7 @@
     }
 
     public void visit(Update operation) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
-        // TODO: TOBEFIXED. not correct.
-        workspaceItemStateMgr.execute(operation);
+        throw new UnsupportedOperationException("Internal error: Update cannot be handled by session ItemStateManager.");
     }
 
     public void visit(Restore operation) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
@@ -790,9 +788,7 @@
     }
 
     private void removeItemState(ItemState itemState, int options) throws RepositoryException {
-        // DIFF JR: check for both, node- and propertyState
         validator.checkRemoveItem(itemState, options);
-
         // recursively remove the complete tree including the given node state.
         boolean success = false;
         try {

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/TransientItemStateManager.java Thu Oct  5 23:55:27 2006
@@ -243,32 +243,9 @@
     //-----------------------------------------< ItemStateLifeCycleListener >---
     /**
      * @inheritDoc
-     * @see ItemStateListener#stateCreated(ItemState)
-     */
-    public void stateCreated(ItemState created) {
-    }
-
-    /**
-     * @inheritDoc
-     * @see ItemStateListener#stateModified(ItemState)
-     */
-    public void stateModified(ItemState modified) {
-    }
-
-    /**
-     * @inheritDoc
-     * @see ItemStateListener#stateDestroyed(ItemState)
-     */
-    public void stateDestroyed(ItemState destroyed) {
-        changeLog.deletedStates.remove(destroyed);
-    }
-
-    /**
-     * @inheritDoc
      * @see ItemStateLifeCycleListener#statusChanged(ItemState, int)
      */
     public void statusChanged(ItemState state, int previousStatus) {
-        // TODO: cleanup operations as well.
         // TODO: depending on status of state adapt change log
         // e.g. a revert on states will reset the status from
         // 'existing modified' to 'existing'.
@@ -292,14 +269,6 @@
                     case ItemState.STATUS_NEW:
                         // was new and has been saved now
                         changeLog.addedStates.remove(state);
-                        // state needs to be connected to the overlayed-state now
-                        try {
-                            ItemState overlayedState = parent.getItemState(state.getId());
-                            state.connect(overlayedState);
-                        } catch (ItemStateException e) {
-                            // TODO, handle property
-                            log.error(e.getMessage());
-                        }
                         break;
                 }
                 break;
@@ -310,6 +279,7 @@
                 // check if modified earlier
                 if (previousStatus == ItemState.STATUS_EXISTING_MODIFIED) {
                     changeLog.modifiedStates.remove(state);
+                    // todo: remove operation(s) as well
                 }
                 changeLog.deleted(state);
                 break;
@@ -317,7 +287,7 @@
                 if (previousStatus == ItemState.STATUS_NEW) {
                     // was new and now removed again
                     changeLog.addedStates.remove(state);
-                    // TODO remove the 'add' operation as well
+                    // TODO: remove operation as well
                 } else if (previousStatus == ItemState.STATUS_EXISTING_REMOVED) {
                     // was removed and is now saved
                     changeLog.deletedStates.remove(state);
@@ -326,6 +296,7 @@
             case ItemState.STATUS_STALE_DESTROYED:
                 // state is now stale. remove from modified
                 changeLog.modifiedStates.remove(state);
+                // TODO: remove operation as well
                 break;
             case ItemState.STATUS_STALE_MODIFIED:
                 // state is now stale. keep in modified. wait until refreshed
@@ -335,7 +306,7 @@
                 changeLog.added(state);
                 break;
             default:
-                log.warn("ItemState has invalid status: " + state.getStatus());
+                log.error("ItemState has invalid status: " + state.getStatus());
         }
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/WorkspaceItemStateManager.java Thu Oct  5 23:55:27 2006
@@ -19,9 +19,10 @@
 import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
 import org.apache.jackrabbit.spi.EventIterator;
 import org.apache.jackrabbit.spi.Event;
-import org.apache.jackrabbit.spi.ItemId;
-import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Set;
 import java.util.HashSet;
@@ -35,6 +36,8 @@
 public class WorkspaceItemStateManager extends CachingItemStateManager
     implements InternalEventListener {
 
+    private static Logger log = LoggerFactory.getLogger(WorkspaceItemStateManager.class);
+
     public WorkspaceItemStateManager(ItemStateFactory isf, IdFactory idFactory) {
         super(isf, idFactory);
     }
@@ -51,73 +54,106 @@
      * @param isLocal
      */
     public void onEvent(EventIterator events, boolean isLocal) {
+        onEvent(events, isLocal, null);
+    }
+
+    public void onEvent(EventIterator events, ChangeLog changeLog) {
+        if (changeLog == null) {
+            throw new IllegalArgumentException("ChangeLog must not be null.");
+        }
+        // inform all transient states, that they have been persisted and must
+        // connect to their workspace state (and eventually reload the data).
+        changeLog.persisted();
+
+        // inform all existing workspace states about the transient modifications
+        // that have been persisted now.
+        onEvent(events, true, changeLog);
+    }
+
+    private void onEvent(EventIterator events, boolean isLocal, ChangeLog changeLog) {
         // collect set of removed node ids
         Set removedNodeIds = new HashSet();
+        List addEventList = new ArrayList();
         List eventList = new ArrayList();
         while (events.hasNext()) {
-            Event e = events.nextEvent();
-            eventList.add(e);
+            Event event = events.nextEvent();
+            int type = event.getType();
+            if (type == Event.NODE_ADDED || event.getType() == Event.PROPERTY_ADDED) {
+                addEventList.add(event);
+            } else if (type == Event.NODE_REMOVED) {
+                // remember removed nodes separately for proper handling later on.
+                removedNodeIds.add(event.getItemId());
+                eventList.add(event);
+            } else {
+                eventList.add(event);
+            }
         }
-        if (eventList.isEmpty()) {
+        if (eventList.isEmpty() && addEventList.isEmpty()) {
             return;
         }
 
+        /* process remove and change events */
         for (Iterator it = eventList.iterator(); it.hasNext(); ) {
-            Event e = (Event) it.next();
-            ItemId itemId = e.getItemId();
-            NodeId parentId = e.getParentId();
-            ItemState state;
-            NodeState parent;
-            switch (e.getType()) {
-                case Event.NODE_ADDED:
-                case Event.PROPERTY_ADDED:
-                    state = lookup(itemId);
-                    if (state != null) {
-                        // TODO: item already exists ???
-                        // invalidate
-                        state.refresh();
-                    }
-                    parent = (NodeState) lookup(parentId);
-                    if (parent != null) {
-                        // discard and let wsp manager reload state when accessed next time
-                        parent.refresh();
-                    }
-                    break;
-                case Event.NODE_REMOVED:
-                case Event.PROPERTY_REMOVED:
-                    state = lookup(itemId);
-                    if (state != null) {
-                        state.notifyStateDestroyed();
-                    }
-                    state = lookup(parentId);
-                    if (state != null) {
-                        parent = (NodeState) state;
-                        // check if removed as well
-                        if (removedNodeIds.contains(parent.getId())) {
-                            // do not invalidate here
-                        } else {
-                            // discard and let wsp manager reload state when accessed next time
-                            parent.refresh();
-                        }
+            Event event = (Event) it.next();
+            int type = event.getType();
+
+            ItemState state = lookup(event.getItemId());
+            NodeState parent = (event.getParentId() != null) ? (NodeState) lookup(event.getParentId()) : null;
+
+            if (type == Event.NODE_REMOVED || type == Event.PROPERTY_REMOVED) {
+                if (state != null) {
+                    state.refresh(event, changeLog);
+                }
+                if (parent != null) {
+                    // invalidate parent only if it has not been removed
+                    // with the same event bundle.
+                    if (!removedNodeIds.contains(event.getParentId())) {
+                        parent.refresh(event, changeLog);
                     }
-                    break;
-                case Event.PROPERTY_CHANGED:
-                    state = lookup(itemId);
-                    // discard and let wsp manager reload state when accessed next time
-                    if (state != null) {
-                        state.refresh();
+                }
+            } else if (type == Event.PROPERTY_CHANGED) {
+                if (state != null) {
+                    try {
+                        // TODO: improve.
+                        /* retrieve property value and type from server even if
+                           changes were issued from this session (changelog).
+                           this is currently the only way to update the workspace
+                           state, which is not connected to its overlaying session-state.
+                        */
+                        PropertyState tmp = getItemStateFactory().createPropertyState((PropertyId) state.getId(), state.getParent());
+                        ((PropertyState) state).init(tmp.getType(), tmp.getValues());
+                        state.refresh(event, changeLog);
+                    } catch (ItemStateException e) {
+                        log.error("Unexpected error while updating modified property state.", e);
                     }
+                }
+            } else {
+                // should never occur
+                throw new IllegalArgumentException("Invalid event type: " + event.getType());
             }
         }
-    }
 
-    public void onEvent(EventIterator events, ChangeLog changeLog) {
-        if (changeLog == null) {
-            throw new IllegalArgumentException("ChangeLog must not be null.");
+        /* Add events need to be processed hierarchically, since its not possible
+           to add a new child reference to a state that is not yet present in
+           the state manager.
+           The 'progress' flag is used to make sure, that during each loop at
+           least one event has been processed and removed from the iterator.
+           If this is not the case, there are not parent states present in the
+           state manager that need to be updated and the remaining events may
+           be ignored.
+         */
+        boolean progress = true;
+        while (!addEventList.isEmpty() && progress) {
+            progress = false;
+            for (Iterator it = addEventList.iterator(); it.hasNext();) {
+                Event event = (Event) it.next();
+                NodeState parent = (NodeState) lookup(event.getParentId());
+                if (parent != null) {
+                    parent.refresh(event, changeLog);
+                    it.remove();
+                    progress = true;
+                }
+            }
         }
-        // apply the transient changes in changeLog to the ItemStates in the
-        // workspace layer and synchronize the changes recorded in the changelog
-        // with the events sent.
-        changeLog.persist(events);
     }
 }

Modified: jackrabbit/trunk/contrib/spi/spi2dav/project.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi2dav/project.xml?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi2dav/project.xml (original)
+++ jackrabbit/trunk/contrib/spi/spi2dav/project.xml Thu Oct  5 23:55:27 2006
@@ -100,6 +100,15 @@
                 <scope>runtime</scope>
             </properties>
         </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.2</version>
+            <url>http://jakarta.apache.org/commons/codec/</url>
+            <properties>
+                <scope>runtime</scope>
+            </properties>
+        </dependency>
     </dependencies>
 
     <!-- ====================================================================== -->

Modified: jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventImpl.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventImpl.java Thu Oct  5 23:55:27 2006
@@ -22,6 +22,7 @@
 import org.apache.jackrabbit.spi.NodeId;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.spi.SessionInfo;
+import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.webdav.xml.DomUtil;
 import org.apache.jackrabbit.webdav.observation.ObservationConstants;
 import org.apache.jackrabbit.webdav.observation.EventType;
@@ -29,6 +30,7 @@
 import org.apache.jackrabbit.webdav.DavConstants;
 import org.apache.jackrabbit.webdav.DavException;
 import org.apache.jackrabbit.webdav.jcr.observation.SubscriptionImpl;
+import org.apache.jackrabbit.util.Text;
 import org.w3c.dom.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,6 +47,7 @@
     private final Element eventElement;
     private final int type;
     private final ItemId itemId;
+    private final NodeId parentId;
     private final Path qPath;
 
     public EventImpl(Element eventElement, URIResolver uriResolver,
@@ -61,8 +64,11 @@
         String href = DomUtil.getChildTextTrim(eventElement, DavConstants.XML_HREF, DavConstants.NAMESPACE);
         if (type == Event.NODE_ADDED || type == Event.NODE_REMOVED) {
             itemId = uriResolver.getNodeId(href, sessionInfo);
+            String parentHref = Text.getRelativeParent(href, 1, true);
+            parentId = uriResolver.getNodeId(parentHref, sessionInfo);
         } else {
             itemId = uriResolver.getPropertyId(href, sessionInfo);
+            parentId = ((PropertyId)itemId).getParentId();
         }
         qPath = uriResolver.getQPath(href, sessionInfo);
     }
@@ -80,8 +86,7 @@
     }
 
     public NodeId getParentId() {
-        // TODO not available from XML_EVENT element
-        return null;
+        return parentId;
     }
 
     public String getUUID() {

Modified: jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java?view=diff&rev=453514&r1=453513&r2=453514
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java Thu Oct  5 23:55:27 2006
@@ -1467,17 +1467,13 @@
             checkConsumed();
 
             String uri = getItemUri(targetId, sessionInfo);
-            try {
-                // first unsubscribe in order to retrieve the events
-                String subscrUri = (targetId.denotesNode() ? uri : getItemUri(((PropertyId) targetId).getParentId(), sessionInfo));
-                EventIterator events = poll(subscrUri, subscriptionId, sessionInfo);
-                unsubscribe(subscrUri, subscriptionId, sessionInfo);
-                return events;
+            String subscrUri = (targetId.denotesNode() ? uri : getItemUri(((PropertyId) targetId).getParentId(), sessionInfo));
 
-            } finally {
+            UnLockMethod method = null;
+            try {
                 // make sure the lock initially created is removed again on the
-                // server.
-                UnLockMethod method = new UnLockMethod(uri, batchId);
+                // server, asking the server to persist the modifications
+                method = new UnLockMethod(uri, batchId);
                 // todo: check if 'initmethod' would work (ev. conflict with TxId header).
                 String[] locktokens = sessionInfo.getLockTokens();
                 if (locktokens != null && locktokens.length > 0) {
@@ -1486,18 +1482,24 @@
                 }
                 // in contrast to standard UNLOCK, the tx-unlock provides a
                 // request body.
-                try {
-                    method.setRequestBody(new TransactionInfo(commit));
-                    client.executeMethod(method);
-                    method.checkSuccess();
-                } catch (IOException e) {
-                    throw new RepositoryException(e);
-                } catch (DavException e) {
-                    throw ExceptionConverter.generate(e);
-                } finally {
+                method.setRequestBody(new TransactionInfo(commit));
+                client.executeMethod(method);
+                method.checkSuccess();
+
+                // retrieve events
+                EventIterator events = poll(subscrUri, subscriptionId, sessionInfo);
+                return events;
+            } catch (IOException e) {
+                throw new RepositoryException(e);
+            } catch (DavException e) {
+                throw ExceptionConverter.generate(e);
+            } finally {
+                if (method != null) {
                     // release UNLOCK method
                     method.releaseConnection();
                 }
+                // unsubscribe
+                unsubscribe(subscrUri, subscriptionId, sessionInfo);
             }
         }