You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2012/06/26 18:03:08 UTC

svn commit: r1354078 - in /jackrabbit/oak/trunk/oak-jcr/src: main/java/org/apache/jackrabbit/oak/jcr/ObservationManagerImpl.java test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java

Author: mduerig
Date: Tue Jun 26 16:03:07 2012
New Revision: 1354078

URL: http://svn.apache.org/viewvc?rev=1354078&view=rev
Log:
OAK-144: Implement observation
- implement jcr events (WIP)

Modified:
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ObservationManagerImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ObservationManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ObservationManagerImpl.java?rev=1354078&r1=1354077&r2=1354078&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ObservationManagerImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ObservationManagerImpl.java Tue Jun 26 16:03:07 2012
@@ -16,7 +16,9 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
@@ -30,13 +32,21 @@ import javax.jcr.observation.EventListen
 import javax.jcr.observation.EventListenerIterator;
 import javax.jcr.observation.ObservationManager;
 
+import org.apache.jackrabbit.commons.iterator.EventIteratorAdapter;
 import org.apache.jackrabbit.commons.iterator.EventListenerIteratorAdapter;
 import org.apache.jackrabbit.oak.api.ChangeExtractor;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.jcr.util.LazyValue;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.util.Function1;
+import org.apache.jackrabbit.oak.util.Iterators;
+
+import static org.apache.jackrabbit.commons.iterator.LazyIteratorChain.chain;
+import static org.apache.jackrabbit.oak.util.Iterators.singleton;
 
 public class ObservationManagerImpl implements ObservationManager {
     private final SessionDelegate sessionDelegate;
@@ -66,7 +76,8 @@ public class ObservationManagerImpl impl
         if (processor == null) {
             ChangeExtractor extractor = sessionDelegate.getChangeExtractor();
             ChangeFilter filter = new ChangeFilter(eventTypes, absPath, isDeep, uuid, nodeTypeName, noLocal);
-            ChangeProcessor changeProcessor = new ChangeProcessor(extractor, listener, filter);
+            ChangeProcessor changeProcessor = new ChangeProcessor(sessionDelegate.getNamePathMapper(), extractor,
+                    listener, filter);
             processors.put(listener, changeProcessor);
             timer.get().schedule(changeProcessor, 0, 1000);
         }
@@ -93,7 +104,7 @@ public class ObservationManagerImpl impl
 
     @Override
     public void setUserData(String userData) throws RepositoryException {
-        throw new UnsupportedRepositoryOperationException();
+        throw new UnsupportedRepositoryOperationException("User data not supported");
     }
 
     @Override
@@ -110,13 +121,16 @@ public class ObservationManagerImpl impl
     //------------------------------------------------------------< private >---
 
     private static class ChangeProcessor extends TimerTask {
+        private final NamePathMapper namePathMapper;
         private final ChangeExtractor changeExtractor;
         private final EventListener listener;
         private final AtomicReference<ChangeFilter> filterRef;
 
         private volatile boolean stopped;
 
-        public ChangeProcessor(ChangeExtractor changeExtractor, EventListener listener, ChangeFilter filter) {
+        public ChangeProcessor(NamePathMapper namePathMapper, ChangeExtractor changeExtractor, EventListener listener,
+                ChangeFilter filter) {
+            this.namePathMapper = namePathMapper;
             this.changeExtractor = changeExtractor;
             this.listener = listener;
             filterRef = new AtomicReference<ChangeFilter>(filter);
@@ -144,46 +158,46 @@ public class ObservationManagerImpl impl
             }
 
             public EventGeneratingNodeStateDiff() {
-                this("");
+                this("/");
             }
 
             @Override
             public void propertyAdded(PropertyState after) {
                 if (!stopped && filterRef.get().include(Event.PROPERTY_ADDED, path, after)) {
-                    // todo implement propertyAdded
-//                    listener.onEvent(null);
+                    Event event = generatePropertyEvent(Event.PROPERTY_ADDED, path, after);
+                    listener.onEvent(new EventIteratorAdapter(Collections.singleton(event)));
                 }
             }
 
             @Override
             public void propertyChanged(PropertyState before, PropertyState after) {
                 if (!stopped && filterRef.get().include(Event.PROPERTY_CHANGED, path, before)) {
-                    // todo implement propertyChanged
-//                    listener.onEvent(null);
+                    Event event = generatePropertyEvent(Event.PROPERTY_CHANGED, path, after);
+                    listener.onEvent(new EventIteratorAdapter(Collections.singleton(event)));
                 }
             }
 
             @Override
             public void propertyDeleted(PropertyState before) {
                 if (!stopped && filterRef.get().include(Event.PROPERTY_REMOVED, path, before)) {
-                    // todo implement propertyDeleted
-//                    listener.onEvent(null);
+                    Event event = generatePropertyEvent(Event.PROPERTY_REMOVED, path, before);
+                    listener.onEvent(new EventIteratorAdapter(Collections.singleton(event)));
                 }
             }
 
             @Override
             public void childNodeAdded(String name, NodeState after) {
                 if (!stopped && filterRef.get().include(Event.NODE_ADDED, path, after)) {
-                    // todo implement childNodeAdded
-//                    listener.onEvent(null);
+                    Iterator<Event> events = generateNodeEvents(Event.NODE_ADDED, path, name, after);
+                    listener.onEvent(new EventIteratorAdapter(events));
                 }
             }
 
             @Override
             public void childNodeDeleted(String name, NodeState before) {
                 if (!stopped && filterRef.get().include(Event.NODE_REMOVED, path, before)) {
-                    // todo implement childNodeDeleted
-//                    listener.onEvent(null);
+                    Iterator<Event> events = generateNodeEvents(Event.NODE_REMOVED, path, name, before);
+                    listener.onEvent(new EventIteratorAdapter(events));
                 }
             }
 
@@ -195,6 +209,43 @@ public class ObservationManagerImpl impl
                 }
             }
         }
