You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2006/11/09 14:12:23 UTC

svn commit: r472888 [1/2] - in /jackrabbit/trunk/contrib/spi: client/ jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/ jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observatio...

Author: mreutegg
Date: Thu Nov  9 05:12:21 2006
New Revision: 472888

URL: http://svn.apache.org/viewvc?view=rev&rev=472888
Log:
- add commons-codec dependency to client project.xml
- add concurrent dependency to jcr2spi project.xml
- remove events again from operation methods on RepositoryService.

Added:
    jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/EventFilter.java   (with props)
    jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventFilterImpl.java   (with props)
Removed:
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/EventFilter.java
Modified:
    jackrabbit/trunk/contrib/spi/client/project.xml
    jackrabbit/trunk/contrib/spi/jcr2spi/project.xml
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/FilteredEventIterator.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/InternalEventListener.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/ObservationManagerImpl.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/NodeState.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.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/WorkspaceItemStateManager.java
    jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java
    jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
    jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/SessionInfo.java
    jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/package.html
    jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/IdFactoryImpl.java
    jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java
    jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/URIResolverImpl.java

Modified: jackrabbit/trunk/contrib/spi/client/project.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/client/project.xml?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/client/project.xml (original)
+++ jackrabbit/trunk/contrib/spi/client/project.xml Thu Nov  9 05:12:21 2006
@@ -101,6 +101,15 @@
             <version>3.1</version>
         </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>
+        <dependency>
             <groupId>xerces</groupId>
             <artifactId>xercesImpl</artifactId>
             <version>2.6.2</version>

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/project.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/project.xml?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/project.xml (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/project.xml Thu Nov  9 05:12:21 2006
@@ -63,6 +63,12 @@
             <version>3.1</version>
             <url>http://jakarta.apache.org/commons/collections/</url>
         </dependency>
+        <dependency>
+            <groupId>concurrent</groupId>
+            <artifactId>concurrent</artifactId>
+            <version>1.3.4</version>
+            <url>http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html</url>
+        </dependency>
         <!-- test only -->
         <dependency>
             <groupId>junit</groupId>

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java Thu Nov  9 05:12:21 2006
@@ -496,8 +496,6 @@
      * @return a new <code>ObservationManager</code> instance
      */
     protected ObservationManager createObservationManager(NamespaceResolver nsResolver, NodeTypeRegistry ntRegistry) {
-        ObservationManagerImpl obsMgr = new ObservationManagerImpl(nsResolver, ntRegistry);
-        wspManager.addEventListener(obsMgr);
-        return obsMgr;
+        return new ObservationManagerImpl(wspManager, nsResolver, ntRegistry);
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceManager.java Thu Nov  9 05:12:21 2006
@@ -72,6 +72,7 @@
 import org.apache.jackrabbit.spi.PropertyId;
 import org.apache.jackrabbit.spi.Batch;
 import org.apache.jackrabbit.spi.EventBundle;
+import org.apache.jackrabbit.spi.EventFilter;
 import org.apache.jackrabbit.value.QValue;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
@@ -102,8 +103,14 @@
 import java.util.HashSet;
 import java.util.Collection;
 import java.util.Map;
+import java.util.Collections;
 import java.io.InputStream;
 
+import EDU.oswego.cs.dl.util.concurrent.Channel;
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+import EDU.oswego.cs.dl.util.concurrent.Latch;
+import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
+
 /**
  * <code>WorkspaceManager</code>...
  */
@@ -132,6 +139,13 @@
     private final Object updateMonitor = new Object();
 
     /**
+     * A producer for this channel can request an immediate poll for events
+     * by placing a Sync into the channel. The Sync is released when the event
+     * poll finished.
+     */
+    private final Channel immediateEventRequests = new LinkedQueue();
+
+    /**
      * This is the event polling for external changes. If <code>null</code>
      * then the underlying repository service does not support observation.
      */
@@ -141,7 +155,7 @@
      * List of event listener that are set on this WorkspaceManager to get
      * notifications about local and external changes.
      */
-    private Set listeners = new HashSet();
+    private final Set listeners = Collections.synchronizedSet(new HashSet());
 
     public WorkspaceManager(RepositoryService service, SessionInfo sessionInfo) throws RepositoryException {
         this.service = service;
@@ -249,10 +263,35 @@
         listeners.remove(listener);
     }
 
+    /**
+     * Creates an event filter based on the parameters available in {@link
+     * javax.jcr.observation.ObservationManager#addEventListener}.
+     *
+     * @param eventTypes   A combination of one or more event type constants
+     *                     encoded as a bitmask.
+     * @param path         an absolute path.
+     * @param isDeep       a <code>boolean</code>.
+     * @param uuids        array of UUIDs.
+     * @param nodeTypes    array of node type names.
+     * @param noLocal      a <code>boolean</code>.
+     * @return the event filter instance with the given parameters.
+     * @throws UnsupportedRepositoryOperationException
+     *          if this implementation does not support observation.
+     */
+    public EventFilter createEventFilter(int eventTypes,
+                                         Path path,
+                                         boolean isDeep,
+                                         String[] uuids,
+                                         QName[] nodeTypes,
+                                         boolean noLocal)
+            throws UnsupportedRepositoryOperationException {
+        return service.createEventFilter(eventTypes, path, isDeep, uuids, nodeTypes, noLocal);
+    }
+
     //--------------------------------------------------------------------------
     private ItemStateManager createItemStateManager() {
         ItemStateFactory isf = new WorkspaceItemStateFactory(service, sessionInfo, this);
-        WorkspaceItemStateManager ism = new WorkspaceItemStateManager(isf, service.getIdFactory());
+        WorkspaceItemStateManager ism = new WorkspaceItemStateManager(this, isf, service.getIdFactory());
         addEventListener(ism);
         return ism;
     }
@@ -368,8 +407,16 @@
      * @see UpdatableItemStateManager#execute(Operation)
      */
     public void execute(Operation operation) throws RepositoryException {
+        Sync eventSignal;
         synchronized (updateMonitor) {
             new OperationVisitorImpl(sessionInfo).execute(operation);
+            eventSignal = getEventPollingRequest();
+        }
+        try {
+            eventSignal.acquire();
+        } catch (InterruptedException e) {
+            Thread.interrupted();
+            log.warn("Interrupted while waiting for events from RepositoryService");
         }
     }
 
@@ -380,8 +427,17 @@
      * @throws RepositoryException
      */
     public void execute(ChangeLog changes) throws RepositoryException {
+        Sync eventSignal;
         synchronized (updateMonitor) {
             new OperationVisitorImpl(sessionInfo).execute(changes);
+            changes.persisted();
+            eventSignal = getEventPollingRequest();
+        }
+        try {
+            eventSignal.acquire();
+        } catch (InterruptedException e) {
+            Thread.interrupted();
+            log.warn("Interrupted while waiting for events from RepositoryService");
         }
     }
 
@@ -532,42 +588,17 @@
 
     /**
      * Called when local or external events occured. This method is called after
-     * changes have been applied to the repository. Depending on <code>changeLog</code>
-     * this method is called as a result of:
-     * <ul>
-     * <li>a local <code>save</code> of transient changes. In this case
-     * <code>changeLog</code> is non-<code>null</code>.</li>
-     * <li>an execution of a workspace operation. In this case
-     * <code>changeLog</code> is <code>null</code></li>
-     * <li>an external change to the workspace (another session modified the
-     * workspace). In this case <code>changeLog</code> is <code>null</code></li>
-     * </ul>
-     *
-     * @param events    the events generated by the repository service as a
-     *                  response to the executed operation(s).
-     * @param changeLog the local <code>ChangeLog</code> which contains the
-     *                  affected transient <code>ItemState</code>s and the
-     *                  relevant {@link Operation}s that lead to the
-     *                  modifications. If <code>null</code> this method is
-     *                  called as a consequence of an external change or a call
-     *                  of a workspace operation. In that case there are no
-     *                  local transient changes.
+     * changes have been applied to the repository.
+     *
+     * @param events the events generated by the repository service as the
+     *               effect of a change.
      */
-    private void onEventReceived(EventBundle[] events, ChangeLog changeLog) {
+    private void onEventReceived(EventBundle[] events) {
         // notify listener
         InternalEventListener[] lstnrs = (InternalEventListener[]) listeners.toArray(new InternalEventListener[listeners.size()]);
         for (int i = 0; i < events.length; i++) {
-            EventBundle bundle = events[i];
-            if (bundle.isLocal() && changeLog != null) {
-                // local change from batch operation
-                for (int j = 0; j < lstnrs.length; j++) {
-                    lstnrs[j].onEvent(bundle, changeLog);
-                }
-            } else {
-                // external change or workspace operation
-                for (int j = 0; j < lstnrs.length; j++) {
-                    lstnrs[j].onEvent(bundle);
-                }
+            for (int j = 0; j < lstnrs.length; j++) {
+                lstnrs[j].onEvent(events[i]);
             }
         }
     }
@@ -584,7 +615,6 @@
         private final SessionInfo sessionInfo;
 
         private Batch batch;
-        private EventBundle[] events;
 
         private OperationVisitorImpl(SessionInfo sessionInfo) {
             this.sessionInfo = sessionInfo;
@@ -605,8 +635,7 @@
                 }
             } finally {
                 if (batch != null) {
-                    events = service.submit(batch);
-                    onEventReceived(events, changeLog);
+                    service.submit(batch);
                     // reset batch field
                     batch = null;
                 }
@@ -617,19 +646,8 @@
          * Executes the operations on the repository service.
          */
         private void execute(Operation workspaceOperation) throws RepositoryException, ConstraintViolationException, AccessDeniedException, ItemExistsException, NoSuchNodeTypeException, UnsupportedRepositoryOperationException, VersionException {
-            boolean success = false;
-            try {
-                log.info("executing: " + workspaceOperation);
-                workspaceOperation.accept(this);
-                success = true;
-            } finally {
-                if (success && events != null) {
-                    // a workspace operation is like an external change: there
-                    // is no changelog to persist. but still the events must
-                    // be reported as local changes.
-                    onEventReceived(events, null);
-                }
-            }
+            log.info("executing: " + workspaceOperation);
+            workspaceOperation.accept(this);
         }
         //-----------------------< OperationVisitor >---------------------------
         // TODO: review retrival of ItemIds for transient modifications 
@@ -671,20 +689,20 @@
         public void visit(Clone operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
             NodeId destParentId = operation.getDestinationParentState().getNodeId();
-            events = service.clone(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName(), operation.isRemoveExisting());
+            service.clone(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName(), operation.isRemoveExisting());
         }
 
         public void visit(Copy operation) throws NoSuchWorkspaceException, LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
             NodeId destParentId = operation.getDestinationParentState().getNodeId();
-            events = service.copy(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName());
+            service.copy(sessionInfo, operation.getWorkspaceName(), nId, destParentId, operation.getDestinationName());
         }
 
         public void visit(Move operation) throws LockException, ConstraintViolationException, AccessDeniedException, ItemExistsException, UnsupportedRepositoryOperationException, VersionException, RepositoryException {
             NodeId moveId = operation.getSourceId();
             NodeId destParentId = operation.getDestinationParentState().getNodeId();
             if (batch == null) {
-                events = service.move(sessionInfo, moveId, destParentId, operation.getDestinationName());
+                service.move(sessionInfo, moveId, destParentId, operation.getDestinationName());
             } else {
                 batch.move(moveId, destParentId, operation.getDestinationName());
             }
@@ -692,7 +710,7 @@
 
         public void visit(Update operation) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
-            events = service.update(sessionInfo, nId, operation.getSourceWorkspaceName());
+            service.update(sessionInfo, nId, operation.getSourceWorkspaceName());
         }
 
         public void visit(Remove operation) throws RepositoryException {
@@ -744,11 +762,11 @@
         }
 
         public void visit(Checkout operation) throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
-            events = service.checkout(sessionInfo, operation.getNodeState().getNodeId());
+            service.checkout(sessionInfo, operation.getNodeState().getNodeId());
         }
 
         public void visit(Checkin operation) throws UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
-            events = service.checkin(sessionInfo, operation.getNodeState().getNodeId());
+            service.checkin(sessionInfo, operation.getNodeState().getNodeId());
         }
 
         public void visit(Restore operation) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
@@ -764,7 +782,7 @@
             }
 
             if (nState == null) {
-                events = service.restore(sessionInfo, vIds, operation.removeExisting());
+                service.restore(sessionInfo, vIds, operation.removeExisting());
             } else {
                 if (vIds.length > 1) {
                     throw new IllegalArgumentException("Restore from a single node must specify but one single Version.");
@@ -777,27 +795,14 @@
                 } else {
                     targetId = nState.getNodeId();
                 }
-                events = service.restore(sessionInfo, targetId, vIds[0], operation.removeExisting());
+                service.restore(sessionInfo, targetId, vIds[0], operation.removeExisting());
             }
         }
 
         public void visit(Merge operation) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
