Author: stefanegli
Date: Fri Nov 11 15:46:33 2016
New Revision: 1769310
URL: http://svn.apache.org/viewvc?rev=1769310&view=rev
Log:
OAK-5096 : when switching from AND to OR one must assure that the aggregated
subpaths are also added to the include paths - both to the prefilter and the
filter conditions - earlier in AND mode this was assured via the
NodeTypeAggregationFilter but now this is no longer used
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/ObservationManagerImpl.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=1769310&r1=1769309&r2=1769310&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
Fri Nov 11 15:46:33 2016
@@ -239,10 +239,12 @@ public class OakEventFilterImpl extends
private FilterBuilder builder;
- private Condition all;
-
private EventAggregator aggregator;
+ private boolean withNodeTypeAggregate;
+
+ private Set<String> relativeGlobPaths;
+
public OakEventFilterImpl(@Nonnull JackrabbitEventFilter delegate) {
checkNotNull(delegate);
this.delegate = delegate;
@@ -475,24 +477,46 @@ public class OakEventFilterImpl extends
return builder;
}
- public OakEventFilterImpl and(Condition... condition) {
- checkNotNull(condition);
- if (all == null) {
- all = builder().all(condition);
- } else {
- all = builder().all(all, builder.all(condition));
- }
- return this;
- }
-
public OakEventFilterImpl aggregator(EventAggregator aggregator) {
checkNotNull(aggregator);
this.aggregator = aggregator;
return this;
}
- public Condition getAnds() {
- return all;
+ public Condition getAdditionalIncludeConditions(Set<String> includePaths) {
+ if (!withNodeTypeAggregate) {
+ return null;
+ }
+ List<Condition> additionalIncludeConditions = new
LinkedList<Condition>();
+ // for nodeTypeAggregation in OR mode we must append
+ // the relativeGlobPaths to all includePaths
+ for (String includePath : includePaths) {
+ if (includePath.equals("**") || includePath.endsWith("/*") ||
includePath.endsWith("/**")) {
+ // this will include anything, so nothing to append in this
case
+ } else {
+ // otherwise append all the relativeGlobPaths, except ""
+ for (String relativeGlobPath : relativeGlobPaths) {
+ if (relativeGlobPath.equals("")) {
+ // this corresponds to 'SELF' which is already
included, so skip
+ continue;
+ } else {
+ String additionalGlobPath;
+ if (includePath.endsWith("/")) {
+ additionalGlobPath = includePath +
relativeGlobPath;
+ } else {
+ additionalGlobPath = includePath + "/" +
relativeGlobPath;
+ }
+
additionalIncludeConditions.add(builder().path(additionalGlobPath));
+
additionalIncludeConditions.add(builder().path(additionalGlobPath + "/*"));
+ }
+ }
+ }
+ }
+ if (additionalIncludeConditions.size() == 0) {
+ return null;
+ } else {
+ return builder().any(additionalIncludeConditions);
+ }
}
public EventAggregator getAggregator() {
@@ -501,6 +525,12 @@ public class OakEventFilterImpl extends
@Override
public OakEventFilter withNodeTypeAggregate(String[] nodeTypes, String[]
relativeGlobPaths) {
+ this.withNodeTypeAggregate = true;
+ if (this.relativeGlobPaths == null) {
+ this.relativeGlobPaths = new HashSet<String>();
+ }
+ this.relativeGlobPaths.addAll(Arrays.asList(relativeGlobPaths));
+
final Pattern[] relativePathPatterns = new
Pattern[relativeGlobPaths.length];
for (int i = 0; i < relativePathPatterns.length; i++) {
relativePathPatterns[i] =
Pattern.compile(GlobbingPathHelper.globPathAsRegex(relativeGlobPaths[i]));
@@ -527,6 +557,25 @@ public class OakEventFilterImpl extends
if (includeAncestorRemove) {
includePaths.clear();
includePaths.add("/");
+ } else if (withNodeTypeAggregate) {
+ // ensure that, for prefixing, all includePaths allow additional
+ // subpaths for the aggregation - this can be simplified
+ // to just allow anything (**) below there, as this is just
+ // about prefiltering, not actual (precise) filtering.
+ // so the goal is just to ensure nothing is erroneously excluded
+ // so more including is fine.
+ Set<String> originalPaths = new HashSet<String>(includePaths);
+ for (String includePath : originalPaths) {
+ if (includePath.equals("**") || includePath.endsWith("/*") ||
includePath.endsWith("/**")) {
+ // skip, this is fine
+ } else if (includePath.endsWith("/")) {
+ includePaths.remove(includePath);
+ includePaths.add(includePath + "**");
+ } else {
+ includePaths.remove(includePath);
+ includePaths.add(includePath + "/**");
+ }
+ }
}
}
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/ObservationManagerImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/ObservationManagerImpl.java?rev=1769310&r1=1769309&r2=1769310&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/ObservationManagerImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/observation/ObservationManagerImpl.java
Fri Nov 11 15:46:33 2016
@@ -285,9 +285,9 @@ public class ObservationManagerImpl impl
Selector nodeTypeSelector = Selectors.PARENT;
boolean deleteSubtree = true;
if (oakEventFilter != null) {
- Condition ands = oakEventFilter.getAnds();
- if (ands != null) {
- excludeConditions.add(ands);
+ Condition additionalIncludes =
oakEventFilter.getAdditionalIncludeConditions(includePaths);
+ if (additionalIncludes != null) {
+ includeConditions.add(additionalIncludes);
}
filterBuilder.aggregator(oakEventFilter.getAggregator());
if (oakEventFilter.getApplyNodeTypeOnSelf()) {
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=1769310&r1=1769309&r2=1769310&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
Fri Nov 11 15:46:33 2016
@@ -38,6 +38,7 @@ import static org.junit.Assert.assertFal
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assume.assumeTrue;
import java.util.Arrays;
@@ -52,20 +53,30 @@ import java.util.concurrent.ScheduledExe
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.ItemExistsException;
import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyType;
+import javax.jcr.ReferentialIntegrityException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.NodeTypeTemplate;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.jcr.observation.ObservationManager;
+import javax.jcr.version.VersionException;
import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ForwardingListenableFuture;
@@ -1756,7 +1767,7 @@ public class ObservationTest extends Abs
}
/**
- * OAK -5096 : new test case for OR mode
+ * OAK-5096 : new test case for OR mode
*/
@Test
public void testAggregate5() throws Exception {
@@ -1793,6 +1804,108 @@ public class ObservationTest extends Abs
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
assertTrue("Missing events: " + missing, missing.isEmpty());
}
+
+ @Test
+ public void testFileWithGlobs() throws Exception {
+ doTestFileWithGlobs("/parent/bar/zet.jsp", "/parent/bar/zet.jsp");
+ doTestFileWithGlobs("/parent/bar/*.jsp", "/parent/bar");
+ doTestFileWithGlobs("/parent/*/zet.jsp", "/parent");
+ doTestFileWithGlobs("/parent/*/*.jsp", "/parent");
+ doTestFileWithGlobs("/parent/**/zet.jsp", "/parent");
+ doTestFileWithGlobs("/parent/**/*.jsp", "/parent");
+ doTestFileWithGlobs("/*/bar/*.jsp", "");
+ doTestFileWithGlobs("/**/bar/*.jsp", "");
+ doTestFileWithGlobs("/**/*.jsp", "");
+ doTestFileWithGlobs("**/*.jsp", "");
+ }
+
+ private void doTestFileWithGlobs(String globPath, String...
expectedSubTrees)
+ throws RepositoryException, ItemExistsException,
PathNotFoundException, NoSuchNodeTypeException,
+ LockException, VersionException, ConstraintViolationException,
AccessDeniedException,
+ ReferentialIntegrityException, InvalidItemStateException,
InterruptedException, ExecutionException {
+ assumeTrue(observationManager instanceof ObservationManagerImpl);
+ ObservationManagerImpl oManager = (ObservationManagerImpl)
observationManager;
+ ExpectationListener listener = new ExpectationListener();
+ JackrabbitEventFilter filter = new JackrabbitEventFilter();
+ filter.setEventTypes(ALL_EVENTS);
+ filter = FilterFactory.wrap(filter)
+ .withIncludeGlobPaths(globPath);
+ oManager.addEventListener(listener, filter);
+ ChangeProcessor cp = oManager.getChangeProcessor(listener);
+ assertNotNull(cp);
+ FilterProvider filterProvider = cp.getFilterProvider();
+ assertNotNull(filterProvider);
+ assertArrayEquals(expectedSubTrees,
Iterables.toArray(filterProvider.getSubTrees(), String.class));
+
+ Node parent = getAdminSession().getRootNode().addNode("parent",
"nt:unstructured");
+ Node bar = parent.addNode("bar", "nt:unstructured");
+ Node zetDotJsp = bar.addNode("zet.jsp", "nt:unstructured");
+ listener.expect(zetDotJsp.getPath() + "/jcr:primaryType",
PROPERTY_ADDED);
+
+ parent.getSession().save();
+
+ Thread.sleep(1000);
+ List<Expectation> missing = listener.getMissing(TIME_OUT,
TimeUnit.SECONDS);
+ List<Event> unexpected = listener.getUnexpected();
+ assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
+ assertTrue("Missing events: " + missing, missing.isEmpty());
+
+ Session session = getAdminSession();
+ session.getRootNode().getNode("parent").remove();
+ session.save();
+ oManager.removeEventListener(listener);
+ }
+
+ // OAK-5096 : a specific **/*.jsp test case
+ @Test
+ public void testAggregate6() throws Exception {
+ assumeTrue(observationManager instanceof ObservationManagerImpl);
+ ObservationManagerImpl oManager = (ObservationManagerImpl)
observationManager;
+ ExpectationListener listener = new ExpectationListener();
+ JackrabbitEventFilter filter = new JackrabbitEventFilter();
+ filter.setEventTypes(ALL_EVENTS);
+ filter = FilterFactory.wrap(filter)
+ .withNodeTypeAggregate(new String[] { "oak:Unstructured" },
new String[] { "", "jcr:content" } )
+ .withIncludeGlobPaths("**/*.jsp");
+ oManager.addEventListener(listener, filter);
+ ChangeProcessor cp = oManager.getChangeProcessor(listener);
+ assertNotNull(cp);
+ FilterProvider filterProvider = cp.getFilterProvider();
+ assertNotNull(filterProvider);
+
+ Node parent = getAdminSession().getRootNode().addNode("parent",
"nt:unstructured");
+ Node bar = parent.addNode("bar", "nt:unstructured");
+ Node zetDotJsp = bar.addNode("zet.jsp", "nt:unstructured");
+ listener.expect(zetDotJsp.getPath() + "/jcr:primaryType",
PROPERTY_ADDED);
+ Node c = bar.addNode("c", "nt:unstructured");
+ Node fooDotJsp = c.addNode("foo.jsp", "oak:Unstructured");
+ listener.expect(fooDotJsp.getPath() + "/jcr:primaryType",
PROPERTY_ADDED);
+ Node jcrContent = fooDotJsp.addNode("jcr:content", "nt:unstructured");
+ jcrContent.setProperty("jcr:data", "foo");
+ listener.expectAdd(jcrContent);
+ listener.expect(jcrContent.getPath() + "/jcr:data",
"/parent/bar/c/foo.jsp", PROPERTY_ADDED);
+
+ parent.getSession().save();
+
+ Thread.sleep(1000);
+ List<Expectation> missing = listener.getMissing(TIME_OUT,
TimeUnit.SECONDS);
+ List<Event> unexpected = listener.getUnexpected();
+ assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
+ assertTrue("Missing events: " + missing, missing.isEmpty());
+
+ // OAK-5096 : this is what OAK-5096 is all about: when you change
+ // the property jcr:content/jcr:data it should be reported with
+ // identifier of the aggregate - even though it is not in the original
glob path
+ jcrContent.setProperty("jcr:data", "bar");
+ listener.expect(jcrContent.getPath() + "/jcr:data",
"/parent/bar/c/foo.jsp", PROPERTY_CHANGED);
+ parent.getSession().save();
+
+ Thread.sleep(1000);
+ missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
+ unexpected = listener.getUnexpected();
+ assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
+ assertTrue("Missing events: " + missing, missing.isEmpty());
+ }
private void assertMatches(Iterable<String> actuals, String... expected) {
assertEquals(newHashSet(expected), newHashSet(actuals));