+
+        private Event generatePropertyEvent(int eventType, String path, PropertyState property) {
+            String jcrPath = namePathMapper.getJcrPath(PathUtils.concat(path, property.getName()));
+
+            // TODO support userId, identifier, info, date
+            return new EventImpl(eventType, jcrPath, null, null, null, 0);
+        }
+
+        private Iterator<Event> generateNodeEvents(final int eventType, String path, String name, NodeState node) {
+            final String jcrPath = namePathMapper.getJcrPath(PathUtils.concat(path, name));
+
+            Iterator<Event> propertyEvents = Iterators.map(node.getProperties().iterator(),
+                    new Function1<PropertyState, Event>() {
+                int propertyEventType = eventType == Event.NODE_ADDED ? Event.PROPERTY_ADDED : Event.PROPERTY_REMOVED;
+                @Override
+                public Event apply(PropertyState property) {
+                    return generatePropertyEvent(propertyEventType, jcrPath, property);
+                }
+            });
+
+            // TODO support userId, identifier, info, date
+            final Event nodeEvent = new EventImpl(eventType, jcrPath, null, null, null, 0);
+            Iterator<Event> events = Iterators.chain(singleton(nodeEvent), propertyEvents);
+            return chain(events, chain(generateChildEvents(eventType, path, name, node)));
+        }
+
+        private Iterator<Iterator<Event>> generateChildEvents(final int eventType, final String path, final String name,
+                NodeState node) {
+            return Iterators.map(node.getChildNodeEntries().iterator(),
+                    new Function1<ChildNodeEntry, Iterator<Event>>() {
+                @Override
+                public Iterator<Event> apply(ChildNodeEntry entry) {
+                    return generateNodeEvents(eventType, path, name, entry.getNodeState());
+                }
+            });
+        }
+
     }
 
     private static class ChangeFilter {
@@ -215,4 +266,98 @@ public class ObservationManagerImpl impl
             return true; // todo implement includeChildren
         }
     }