-            events = service.merge(sessionInfo, nId, operation.getSourceWorkspaceName(), operation.bestEffort());
-            List externalEventBundles = new ArrayList();
-            for (int i = 0; i < events.length; i++) {
-                if (events[i].isLocal()) {
-                    // todo: improve.... inform operation about modified items (build mergefailed iterator)
-                    operation.getEventListener().onEvent(events[i]);
-                } else {
-                    // otherwise dispatch as external event
-                    externalEventBundles.add(events[i]);
-                }
-            }
-            if (!externalEventBundles.isEmpty()) {
-                EventBundle[] bundles = (EventBundle[]) externalEventBundles.toArray(new EventBundle[externalEventBundles.size()]);
-                onEventReceived(bundles, null);
-            }
+            // todo service should return ids of failed nodes
+            service.merge(sessionInfo, nId, operation.getSourceWorkspaceName(), operation.bestEffort());
         }
 
         public void visit(ResolveMergeConflict operation) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
@@ -838,7 +843,7 @@
                 if (resolveDone) {
                     predecessorIds[i] = vId;
                 }
-                events = service.resolveMergeConflict(sessionInfo, nId, mergeFailedIds, predecessorIds);
+                service.resolveMergeConflict(sessionInfo, nId, mergeFailedIds, predecessorIds);
             } catch (ItemStateException e) {
                 // should not occur.
                 throw new RepositoryException(e);
@@ -847,30 +852,62 @@
 
         public void visit(LockOperation operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
-            events = service.lock(sessionInfo, nId, operation.isDeep(), operation.isSessionScoped());
+            service.lock(sessionInfo, nId, operation.isDeep(), operation.isSessionScoped());
         }
 
         public void visit(LockRefresh operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
-            events = service.refreshLock(sessionInfo, nId);
+            service.refreshLock(sessionInfo, nId);
         }
 
         public void visit(LockRelease operation) throws AccessDeniedException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
             NodeId nId = operation.getNodeState().getNodeId();
-            events = service.unlock(sessionInfo, nId);
+            service.unlock(sessionInfo, nId);
         }
 
         public void visit(AddLabel operation) throws VersionException, RepositoryException {
             NodeId vhId = operation.getVersionHistoryState().getNodeId();
             NodeId vId = operation.getVersionState().getNodeId();
-            events = service.addVersionLabel(sessionInfo, vhId, vId, operation.getLabel(), operation.moveLabel());
+            service.addVersionLabel(sessionInfo, vhId, vId, operation.getLabel(), operation.moveLabel());
         }
 
         public void visit(RemoveLabel operation) throws VersionException, RepositoryException {
             NodeId vhId = operation.getVersionHistoryState().getNodeId();
             NodeId vId = operation.getVersionState().getNodeId();
-            events = service.removeVersionLabel(sessionInfo, vhId, vId, operation.getLabel());
+            service.removeVersionLabel(sessionInfo, vhId, vId, operation.getLabel());
+        }
+    }
+
+    /**
+     * Requests an immediate poll for events. The returned Sync will be
+     * released by the event polling thread when events have been retrieved.
+     */
+    private Sync getEventPollingRequest() {
+        Sync signal;
+        if (externalChangeFeed != null) {
+            // observation supported
+            signal = new Latch();
+            try {
+                immediateEventRequests.put(signal);
+            } catch (InterruptedException e) {
+                log.warn("Unable to request immediate event poll: " + e);
+            }
+        } else {
+            // no observation, return a dummy sync which can be acquired immediately
+            signal = new Sync() {
+                public void acquire() {
+                }
+
+                public boolean attempt(long l) {
+                    return true;
+                }
+
+                public void release() {
+                    throw new UnsupportedOperationException();
+                }
+            };
         }
+        return signal;
     }
 
     /**
@@ -895,21 +932,48 @@
         public void run() {
             while (!Thread.interrupted()) {
                 try {
-                    Thread.sleep(pollingInterval);
-                } catch (InterruptedException e) {
-                    // terminate
-                    break;
-                }
-                try {
+                    // wait for a signal to do an immediate poll but wait at
+                    // most EXTERNAL_EVENT_POLLING_INTERVAL
+                    Sync signal = (Sync) immediateEventRequests.poll(pollingInterval);
+
                     synchronized (updateMonitor) {
-                        EventBundle[] bundles = service.getEvents(sessionInfo, 0);
+                        // if this thread was waiting for updateMonitor and now
+                        // enters this synchronized block, then a user thread
+                        // has just finished an operation and will probably
+                        // request an immediate event poll. That's why we
+                        // check here again for a sync signal
+                        if (signal == null) {
+                            signal = (Sync) immediateEventRequests.poll(0);
+                        }
+
+                        if (signal != null) {
+                            log.debug("Request for immediate event poll");
+                        }
+
+                        // get filters from listeners
+                        List filters = new ArrayList();
+                        InternalEventListener[] iel = (InternalEventListener[]) listeners.toArray(new InternalEventListener[0]);
+                        for (int i = 0; i < iel.length; i++) {
+                            filters.addAll(iel[i].getEventFilters());
+                        }
+                        EventBundle[] bundles = service.getEvents(sessionInfo,
+                                0, (EventFilter[]) filters.toArray(
+                                        new EventFilter[filters.size()]));
                         if (bundles.length > 0) {
-                            onEventReceived(bundles, null);
+                            onEventReceived(bundles);
+                        }
+                        if (signal != null) {
+                            log.debug("About to signal that events have been delivered");
+                            signal.release();
+                            log.debug("Event delivery signaled");
                         }
                     }
                 } catch (RepositoryException e) {
                     log.warn("Exception while retrieving event bundles: " + e);
                     log.debug("Dump:", e);
+                } catch (InterruptedException e) {
+                    // terminate
+                    break;
                 }
             }
         }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/lock/LockManagerImpl.java Thu Nov  9 05:12:21 2006
@@ -25,13 +25,12 @@
 import org.apache.jackrabbit.jcr2spi.operation.LockRelease;
 import org.apache.jackrabbit.jcr2spi.operation.LockRefresh;
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
-import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
 import org.apache.jackrabbit.jcr2spi.state.Status;
 import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.spi.EventIterator;
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.LockInfo;
 import org.apache.jackrabbit.spi.EventBundle;
+import org.apache.jackrabbit.spi.EventFilter;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
@@ -45,6 +44,8 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Collection;
+import java.util.Collections;
 
 /**
  * <code>LockManagerImpl</code>...
@@ -454,6 +455,7 @@
         private final NodeState lockHoldingState;
         private LockInfo lockInfo;
         private boolean isLive = true;
+        private final EventFilter eventFilter;
 
         private LockState(NodeState lockHoldingState) throws LockException, RepositoryException {
             lockHoldingState.checkIsWorkspaceState();
@@ -466,6 +468,8 @@
 
             // register as internal listener to the wsp manager in order to get
             // informed if this lock ends his life.
+            eventFilter = wspManager.createEventFilter(Event.PROPERTY_REMOVED,
+                    lockHoldingState.getQPath(), false, null, null, true);
             wspManager.addEventListener(this);
         }
 
@@ -532,6 +536,17 @@
         }
 
         //------------------------------------------< InternalEventListener >---
+
+        /**
+         * @see InternalEventListener#getEventFilters()
+         */
+        public Collection getEventFilters() {
+            return Collections.singletonList(eventFilter);
+        }
+
+        /**
+         * @see InternalEventListener#onEvent(EventBundle)
+         */
         public void onEvent(EventBundle eventBundle) {
             if (!isLive) {
                 // since we only monitor the removal of the lock (by means
@@ -540,26 +555,10 @@
                 return;
             }
 
-            for (EventIterator it = eventBundle.getEvents(); it.hasNext(); ) {
-                Event ev = it.nextEvent();
-                // if the jcr:lockIsDeep property related to this Lock got removed,
-                // we assume that the lock has been released.
-                // TODO: not correct to compare nodeIds
-                if (ev.getType() == Event.PROPERTY_REMOVED
-                    && QName.JCR_LOCKISDEEP.equals(ev.getQPath().getNameElement().getName())
-                    && lockHoldingState.getNodeId().equals(ev.getParentId())) {
-
-                    // this lock has been release by someone else (and not by
-                    // a call to LockManager#unlock -> clean up and set isLive
-                    // flag to false.
-                    unlocked();
-                    break;
-                }
-            }
-        }
-
-        public void onEvent(EventBundle eventBundle, ChangeLog changeLog) {
-            // nothing to do. not interested in transient modifications
+            // this lock has been release by someone else (and not by
+            // a call to LockManager#unlock -> clean up and set isLive
+            // flag to false.
+            unlocked();
         }
     }
 

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/FilteredEventIterator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/FilteredEventIterator.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/FilteredEventIterator.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/FilteredEventIterator.java Thu Nov  9 05:12:21 2006
@@ -19,8 +19,9 @@
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 import org.apache.jackrabbit.spi.EventBundle;
+import org.apache.jackrabbit.spi.EventFilter;
+import org.apache.jackrabbit.name.NamespaceResolver;
 
