Author: stefanegli
Date: Mon Nov 14 16:42:58 2016
New Revision: 1769658
URL: http://svn.apache.org/viewvc?rev=1769658&view=rev
Log:
OAK-5105 : adding support for globs and withIncludeAncestorsRemove : besides
the already added known-parent filters, for globs the actual path is added
without the deleteSubtree filter to catch the actual deletion of anything
matching
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/OakEventFilterImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/filter/OakEventFilter.java
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/observation/ObservationTest.java
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/OakEventFilterImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/OakEventFilterImpl.java?rev=1769658&r1=1769657&r2=1769658&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/OakEventFilterImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/OakEventFilterImpl.java
Mon Nov 14 16:42:58 2016
@@ -433,14 +433,25 @@ public class OakEventFilterImpl extends
}
List<Condition> ancestorsIncludeConditions = new
LinkedList<Condition>();
for (String aParentPath : parentPaths) {
- ancestorsIncludeConditions.add(filterBuilder.path(aParentPath));
+ ancestorsIncludeConditions.add(
+ filterBuilder.all(
+ filterBuilder.path(aParentPath),
+ filterBuilder.deleteSubtree()));
+ }
+ if (globPaths != null) {
+ for (String globPath : globPaths) {
+ if (globPath.contains("**") || globPath.contains("/*/")) {
+
ancestorsIncludeConditions.add(filterBuilder.path(globPath));
+ // unlike the known parent case above, this variant
doesn't filter out deleteSubtrees
+ // that way it will report the actual file deleted
+ }
+ }
}
return filterBuilder.any(
mainCondition,
filterBuilder.all(
filterBuilder.eventType(NODE_REMOVED),
filterBuilder.any(ancestorsIncludeConditions),
- filterBuilder.deleteSubtree(),
filterBuilder.accessControl(permissionProviderFactory)
)
);
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/filter/OakEventFilter.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/filter/OakEventFilter.java?rev=1769658&r1=1769657&r2=1769658&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/filter/OakEventFilter.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/filter/OakEventFilter.java
Mon Nov 14 16:42:58 2016
@@ -58,6 +58,11 @@ public abstract class OakEventFilter ext
* filters on /a/b/c, on /a/b and on /a</li>
* <li>include path /a/b/** results in additional !deep NODE_REMOVED
* filter on /a</li>
+ * <li>additionally for paths with globs (eg /a/b/**{@code /}*.jsp)
+ * it adds a deep NODE_REMOVED filter explicitly for that path
+ * using the same method as withIncludeSubtreeOnRemove does, but only
+ * limited to said path. So in this case you get a NODE_REMOVED
+ * for all *.jsp that are deleted in a subtree individually</li>
* </ul>
* <p>
* Note that unlike 'normal' include and exclude paths, this variant
Modified:
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/observation/ObservationTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/observation/ObservationTest.java?rev=1769658&r1=1769657&r2=1769658&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/observation/ObservationTest.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/observation/ObservationTest.java
Mon Nov 14 16:42:58 2016
@@ -52,6 +52,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.jcr.AccessDeniedException;
import javax.jcr.InvalidItemStateException;
@@ -1141,6 +1142,8 @@ public class ObservationTest extends Abs
private static class ExpectationListener implements EventListener {
private final Set<Expectation> expected = synchronizedSet(
Sets.<Expectation>newCopyOnWriteArraySet());
+ private final Set<Expectation> optional = synchronizedSet(
+ Sets.<Expectation>newCopyOnWriteArraySet());
private final List<Event> unexpected = synchronizedList(
Lists.<Event>newCopyOnWriteArrayList());
@@ -1153,6 +1156,14 @@ public class ObservationTest extends Abs
expected.add(expectation);
return expectation;
}
+
+ public Expectation optional(Expectation expectation) {
+ if (failed != null) {
+ expectation.fail(failed);
+ }
+ optional.add(expectation);
+ return expectation;
+ }
public Future<Event> expect(final String path, final int type) {
return expect(new Expectation("path = " + path + ", type = " +
type) {
@@ -1273,6 +1284,12 @@ public class ObservationTest extends Abs
exp.complete(event);
}
}
+ for (Expectation opt : optional) {
+ if (opt.isEnabled() && !opt.isComplete() &&
opt.onEvent(event)) {
+ found = true;
+ opt.complete(event);
+ }
+ }
if (!found) {
unexpected.add(event);
}
@@ -1337,6 +1354,138 @@ public class ObservationTest extends Abs
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
+
+ @Test
+ public void includeAncestorsRemove_WithGlobs() throws Exception {
+ OakEventFilter oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/a/b/c/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/a/b/*/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/a/*/*/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/*/b/*/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/*/b/c/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/*/*/c/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/*/*/*/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/a/**/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/**/c/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths("/**/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+
+ oef = FilterFactory.wrap(new JackrabbitEventFilter());
+ oef.setEventTypes(ALL_EVENTS);
+ oef.withIncludeGlobPaths(TEST_PATH + "/**/d.jsp");
+ oef.setIsDeep(true);
+ oef.withIncludeAncestorsRemove();
+ doIncludeAncestorsRemove_WithGlobs(oef);
+ }
+
+ void doIncludeAncestorsRemove_WithGlobs(OakEventFilter oef) throws
Exception {
+ Node testNode = getNode(TEST_PATH);
+
testNode.addNode("a").addNode("b").addNode("c").addNode("d.jsp").setProperty("e",
42);
+ testNode.getSession().save();
+
+ ObservationManagerImpl oManager = (ObservationManagerImpl)
observationManager;
+ final AtomicBoolean done = new AtomicBoolean(false);
+ final AtomicBoolean unexpected = new AtomicBoolean(false);
+ final AtomicBoolean failure = new AtomicBoolean(false);
+ EventListener listener = new EventListener() {
+
+ @Override
+ public void onEvent(EventIterator events) {
+ while(events.hasNext()) {
+ Event event = events.nextEvent();
+ System.out.println("got: "+event);
+ String path = "";
+ try {
+ path = event.getPath();
+ } catch (RepositoryException e) {
+ e.printStackTrace();
+ failure.set(true);
+ }
+ if (path.equals(TEST_PATH + "/a/b") && event.getType() ==
NODE_REMOVED) {
+ done.set(true);
+ } else if (path.equals(TEST_PATH + "/a/b/c/d.jsp") &&
event.getType() == NODE_REMOVED) {
+ done.set(true);
+ } else if (path.equals(TEST_PATH +
"/a/b/c/d.jsp/jcr:primaryType") && event.getType() == PROPERTY_REMOVED) {
+ done.set(true);
+ } else {
+ System.out.println("Unexpected event: "+event);
+ unexpected.set(true);
+ }
+ }
+ }
+ };
+ oManager.addEventListener(listener, oef);
+
+ Node b = testNode.getNode("a").getNode("b");
+ b.remove();
+ testNode.getSession().save();
+
+ Thread.sleep(1000);
+ assertTrue("didnt get either event", done.get());
+ assertFalse("did get unexpected events", unexpected.get());
+ assertFalse("got an exception", failure.get());
+
+ oManager.removeEventListener(listener);
+ testNode.getNode("a").remove();
+ testNode.getSession().save();
+ }
@Test
public void includeAncestorsRemove() throws Exception {
@@ -1356,6 +1505,7 @@ public class ObservationTest extends Abs
filterProvider = doIncludeAncestorsRemove(filter);
// with 'includeAncestorsRemove' flag the listener is registered at '/'
assertMatches(filterProvider.getSubTrees(), "/");
+
}
private FilterProvider doIncludeAncestorsRemove(JackrabbitEventFilter
filter) throws Exception {
@@ -1389,7 +1539,7 @@ public class ObservationTest extends Abs
@Override
public void onEvent(EventIterator events) {
while(events.hasNext()) {
- System.out.println("GOT: "+events.next());
+ System.out.println("/a-listener GOT: "+events.next());
}
}
@@ -1399,6 +1549,15 @@ public class ObservationTest extends Abs
testNode = getNode(TEST_PATH);
Node b = testNode.getNode("a").getNode("b");
listener.expect(b.getPath(), NODE_REMOVED);
+ listener.optional(new Expectation("/a/b/c is optionally sent depending
on filter") {
+ @Override
+ public boolean onEvent(Event event) throws Exception {
+ if (event.getPath().equals(TEST_PATH + "/a/b/c") &&
event.getType() == NODE_REMOVED) {
+ return true;
+ }
+ return false;
+ }
+ });
b.remove();
// but not the jcr:primaryType
testNode.getSession().save();