+
+    private static class EventImpl implements Event {
+        private final int type;
+        private final String path;
+        private final String userID;
+        private final String identifier;
+        private final Map<?, ?> info;
+        private final long date;
+
+        EventImpl(int type, String path, String userID, String identifier, Map<?, ?> info, long date) {
+            this.type = type;
+            this.path = path;
+            this.userID = userID;
+            this.identifier = identifier;
+            this.info = info;
+            this.date = date;
+        }
+
+        @Override
+        public int getType() {
+            return type;
+        }
+
+        @Override
+        public String getPath() throws RepositoryException {
+            return path;
+        }
+
+        @Override
+        public String getUserID() {
+            return userID;
+        }
+
+        @Override
+        public String getIdentifier() throws RepositoryException {
+            return identifier;
+        }
+
+        @Override
+        public Map<?, ?> getInfo() throws RepositoryException {
+            return info;
+        }
+
+        @Override
+        public String getUserData() throws RepositoryException {
+            throw new UnsupportedRepositoryOperationException("User data not supported");
+        }
+
+        @Override
+        public long getDate() throws RepositoryException {
+            return date;
+        }
+
+        @Override
+        public final boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (other == null || getClass() != other.getClass()) {
+                return false;
+            }
+
+            EventImpl that = (EventImpl) other;
+            return date == that.date && type == that.type &&
+                    (identifier == null ? that.identifier == null : identifier.equals(that.identifier)) &&
+                    (info == null ? that.info == null : info.equals(that.info)) &&
+                    (path == null ? that.path == null : path.equals(that.path)) &&
+                    (userID == null ? that.userID == null : userID.equals(that.userID));
+
+        }
+
+        @Override
+        public final int hashCode() {
+            int result = type;
+            result = 31 * result + (path == null ? 0 : path.hashCode());
+            result = 31 * result + (userID == null ? 0 : userID.hashCode());
+            result = 31 * result + (identifier == null ? 0 : identifier.hashCode());
+            result = 31 * result + (info == null ? 0 : info.hashCode());
+            result = 31 * result + (int) (date ^ (date >>> 32));
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "EventImpl{" +
+                    "type=" + type +
+                    ", path='" + path + '\'' +
+                    ", userID='" + userID + '\'' +
+                    ", identifier='" + identifier + '\'' +
+                    ", info=" + info +
+                    ", date=" + date +
+                    '}';
+        }
+    }
 }

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java?rev=1354078&r1=1354077&r2=1354078&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java Tue Jun 26 16:03:07 2012
@@ -1477,12 +1477,12 @@ public class RepositoryTest extends Abst
                 TEST_PATH + "/1/prop1");
 
         final Set<String> removeProperties = toSet(
-                TEST_PATH + "/1/prop2");
+                TEST_PATH + "/1/prop2",
+                TEST_PATH + "/2/jcr:primaryType");
 
         final List<Event> failedEvents = new ArrayList<Event>();
 
         ObservationManager obsMgr = getSession().getWorkspace().getObservationManager();
-        obsMgr.setUserData("my user data");
         obsMgr.addEventListener(new EventListener() {
                 @Override
                 public void onEvent(EventIterator events) {
@@ -1536,6 +1536,13 @@ public class RepositoryTest extends Abst
         n.addNode("2");
         getSession().save();
 
+        try {
+            Thread.sleep(1000);
+        }
+        catch (InterruptedException e) {
+            // FIXME wait rather than sleep
+        }
+
         n.setProperty("property", 42);
         n.addNode("3").setProperty("prop3", "val3");
         n1.setProperty("prop1", "val1 new");
@@ -1543,12 +1550,19 @@ public class RepositoryTest extends Abst
         n.getNode("2").remove();
         getSession().save();
 
-        assertTrue(failedEvents.isEmpty());
-        assertTrue(addNodes.isEmpty());
-        assertTrue(removeNodes.isEmpty());
-        assertTrue(addProperties.isEmpty());
-        assertTrue(removeProperties.isEmpty());
-        assertTrue(setProperties.isEmpty());
+        try {
+            Thread.sleep(1000);
+        }
+        catch (InterruptedException e) {
+            // FIXME wait rather than sleep
+        }
+
+        assertTrue("failedEvents not empty: " + failedEvents, failedEvents.isEmpty());
+        assertTrue("addNodes not empty: " + addNodes, addNodes.isEmpty());
+        assertTrue("removeNodes not empty: " + removeNodes, removeNodes.isEmpty());
+        assertTrue("addProperties not empty: " + addProperties, addProperties.isEmpty());
+        assertTrue("removeProperties not empty: " + removeProperties, removeProperties.isEmpty());
+        assertTrue("setProperties not empty: " + setProperties, setProperties.isEmpty());
     }
 
     @Ignore // TODO implement observation