-import javax.jcr.RepositoryException;
 import javax.jcr.observation.Event;
 import javax.jcr.observation.EventIterator;
 
@@ -53,6 +54,11 @@
     private final boolean isLocal;
 
     /**
+     * The namespace resolver of the session that created this event iterator.
+     */
+    private final NamespaceResolver nsResolver;
+
+    /**
      * The next {@link javax.jcr.observation.Event} in this iterator
      */
     private Event next;
@@ -65,15 +71,20 @@
     /**
      * Creates a new <code>FilteredEventIterator</code>.
      *
-     * @param events the {@link org.apache.jackrabbit.spi.Event}s as a bundle.
-     * @param filter only event that pass the filter will be dispatched to the
-     *               event listener.
+     * @param events     the {@link org.apache.jackrabbit.spi.Event}s as a
+     *                   bundle.
+     * @param filter     only event that pass the filter will be dispatched to
+     *                   the event listener.
+     * @param nsResolver the namespace resolver of the session that created this
+     *                   <code>FilteredEventIterator</code>.
      */
     public FilteredEventIterator(EventBundle events,
-                                 EventFilter filter) {
-        actualEvents = events.getEvents();
+                                 EventFilter filter,
+                                 NamespaceResolver nsResolver) {
+        this.actualEvents = events.getEvents();
         this.filter = filter;
         this.isLocal = events.isLocal();
+        this.nsResolver = nsResolver;
         fetchNext();
     }
 
