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