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();


Reply via email to