@@ -151,11 +162,7 @@
         next = null;
         while (next == null && actualEvents.hasNext()) {
             event = (org.apache.jackrabbit.spi.Event) actualEvents.next();
-            try {
-                next = filter.blocks(event, isLocal) ? null : new EventImpl(filter.getNamespaceResolver(), event);
-            } catch (RepositoryException e) {
-                log.error("Exception while applying filter.", e);
-            }
+            next = filter.accept(event, isLocal) ? new EventImpl(nsResolver, event) : null;
         }
     }
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/InternalEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/InternalEventListener.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/InternalEventListener.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/InternalEventListener.java Thu Nov  9 05:12:21 2006
@@ -17,7 +17,8 @@
 package org.apache.jackrabbit.jcr2spi.observation;
 
 import org.apache.jackrabbit.spi.EventBundle;
-import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
+
+import java.util.Collection;
 
 /**
  * <code>InternalEventListener</code> receives changes as a result of a local
@@ -32,5 +33,15 @@
      */
     public void onEvent(EventBundle eventBundle);
 
-    public void onEvent(EventBundle eventBundle, ChangeLog changeLog);
+    /**
+     * Returns a collection of event filters which is in use by this event
+     * listener. The event bundles delivered to {@link #onEvent} will be filtered
+     * using the collection returned by this method. An event is included
+     * in an event bundles if it is accepted by at least one of the filters
+     * returned by this method.
+     *
+     * @return an unmodifiable collection of {@link EventFilter}s currently
+     * in use by this event listener.
+     */
+    public Collection getEventFilters();
 }

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/ObservationManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/ObservationManagerImpl.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/ObservationManagerImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/observation/ObservationManagerImpl.java Thu Nov  9 05:12:21 2006
@@ -17,7 +17,7 @@
 package org.apache.jackrabbit.jcr2spi.observation;
 
 import org.apache.jackrabbit.jcr2spi.nodetype.NodeTypeRegistry;
-import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
+import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
 import org.apache.jackrabbit.name.MalformedPathException;
 import org.apache.jackrabbit.name.NameException;
 import org.apache.jackrabbit.name.NameFormat;
@@ -26,6 +26,7 @@
 import org.apache.jackrabbit.name.PathFormat;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.spi.EventBundle;
+import org.apache.jackrabbit.spi.EventFilter;
 import org.apache.jackrabbit.util.IteratorHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,6 +39,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * <code>ObservationManagerImpl</code>...
@@ -50,6 +53,11 @@
     private static final Logger log = LoggerFactory.getLogger(ObservationManagerImpl.class);
 
     /**
+     * The workspace manager.
+     */
+    private final WorkspaceManager wspManager;
+
+    /**
      * The session this observation manager belongs to.
      */
     private final NamespaceResolver nsResolver;
@@ -71,13 +79,16 @@
 
     /**
      * Creates a new observation manager for <code>session</code>.
+     * @param wspManager the WorkspaceManager.
      * @param nsResolver NamespaceResolver to be used by this observation manager
      * is based on.
      * @param ntRegistry The <code>NodeTypeRegistry</code> of the session.
      */
-    public ObservationManagerImpl(NamespaceResolver nsResolver, NodeTypeRegistry ntRegistry) {
+    public ObservationManagerImpl(WorkspaceManager wspManager, NamespaceResolver nsResolver, NodeTypeRegistry ntRegistry) {
+        this.wspManager = wspManager;
         this.nsResolver = nsResolver;
         this.ntRegistry = ntRegistry;
+        this.wspManager.addEventListener(this);
     }
 
     /**
@@ -87,8 +98,8 @@
                                  int eventTypes,
                                  String absPath,
                                  boolean isDeep,
-                                 String[] uuid,
-                                 String[] nodeTypeName,
+                                 String[] uuids,
+                                 String[] nodeTypeNames,
                                  boolean noLocal) throws RepositoryException {
         Path path;
         try {
@@ -98,18 +109,18 @@
         }
 
         // create NodeType instances from names
-        QName[] nodeTypeNames;
-        if (nodeTypeName == null) {
-            nodeTypeNames = null;
+        QName[] nodeTypeQNames;
+        if (nodeTypeNames == null) {
+            nodeTypeQNames = null;
         } else {
             try {
-                nodeTypeNames = new QName[nodeTypeName.length];
-                for (int i = 0; i < nodeTypeName.length; i++) {
-                    QName ntName = NameFormat.parse(nodeTypeName[i], nsResolver);
+                nodeTypeQNames = new QName[nodeTypeNames.length];
+                for (int i = 0; i < nodeTypeNames.length; i++) {
+                    QName ntName = NameFormat.parse(nodeTypeNames[i], nsResolver);
                     if (!ntRegistry.isRegistered(ntName)) {
-                        throw new RepositoryException("unknown node type: " + nodeTypeName[i]);
+                        throw new RepositoryException("unknown node type: " + nodeTypeNames[i]);
                     }
-                    nodeTypeNames[i] = ntName;
+                    nodeTypeQNames[i] = ntName;
                 }
             } catch (NameException e) {
                 throw new RepositoryException(e.getMessage());
@@ -117,8 +128,7 @@
         }
 
         synchronized (subscriptions) {
-            EventFilter filter = new EventFilter(nsResolver, ntRegistry,
-                    eventTypes, path, isDeep, uuid, nodeTypeNames, noLocal);
+            EventFilter filter = wspManager.createEventFilter(eventTypes, path, isDeep, uuids, nodeTypeQNames, noLocal);
             subscriptions.put(listener, filter);
             readOnlySubscriptions = null;
         }
@@ -149,6 +159,15 @@
 
     //-----------------------< InternalEventListener >--------------------------
 
+    public Collection getEventFilters() {
+        List filters = new ArrayList();
+        synchronized (subscriptions) {
+            ensureReadOnlyMap();
+            filters.addAll(readOnlySubscriptions.values());
+        }
+        return filters;
+    }
+
     public void onEvent(EventBundle eventBundle) {
         // get active listeners
         Map activeListeners;
@@ -159,7 +178,7 @@
         for (Iterator it = activeListeners.keySet().iterator(); it.hasNext(); ) {
             EventListener listener = (EventListener) it.next();
             EventFilter filter = (EventFilter) activeListeners.get(listener);
-            FilteredEventIterator eventIter = new FilteredEventIterator(eventBundle, filter);
+            FilteredEventIterator eventIter = new FilteredEventIterator(eventBundle, filter, nsResolver);
             if (eventIter.hasNext()) {
                 try {
                     listener.onEvent(eventIter);
@@ -170,17 +189,6 @@
                 }
             }
         }
-    }
-
-    /**
-     * Same as {@link #onEvent(EventBundle)} but only used for local changes
-     * with a <code>ChangeLog</code>.
-     * 
-     * @param eventBundle
-     * @param changeLog
-     */
-    public void onEvent(EventBundle eventBundle, ChangeLog changeLog) {
-        onEvent(eventBundle);
     }
 
     //-------------------------< internal >-------------------------------------

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=472888&r1=472887&r2=472888
==============================================================================
--- 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 Nov  9 05:12:21 2006
@@ -122,6 +122,15 @@
         modifiedRefs.add(refs);
     }
 
+    /**
+     * Call this method when this change log has been sucessfully persisted.
+     * This implementation will call {@link ItemState#refresh(ChangeLog)
+     * ItemState.refresh(this)} on the target item of this change log.
+     */
+    public void persisted() {
+        target.refresh(this);
+    }
+
     //----------------------< Retrieve information present in the ChangeLog >---
     /**
      *

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=472888&r1=472887&r2=472888
==============================================================================
--- 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 Nov  9 05:12:21 2006
@@ -447,11 +447,9 @@
      * successfully submitted to the SPI..
      *
      * @param changeLog
-     * @return a Set of <code>ItemId</code>s in order to allow the caller to
-     * remove those events that have already been processed.
      * @throws IllegalStateException if this state is a 'session' state.
      */
-    abstract Set refresh(ChangeLog changeLog) throws IllegalStateException;
+    abstract void refresh(ChangeLog changeLog) throws IllegalStateException;
 
     /**
      * Copy all state information from overlayed state to this state

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=472888&r1=472887&r2=472888
==============================================================================
--- 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 Nov  9 05:12:21 2006
@@ -141,7 +141,7 @@
         super(overlayedState, parent, initialStatus, isf, idFactory);
         if (overlayedState != null) {
             synchronized (overlayedState) {
-                NodeState wspState = (NodeState) overlayedState;
+                NodeState wspState = overlayedState;
                 name = wspState.name;
                 uuid = wspState.uuid;
                 nodeTypeName = wspState.nodeTypeName;
@@ -713,12 +713,11 @@
      * {@inheritDoc}
      * @see ItemState#refresh(ChangeLog)
      */
-    Set refresh(ChangeLog changeLog) throws IllegalStateException {
+    void refresh(ChangeLog changeLog) throws IllegalStateException {
 
         // remember parent states that have need to adjust their uuid/mixintypes
         // or that got a new child entry added or existing entries removed.
         Map modParents = new HashMap();
-        Set processedIds = new HashSet();
 
         // process deleted states from the changelog
         for (Iterator it = changeLog.deletedStates(); it.hasNext();) {
@@ -737,9 +736,6 @@
                 }
                 modifiedParent(parent, state, modParents);
             }
-            // don't remove processed state from changelog, but from event list
-            // state on changelog is used for check if parent is deleted as well.
-            processedIds.add(state.getId());
         }
 
         // process added states from the changelog. since the changlog maintains
@@ -787,7 +783,6 @@
                 }
 
                 it.remove();
-                processedIds.add(addedState.getId());
             } catch (ItemStateException e) {
                 log.error("Internal error.", e);
             }
@@ -802,7 +797,6 @@
                     // move overlayed state as well
                     NodeState newParent = (NodeState) modState.parent.overlayedState;
                     NodeState overlayed = (NodeState) modState.overlayedState;
-                    ItemId removedId = overlayed.getId();
                     try {
                         overlayed.parent.moveEntry(newParent, overlayed, modNodeState.getQName(), modNodeState.getDefinition());
                     } catch (RepositoryException e) {
@@ -813,8 +807,6 @@
                     modNodeState.setStatus(Status.EXISTING);
                     it.remove();
 
-                    processedIds.add(removedId);
-                    processedIds.add(modNodeState.getId());
                 } else {
                     modifiedParent((NodeState)modState, null, modParents);
                 }
@@ -832,8 +824,6 @@
                     modifiedParent(modState.getParent(), modState, modParents);
                 }
                 it.remove();
-                // remove the property-modification event from the set
-                processedIds.add(modState.getId());
             }
         }
 
@@ -858,8 +848,6 @@
                 // TODO: discard state and force reload of all data
             }
         }
-
-        return processedIds;
     }
 
     /**

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PathResolver.java Thu Nov  9 05:12:21 2006
@@ -143,6 +143,15 @@
         NodeState state = start;
         for (int i = 0; i < path.getLength(); i++) {
             Path.PathElement elem = path.getElement(i);
+            // check for root element
+            if (elem.denotesRoot()) {
+                if (start.getParent() != null) {
+                    throw new NoSuchItemStateException(path.toString());
+                } else {
+                    continue;
+                }
+            }
+
             // first try to resolve node
             if (state.hasChildNodeEntry(elem.getName(), elem.getNormalizedIndex())) {
                 ChildNodeEntry cne = state.getChildNodeEntry(elem.getName(), elem.getNormalizedIndex());

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=472888&r1=472887&r2=472888
==============================================================================
--- 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 Nov  9 05:12:21 2006
@@ -35,7 +35,6 @@
 import java.util.Set;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.HashSet;
 
 /**
  * <code>PropertyState</code> represents the state of a <code>Property</code>.
@@ -262,8 +261,7 @@
      * {@inheritDoc}
      * @see ItemState#refresh(ChangeLog)
      */
-    Set refresh(ChangeLog changeLog) throws IllegalStateException {
-        Set processedIds = new HashSet(1);
+    void refresh(ChangeLog changeLog) throws IllegalStateException {
         for (Iterator it = changeLog.modifiedStates(); it.hasNext();) {
             ItemState modState = (ItemState) it.next();
             if (modState == this) {
@@ -275,12 +273,8 @@
                 // push changes to overlayed state and reset status
                 ((PropertyState) overlayedState).init(getType(), getValues());
                 setStatus(Status.EXISTING);
-                // parent must not be informed, since all properties that
-                // affect the parent state (uuid, mixins) are protected.
-                processedIds.add(modState.getId());
             }
         }
-        return processedIds;
     }
 
     /**

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=472888&r1=472887&r2=472888
==============================================================================
--- 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 Nov  9 05:12:21 2006
@@ -17,20 +17,24 @@
 package org.apache.jackrabbit.jcr2spi.state;
 
 import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
+import org.apache.jackrabbit.jcr2spi.WorkspaceManager;
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.EventBundle;
 import org.apache.jackrabbit.spi.EventIterator;
-import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.EventFilter;
+import org.apache.jackrabbit.name.Path;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.UnsupportedRepositoryOperationException;
 import java.util.Set;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Collection;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Collections;
 
 /**
  * <code>WorkspaceItemStateManager</code>
@@ -40,12 +44,26 @@
 
     private static Logger log = LoggerFactory.getLogger(WorkspaceItemStateManager.class);
 
-    public WorkspaceItemStateManager(ItemStateFactory isf, IdFactory idFactory) {
+    private final Collection eventFilter;
+
+    public WorkspaceItemStateManager(WorkspaceManager wspManager, ItemStateFactory isf, IdFactory idFactory) {
         super(isf, idFactory);
+        EventFilter filter = null;
+        try {
+            // todo for now listen to everything
+            filter = wspManager.createEventFilter(Event.ALL_TYPES, Path.ROOT, true, null, null, false);
+        } catch (UnsupportedRepositoryOperationException e) {
+            // repository does not support observation
+        }
+        this.eventFilter = filter == null ? Collections.EMPTY_LIST : Collections.singletonList(filter);
     }
 
     //-------------------------------< InternalEventListener >------------------
 
+    public Collection getEventFilters() {
+        return eventFilter;
+    }
+
     /**
      * Processes <code>events</code> and invalidates cached <code>ItemState</code>s
      * accordingly. Note that this performed for both local and non-local changes,
@@ -56,36 +74,6 @@
      */
     public void onEvent(EventBundle eventBundle) {
         pushEvents(getEventCollection(eventBundle));
-    }
-
-    /**
-     *
-     * @param eventBundle
-     * @param changeLog
-     */
-    public void onEvent(EventBundle eventBundle, ChangeLog changeLog) {
-        if (changeLog == null) {
-            throw new IllegalArgumentException("ChangeLog must not be null.");
-        }
-        Collection evs = getEventCollection(eventBundle);
-        // TODO: make sure, that events only contain events related to the modifications submitted with the changelog.
-
-        // inform the changelog target state about the transient modifications
-        // that have been persisted now: NEW-states will be connected to their
-        // overlayed state, EXISTING_REMOVED states will be definitely removed,
-        // EXISTING_MODIFIED states are merged with their workspace-state.
-        Set processedIds = changeLog.getTarget().refresh(changeLog);
-        for (Iterator it = evs.iterator(); it.hasNext();) {
-            ItemId evId = ((Event)it.next()).getItemId();
-            if (processedIds.contains(evId)) {
-                it.remove();
-            }
-        }
-
-        // all events not covered by the changelog must not be handled on the
-        // session-states -> treat the same way as events returned by
-        // workspace operations.
-        pushEvents(evs);
     }
 
     private void pushEvents(Collection events) {

Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/version/VersionManagerImpl.java Thu Nov  9 05:12:21 2006
@@ -19,7 +19,6 @@
 import org.apache.jackrabbit.jcr2spi.state.NodeState;
 import org.apache.jackrabbit.jcr2spi.state.PropertyState;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateException;
-import org.apache.jackrabbit.jcr2spi.state.ChangeLog;
 import org.apache.jackrabbit.jcr2spi.state.Status;
 import org.apache.jackrabbit.jcr2spi.observation.InternalEventListener;
 import org.apache.jackrabbit.jcr2spi.operation.Operation;
@@ -43,10 +42,12 @@
 import org.apache.jackrabbit.spi.EventIterator;
 import org.apache.jackrabbit.spi.Event;
 import org.apache.jackrabbit.spi.EventBundle;
+import org.apache.jackrabbit.spi.EventFilter;
 
 import java.util.Collection;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Collections;
 
 /**
  * <code>VersionManagerImpl</code>...
@@ -158,7 +159,14 @@
         NodeState wspState = getWorkspaceState(nodeState);
         // TODO find better solution to build the mergeFailed-collection
         final List failedIds = new ArrayList();
+        final EventFilter eventFilter = workspaceManager.createEventFilter(
+                Event.ALL_TYPES, nodeState.getQPath(), true, null, null, false);
         InternalEventListener mergeFailedCollector = new InternalEventListener() {
+
+            public Collection getEventFilters() {
+                return Collections.singletonList(eventFilter);
+            }
+
             public void onEvent(EventBundle eventBundle) {
                 if (eventBundle.isLocal()) {
                     EventIterator it = eventBundle.getEvents();
@@ -169,9 +177,6 @@
                         }
                     }
                 }
-            }
-            public void onEvent(EventBundle eventBundle, ChangeLog changeLog) {
-                // nothing to do. we are not interested in transient modifications
             }
         };
 

Added: jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/EventFilter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/EventFilter.java?view=auto&rev=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/EventFilter.java (added)
+++ jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/EventFilter.java Thu Nov  9 05:12:21 2006
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.spi;
+
+import java.io.Serializable;
+
+/**
+ * An <code>EventFilter</code> is applied to the events as generated on the
+ * repository server. Event filter instances can be created with {@link
+ * RepositoryService#createEventFilter(int, org.apache.jackrabbit.name.Path,
+ * boolean, String[], org.apache.jackrabbit.name.QName[], boolean)
+ * RepositoryService.createEventFilter()}. Some repository implementation may
+ * also support event filters that are directly instanciated by the client.
+ */
+public interface EventFilter extends Serializable {
+
+    /**
+     * If an implementation returns <code>true</code> the <code>event</code>
+     * will be included in the event bundle returned by {@link
+     * RepositoryService#getEvents(SessionInfo, long, EventFilter[])}. A return
+     * value of <code>false</code> indicates that the client is not interested
+     * in the <code>event</code>.
+     *
+     * @param event   the event in question.
+     * @param isLocal flag indicating whether this is a local event.
+     * @return <code>true</code> if the event is accepted by the filter;
+     *         <code>false</code> otherwise.
+     */
+    public boolean accept(Event event, boolean isLocal);
+}

Propchange: jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/EventFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java (original)
+++ jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/RepositoryService.java Thu Nov  9 05:12:21 2006
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.spi;
 
 import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
 
 import javax.jcr.lock.LockException;
 import javax.jcr.version.VersionException;
@@ -212,7 +213,6 @@
      * Completes the this Batch or discard all the previous modifications.
      *
      * @param batch
-     * @return EventBundle
      * @throws PathNotFoundException
      * @throws ItemNotFoundException
      * @throws NoSuchNodeTypeException
@@ -224,7 +224,7 @@
      * @throws UnsupportedRepositoryOperationException
      * @throws RepositoryException
      */
-    public EventBundle[] submit(Batch batch) throws PathNotFoundException, ItemNotFoundException, NoSuchNodeTypeException, ValueFormatException, VersionException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
+    public void submit(Batch batch) throws PathNotFoundException, ItemNotFoundException, NoSuchNodeTypeException, ValueFormatException, VersionException, LockException, ConstraintViolationException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
 
     //-------------------------------------------------------------< Import >---
     /**
@@ -233,7 +233,6 @@
      * @param parentId
      * @param xmlStream
      * @param uuidBehaviour
-     * @return
      * @throws ItemExistsException
      * @throws PathNotFoundException
      * @throws VersionException
@@ -244,7 +243,7 @@
      * @throws RepositoryException
      * @see javax.jcr.Workspace#importXML(String, java.io.InputStream, int)
      */
-    public EventBundle[] importXml(SessionInfo sessionInfo, NodeId parentId, InputStream xmlStream, int uuidBehaviour) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
+    public void importXml(SessionInfo sessionInfo, NodeId parentId, InputStream xmlStream, int uuidBehaviour) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
 
     //---------------------------------------------------------< Copy, Move >---
     /**
@@ -262,7 +261,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Workspace#move(String, String)
      */
-    public EventBundle[] move(SessionInfo sessionInfo, NodeId srcNodeId, NodeId destParentNodeId, QName destName) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
+    public void move(SessionInfo sessionInfo, NodeId srcNodeId, NodeId destParentNodeId, QName destName) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -282,7 +281,7 @@
      * @see javax.jcr.Workspace#copy(String, String)
      * @see javax.jcr.Workspace#copy(String, String, String)
      */
-    public EventBundle[] copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, QName destName) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, UnsupportedRepositoryOperationException, RepositoryException;
+    public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, QName destName) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, UnsupportedRepositoryOperationException, RepositoryException;
 
     //------------------------------------------------------< Update, Clone >---
     /**
@@ -296,7 +295,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Node#update(String)
      */
-    public EventBundle[] update(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException;
+    public void update(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -316,7 +315,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Workspace#clone(String, String, String, boolean)
      */
-    public EventBundle[] clone(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, QName destName, boolean removeExisting) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, UnsupportedRepositoryOperationException, RepositoryException;
+    public void clone(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, QName destName, boolean removeExisting) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, UnsupportedRepositoryOperationException, RepositoryException;
 
     //------------------------------------------------------------< Locking >---
 
@@ -344,7 +343,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Node#lock(boolean, boolean)
      */
-    public EventBundle[] lock(SessionInfo sessionInfo, NodeId nodeId, boolean deep, boolean sessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException;
+    public void lock(SessionInfo sessionInfo, NodeId nodeId, boolean deep, boolean sessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException;
 
     /**
      * Explicit refresh of an existing lock. Existing locks should be refreshed
@@ -356,7 +355,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.lock.Lock#refresh()
      */
-    public EventBundle[] refreshLock(SessionInfo sessionInfo, NodeId nodeId) throws LockException, RepositoryException;
+    public void refreshLock(SessionInfo sessionInfo, NodeId nodeId) throws LockException, RepositoryException;
 
     /**
      * Releases the lock on the given node.<p/>
@@ -372,7 +371,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Node#unlock()
      */
-    public EventBundle[] unlock(SessionInfo sessionInfo, NodeId nodeId) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException;
+    public void unlock(SessionInfo sessionInfo, NodeId nodeId) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException;
 
     //---------------------------------------------------------< Versioning >---
     /**
@@ -385,7 +384,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Node#checkin()
      */
-    public EventBundle[] checkin(SessionInfo sessionInfo, NodeId nodeId) throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException;
+    public void checkin(SessionInfo sessionInfo, NodeId nodeId) throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -395,7 +394,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Node#checkout()
      */
-    public EventBundle[] checkout(SessionInfo sessionInfo, NodeId nodeId) throws UnsupportedRepositoryOperationException, LockException, RepositoryException;
+    public void checkout(SessionInfo sessionInfo, NodeId nodeId) throws UnsupportedRepositoryOperationException, LockException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -414,7 +413,7 @@
      * @see javax.jcr.Node#restore(javax.jcr.version.Version, String, boolean)
      * @see javax.jcr.Node#restoreByLabel(String, boolean)
      */
-    public EventBundle[] restore(SessionInfo sessionInfo, NodeId nodeId, NodeId versionId, boolean removeExisting) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException;
+    public void restore(SessionInfo sessionInfo, NodeId nodeId, NodeId versionId, boolean removeExisting) throws VersionException, PathNotFoundException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -428,13 +427,15 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Workspace#restore(javax.jcr.version.Version[], boolean)
      */
-    public EventBundle[] restore(SessionInfo sessionInfo, NodeId[] versionIds, boolean removeExisting) throws ItemExistsException, UnsupportedRepositoryOperationException, VersionException, LockException, InvalidItemStateException, RepositoryException;
+    public void restore(SessionInfo sessionInfo, NodeId[] versionIds, boolean removeExisting) throws ItemExistsException, UnsupportedRepositoryOperationException, VersionException, LockException, InvalidItemStateException, RepositoryException;
 
     /**
      * @param sessionInfo
      * @param nodeId
      * @param srcWorkspaceName
      * @param bestEffort
+     * @return an <code>IdIterator</code> over all nodes that received a merge
+     *         result of "fail" in the course of this operation.
      * @throws javax.jcr.NoSuchWorkspaceException
      * @throws javax.jcr.AccessDeniedException
      * @throws javax.jcr.MergeException
@@ -443,7 +444,7 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.Node#merge(String, boolean)
      */
-    public EventBundle[] merge(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException;
+    public IdIterator merge(SessionInfo sessionInfo, NodeId nodeId, String srcWorkspaceName, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -461,7 +462,7 @@
      * @see javax.jcr.Node#cancelMerge(javax.jcr.version.Version)
      * @see javax.jcr.Node#doneMerge(javax.jcr.version.Version)
      */
-    public EventBundle[] resolveMergeConflict(SessionInfo sessionInfo, NodeId nodeId, NodeId[] mergeFailedIds, NodeId[] predecessorIds) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException;
+    public void resolveMergeConflict(SessionInfo sessionInfo, NodeId nodeId, NodeId[] mergeFailedIds, NodeId[] predecessorIds) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException;
 
     /**
      * @param sessionInfo
@@ -472,18 +473,17 @@
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.version.VersionHistory#addVersionLabel(String, String, boolean)
      */
-    public EventBundle[] addVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId, QName label, boolean moveLabel) throws VersionException, RepositoryException;
+    public void addVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId, QName label, boolean moveLabel) throws VersionException, RepositoryException;
 
     /**
      * @param sessionInfo
      * @param versionId
      * @param label
-     * @return
      * @throws javax.jcr.version.VersionException
      * @throws javax.jcr.RepositoryException
      * @see javax.jcr.version.VersionHistory#removeVersionLabel(String)
      */
-    public EventBundle[] removeVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId, QName label) throws VersionException, RepositoryException;
+    public void removeVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, NodeId versionId, QName label) throws VersionException, RepositoryException;
 
     //----------------------------------------------------------< Searching >---
     /**
@@ -507,31 +507,76 @@
     //--------------------------------------------------------< Observation >---
 
     /**
+     * Creates an event filter based on the parameters available in {@link
+     * javax.jcr.observation.ObservationManager#addEventListener}.
+     *
+     * @param eventTypes   A combination of one or more event type constants
+     *                     encoded as a bitmask.
+     * @param absPath      an absolute path.
+     * @param isDeep       a <code>boolean</code>.
+     * @param uuid         array of UUIDs.
+     * @param nodeTypeName array of node type names.
+     * @param noLocal      a <code>boolean</code>.
+     * @return the event filter instance with the given parameters.
+     * @throws UnsupportedRepositoryOperationException
+     *          if this implementation does not support observation.
+     * @see javax.jcr.observation.ObservationManager#addEventListener(javax.jcr.observation.EventListener, int, String, boolean, String[], String[], boolean)
+     */
+    public EventFilter createEventFilter(int eventTypes,
+                                         Path absPath,
+                                         boolean isDeep,
+                                         String[] uuid,
+                                         QName[] nodeTypeName,
+                                         boolean noLocal)
+            throws UnsupportedRepositoryOperationException;
+
+    /**
      * Retrieves the external events that occurred since the last call to this
-     * method or any of the other methods of this interface that return {@link
-     * EventBundle}s (e.g. {@link RepositoryService#submit(Batch)}). When
-     * this method returns without an exception the bundle identfier in
-     * <code>sessionInfo</code> will be updated to reference the most recent
-     * event bundle returned by this call.
+     * method. When this method returns without an exception the bundle
+     * identfier in <code>sessionInfo</code> will be updated to reference the
+     * most recent event bundle returned by this call. In case an empty array is
+     * supplied as event filters it may also happen that the bundle identifier
+     * is updated even though no event bundle had been returned.
+     * <p/>
+     * An implementation is required to accept at least event filter instances
+     * created by {@link #createEventFilter}. Optionally an implementation may
+     * also support event filters instanciated by the client itself. An
+     * implementation may require special deployment in that case, e.g. to make
+     * the event filter implementation class available to the repository
+     * server.
      *
      * @param sessionInfo the session info.
      * @param timeout     a timeout in milliseconds to wait at most for an
      *                    external event bundle. If <code>timeout</code> is up
      *                    and no event occurred meanwhile an empty array is
      *                    returned.
+     * @param filters     the filters that are applied to the external events as
+     *                    they occurred on the repository. An event is included
+     *                    in an event bundle if it is {@link EventFilter#accept(Event)
+     *                    accept}ed by at least one of the supplied filters. If
+     *                    an empty array is passed none of the potential events
+     *                    are include in an event bundle. This allows a client
+     *                    to skip or ignore events for a certain period of time.
+     *                    If <code>null</code> is passed no filtering is done
+     *                    and all events are included in the event bundle.
      * @return an array of <code>EventBundle</code>s representing the external
      *         events that occurred.
-     * @throws RepositoryException if an error occurs while retrieving the event
-     *                             bundles or the currently set bundle
-     *                             identifier in <code>sessionInfo</code>
-     *                             references an unknown or outdated event
-     *                             bundle.
+     * @throws RepositoryException  if an error occurs while retrieving the
+     *                              event bundles or the currently set bundle
+     *                              identifier in <code>sessionInfo</code>
+     *                              references an unknown or outdated event
+     *                              bundle.
      * @throws UnsupportedRepositoryOperationException
-     *                             if this implementation does not support
-     *                             observation.
-     */
-    public EventBundle[] getEvents(SessionInfo sessionInfo, long timeout)
-            throws RepositoryException, UnsupportedRepositoryOperationException;
+     *                              if this implementation does not support
+     *                              observation.
+     * @throws InterruptedException if the calling thread is interrupted while
+     *                              waiting for events within the specified
+     *                              <code>timeout</code>.
+     */
+    public EventBundle[] getEvents(SessionInfo sessionInfo,
+                                   long timeout,
+                                   EventFilter[] filters)
+            throws RepositoryException, UnsupportedRepositoryOperationException, InterruptedException;
 
     //---------------------------------------------------------< Namespaces >---
     /**

Modified: jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/SessionInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/SessionInfo.java?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/SessionInfo.java (original)
+++ jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/SessionInfo.java Thu Nov  9 05:12:21 2006
@@ -57,7 +57,7 @@
     /**
      * Sets the identifier of the last {@link EventBundle} delivered using this
      * <code>SessionInfo</code>. This identifier will be used to retrieve the
-     * subsequent event bundles when calling {@link RepositoryService#getEvents(SessionInfo, long)}.
+     * subsequent event bundles when calling {@link RepositoryService#getEvents(SessionInfo, long, EventFilter[])}.
      *
      * @param eventBundleId the identifier of the last {@link EventBundle}
      *                      delivered using this <code>SessionInfo</code>.

Modified: jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/package.html
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/package.html?view=diff&rev=472888&r1=472887&r2=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/package.html (original)
+++ jackrabbit/trunk/contrib/spi/spi/src/main/java/org/apache/jackrabbit/spi/package.html Thu Nov  9 05:12:21 2006
@@ -45,19 +45,7 @@
 SPI. With only little thread synchronization overhead events can be acquired
 using a <code>timeout</code> of {@link java.lang.Long#MAX_VALUE}.
 <p/>
-In addition all methods on the RepositoryService that execute an operation on
-the repository return an array of <code>EventBundle</code>s. This array not
-only contains the bundle that is associated to the previously executed operation
-but also contains all preceeding event bundles not yet delivered since the last
-call to <code>RepositoryService.getEvents()</code> or any other method on the
-<code>RepositoryService</code> which returns <code>EventBundle</code>s. This
-design ensures a consistent sequence of event bundles delivered to the SPI
-client.
-<p/>
 If an SPI implementation does not support observation, the method
 <code>RepositoryService.getEvents()</code> will always throw an
-{@link javax.jcr.UnsupportedRepositoryOperationException} and all methods
-on <code>RepositoryService</code> which return an array of
-<code>EventBundle</code>s will only return one event bundle which is associated
-with the local operation that has just been executed.
+{@link javax.jcr.UnsupportedRepositoryOperationException}.
 </body>

Added: jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventFilterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventFilterImpl.java?view=auto&rev=472888
==============================================================================
--- jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventFilterImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventFilterImpl.java Thu Nov  9 05:12:21 2006
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.spi2dav;
+
+import org.apache.jackrabbit.spi.EventFilter;
+import org.apache.jackrabbit.spi.Event;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.PathNotFoundException;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+
+/**
+ * <code>EventFilterImpl</code> is the spi2dav implementation of an {@link
+ * EventFilter}.
+ */
+class EventFilterImpl implements EventFilter {
+
+    /**
+     * The logger instance for this class.
+     */
+    private static final Logger log = LoggerFactory.getLogger(EventFilterImpl.class);
+
+    private final int eventTypes;
+
+    private final boolean isDeep;
+
+    private final Path absPath;
+
+    private final Set uuids;
+
+    private final Set nodeTypeNames;
+
+    private final boolean noLocal;
+
+    /**
+     * Creates a new <code>EventFilterImpl</code>.
+     *
+     * @param eventTypes    the event types this filter is interested in.
+     * @param absPath       filter events that are below this path.
+     * @param isDeep        whether this filter is applied deep.
+     * @param uuids         the uuids of the nodes this filter allows.
+     * @param nodeTypeNames the QNames of the already resolved node types this
+     *                      filter allows.
+     * @param noLocal       whether this filter accepts local events or not.
+     */
+    EventFilterImpl(int eventTypes,
+                    Path absPath,
+                    boolean isDeep,
+                    String[] uuids,
+                    Set nodeTypeNames,
+                    boolean noLocal) {
+        this.eventTypes = eventTypes;
+        this.absPath = absPath;
+        this.isDeep = isDeep;
+        this.uuids = uuids != null ? new HashSet(Arrays.asList(uuids)) : null;
+        this.nodeTypeNames = nodeTypeNames != null ? new HashSet(nodeTypeNames) : null;
+        this.noLocal = noLocal;
+    }
+
+    public boolean accept(Event event, boolean isLocal) {
+        int type = event.getType();
+        // check type
+        if ((type & eventTypes) == 0) {
+            return false;
+        }
+
+        // check local flag
+        if (isLocal && noLocal) {
+            return false;
+        }
+
+        // check UUIDs
+        NodeId parentId = event.getParentId();
+        if (uuids != null) {
+            if (parentId.getPath() == null) {
+                if (!uuids.contains(parentId.getUUID())) {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+
+        // check node types
+        if (nodeTypeNames != null) {
+            Set eventTypes = new HashSet();
+            eventTypes.addAll(Arrays.asList(event.getMixinTypeNames()));
+            eventTypes.add(event.getPrimaryNodeTypeName());
+            // create intersection
+            eventTypes.retainAll(nodeTypeNames);
+            if (eventTypes.isEmpty()) {
+                return false;
+            }
+        }
+
+        // finally check path
+        try {
+            // the relevant path for the path filter depends on the event type
+            // for node events, the relevant path is the one returned by
+            // Event.getPath().
+            // for property events, the relevant path is the path of the
+            // node where the property belongs to.
+            Path eventPath;
+            if (type == Event.NODE_ADDED || type == Event.NODE_REMOVED) {
+                eventPath = event.getQPath();
+            } else {
+                eventPath = event.getQPath().getAncestor(1);
+            }
+
+            boolean match = eventPath.equals(absPath);
+            if (!match && isDeep) {
+                match = eventPath.isDescendantOf(absPath);
+            }
+            return match;
+        } catch (MalformedPathException e) {
+            // should never get here
+            log.warn("malformed path: " + e);
+            log.debug("Exception: ", e);
+        } catch (PathNotFoundException e) {
+            // should never get here
+            log.warn("invalid property path: " + e);
+            log.debug("Exception: ", e);
+        }
+        // if we get here an exception occurred while checking for the path
+        return false;
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/EventFilterImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native