blob: 514258e2558e37355ae527a617d71a8b9e63c649 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.jackrabbit.oak.jcr.observation;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.base.Objects.equal;
import static java.util.Collections.synchronizedList;
import static java.util.Collections.synchronizedSet;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static javax.jcr.observation.Event.NODE_ADDED;
import static javax.jcr.observation.Event.NODE_MOVED;
import static javax.jcr.observation.Event.NODE_REMOVED;
import static javax.jcr.observation.Event.PERSIST;
import static javax.jcr.observation.Event.PROPERTY_ADDED;
import static javax.jcr.observation.Event.PROPERTY_CHANGED;
import static javax.jcr.observation.Event.PROPERTY_REMOVED;
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static org.junit.Assert.assertFalse;
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;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
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;
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.NodeDefinition;
import javax.jcr.nodetype.NodeDefinitionTemplate;
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;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import junitx.util.PrivateAccessor;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.api.JackrabbitNode;
import org.apache.jackrabbit.api.observation.JackrabbitEventFilter;
import org.apache.jackrabbit.api.observation.JackrabbitObservationManager;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
import org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest;
import org.apache.jackrabbit.oak.jcr.observation.filter.FilterFactory;
import org.apache.jackrabbit.oak.jcr.observation.filter.OakEventFilter;
import org.apache.jackrabbit.oak.plugins.observation.filter.ChangeSetFilterImpl;
import org.apache.jackrabbit.oak.plugins.observation.filter.FilterBuilder;
import org.apache.jackrabbit.oak.plugins.observation.filter.FilterProvider;
import org.apache.jackrabbit.oak.plugins.observation.filter.Selectors;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
// Don't run "Parallelized" as this causes tests to timeout in "weak" environments
public class ObservationTest extends AbstractRepositoryTest {
public static final int ALL_EVENTS = NODE_ADDED | NODE_REMOVED | NODE_MOVED | PROPERTY_ADDED |
PROPERTY_REMOVED | PROPERTY_CHANGED | PERSIST;
private static final String TEST_NODE = "test_node";
private static final String REFERENCEABLE_NODE = "\"referenceable\"";
private static final String TEST_PATH = '/' + TEST_NODE;
private static final String TEST_TYPE = "mix:test";
public static final int TIME_OUT = 60;
private Session observingSession;
private ObservationManager observationManager;
private String test_uuid;
public ObservationTest(NodeStoreFixture fixture) {
super(fixture);
}
@Before
public void setup() throws RepositoryException {
Session session = getAdminSession();
NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
NodeTypeTemplate mixTest = ntMgr.createNodeTypeTemplate();
mixTest.setName(TEST_TYPE);
mixTest.setMixin(true);
ntMgr.registerNodeType(mixTest, false);
Node n = session.getRootNode().addNode(TEST_NODE);
n.setProperty("test_property1", 42);
n.setProperty("test_property2", "forty_two");
n.addMixin(TEST_TYPE);
Node refNode = n.addNode(REFERENCEABLE_NODE);
refNode.addMixin(JcrConstants.MIX_REFERENCEABLE);
test_uuid = refNode.getProperty(JcrConstants.JCR_UUID).getString();
session.save();
observingSession = createAdminSession();
observationManager = observingSession.getWorkspace().getObservationManager();
}
@After
public void tearDown() {
if (observingSession != null) {
observingSession.logout();
}
}
@Test
public void observation() throws RepositoryException, ExecutionException, InterruptedException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
try {
Node n = getNode(TEST_PATH);
listener.expectAdd(n.setProperty("p0", "v0"));
Node n1 = listener.expectAdd(n.addNode("n1"));
listener.expectAdd(n1.setProperty("p1", "v1"));
listener.expectAdd(n1.setProperty("p2", "v2"));
listener.expectAdd(n.addNode("n2"));
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
listener.expectAdd(n.setProperty("property", 42));
Node n3 = listener.expectAdd(n.addNode("n3"));
listener.expectAdd(n3.setProperty("p3", "v3"));
listener.expectChange(n1.setProperty("p1", "v1.1"));
listener.expectRemove(n1.getProperty("p2")).remove();
listener.expectRemove(n.getNode("n2")).remove();
listener.expectAdd(n.addNode("{4}"));
getAdminSession().save();
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void infoMap() throws RepositoryException, ExecutionException, InterruptedException {
Node n = getNode(TEST_PATH);
Node n3 = n.addNode("n3");
n3.setProperty("p1", "q1");
n3.setProperty("p2", "q2");
getAdminSession().save();
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
try {
n.addNode("n1", "oak:Unstructured");
n.addNode("n2");
n.getNode("n2").addMixin(TEST_TYPE);
n3.setProperty("p1", "changed");
n3.setProperty("p2", (String) null);
listener.expect(new Expectation("infoMap for n1") {
@Override
public boolean onEvent(Event event) throws Exception {
if (event.getType() == NODE_ADDED && event.getPath().endsWith("n1")) {
Map<?, ?> info = event.getInfo();
return info != null &&
"oak:Unstructured".equals(info.get(JCR_PRIMARYTYPE));
} else {
return false;
}
}
});
listener.expect(new Expectation("infoMap for n1/jcr:primaryType") {
@Override
public boolean onEvent(Event event) throws Exception {
if (event.getType() == PROPERTY_ADDED &&
event.getPath().endsWith("n1/jcr:primaryType")) {
Map<?, ?> info = event.getInfo();
return info != null &&
"oak:Unstructured".equals(info.get(JCR_PRIMARYTYPE));
} else {
return false;
}
}
});
listener.expect(new Expectation("infoMap for n2") {
@Override
public boolean onEvent(Event event) throws Exception {
if (event.getType() == NODE_ADDED && event.getPath().endsWith("n2")) {
Map<?, ?> info = event.getInfo();
if (info == null) {
return false;
}
Object mixinTypes = info.get(JCR_MIXINTYPES);
if (!(mixinTypes instanceof String[])) {
return false;
}
Object primaryType = info.get(JCR_PRIMARYTYPE);
String[] mixins = (String[]) mixinTypes;
return NT_UNSTRUCTURED.equals(primaryType) &&
mixins.length == 1 &&
TEST_TYPE.equals(mixins[0]);
} else {
return false;
}
}
});
listener.expect(new Expectation("n2/jcr:primaryType") {
@Override
public boolean onEvent(Event event) throws Exception {
return event.getType() == PROPERTY_ADDED &&
event.getPath().endsWith("n2/jcr:primaryType");
}
});
listener.expect(new Expectation("n2/jcr:mixinTypes") {
@Override
public boolean onEvent(Event event) throws Exception {
return event.getType() == PROPERTY_ADDED &&
event.getPath().endsWith("n2/jcr:mixinTypes");
}
});
listener.expect(new Expectation("infoMap for n3/p1") {
@Override
public boolean onEvent(Event event) throws Exception {
if (event.getType() == PROPERTY_CHANGED &&
event.getPath().endsWith("n3/p1")) {
Map<?, ?> info = event.getInfo();
return info != null &&
NT_UNSTRUCTURED.equals(info.get(JCR_PRIMARYTYPE));
} else {
return false;
}
}
});
listener.expect(new Expectation("infoMap for n3/p2") {
@Override
public boolean onEvent(Event event) throws Exception {
if (event.getType() == PROPERTY_REMOVED &&
event.getPath().endsWith("n3/p2")) {
Map<?, ?> info = event.getInfo();
return info != null &&
NT_UNSTRUCTURED.equals(info.get(JCR_PRIMARYTYPE));
} else {
return false;
}
}
});
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void observation2() throws RepositoryException, InterruptedException, ExecutionException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
try {
Node n = getNode(TEST_PATH);
listener.expectAdd(n.addNode("n1"));
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
listener.expectAdd(n.addNode("n2"));
listener.expectRemove(n.getNode("n1")).remove();
getAdminSession().save();
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void typeFilter() throws RepositoryException, InterruptedException, ExecutionException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null,
new String[]{TEST_TYPE}, false);
try {
Node n = getNode(TEST_PATH);
Property p = n.setProperty("p", "v");
listener.expectAdd(p);
Node n1 = n.addNode("n1");
listener.expect(n1.getPath(), NODE_ADDED);
n1.addNode("n2");
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
listener.expectChange(p).setValue("v2");
getAdminSession().save();
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
listener.expectRemove(p).remove();
getAdminSession().save();
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void uuidFilter() throws RepositoryException, InterruptedException, ExecutionException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true,
new String[]{test_uuid}, null, false);
try {
Node nonRefNode = getNode(TEST_PATH);
Node refNode = nonRefNode.getNode(REFERENCEABLE_NODE);
nonRefNode.addNode("n");
listener.expect(refNode.addNode("r").getPath(), NODE_ADDED);
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void identifier() throws RepositoryException, InterruptedException, ExecutionException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, NODE_ADDED, TEST_PATH, true, null, null, false);
try {
Node n = getNode(TEST_PATH);
listener.expect(new Expectation("Has correct id") {
@Override
public boolean onEvent(Event event) throws Exception {
return (TEST_PATH + "/newNode").equals(event.getIdentifier());
}
});
n.addNode("newNode");
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void observationOnRootNode() throws Exception {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, PROPERTY_ADDED, "/", true, null, null, false);
try {
// add property to root node
Node root = getNode("/");
listener.expectAdd(root.setProperty("prop", "value"));
root.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void propertyFilter() throws Exception {
Node root = getNode("/");
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, PROPERTY_ADDED, "/a/b", false, null, null, false);
Node a = root.addNode("a");
Node b = a.addNode("b");
listener.expect("/a/b/jcr:primaryType", PROPERTY_ADDED);
listener.expectAdd(b.setProperty("propName", 1));
root.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void pathFilter() throws Exception {
final String path = "/events/only/here";
for (boolean deep : new boolean[]{false, true}) {
Node root = getNode("/");
if (root.hasNode("events")) {
root.getNode("events").remove();
root.getSession().save();
}
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, NODE_ADDED, path, deep, null, null, false);
try {
root.addNode("events").addNode("only").addNode("here").addNode("below").addNode("this");
listener.expect("/events/only/here/below", NODE_ADDED);
if (deep) {
listener.expect("/events/only/here/below/this", NODE_ADDED);
}
root.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
}
@Test
public void pathFilterWithTrailingSlash() throws Exception {
final String path = "/events/only/here";
ExpectationListener listener = new ExpectationListener();
listener.expect(new Expectation(path){
@Override
public boolean onEvent(Event event) throws Exception {
return PathUtils.isAncestor(path, event.getPath());
}
});
observationManager.addEventListener(listener, NODE_ADDED, path + '/', true, null, null, false);
try {
Node root = getNode("/");
root.addNode("events").addNode("only").addNode("here").addNode("at");
root.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void observationDispose()
throws RepositoryException, InterruptedException, ExecutionException, TimeoutException {
final ExpectationListener listener = new ExpectationListener();
Expectation hasEvents = listener.expect(
new Expectation("has events after registering"));
final Expectation noEvents = listener.expect(
new Expectation("has no more events after unregistering", false));
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
final Session s = getAdminSession();
// Generate events
ScheduledExecutorService service = newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(new Runnable() {
private int c;
@Override
public void run() {
try {
s.getNode(TEST_PATH)
.addNode("c" + c++)
.getSession()
.save();
}
catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
}, 10, 10, TimeUnit.MILLISECONDS);
// Make sure we see the events
assertNotNull(hasEvents.get(TIME_OUT, TimeUnit.SECONDS));
// Remove event listener
Executors.newSingleThreadExecutor().submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
observationManager.removeEventListener(listener);
noEvents.enable(true);
return null;
}
}).get(10, TimeUnit.SECONDS);
// Make sure we see no more events
assertFalse(noEvents.wait(4, TimeUnit.SECONDS));
service.shutdown();
service.awaitTermination(10, TimeUnit.SECONDS);
}
@Test
public void observationDisposeFromListener()
throws RepositoryException, InterruptedException, ExecutionException, TimeoutException {
final ExpectationListener listener = new ExpectationListener();
Expectation unregistered = listener.expect(new Expectation
("Unregistering listener from event handler should not block") {
@Override
public boolean onEvent(Event event) throws Exception {
observationManager.removeEventListener(listener);
return true;
}
});
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
// Ensure the listener is there
assertTrue(observationManager.getRegisteredEventListeners().hasNext());
// Generate events
Node n = getNode(TEST_PATH);
n.addNode("c");
n.getSession().save();
// Make sure we see the events and the listener is gone
assertNotNull(unregistered.get(TIME_OUT, TimeUnit.SECONDS));
assertFalse(observationManager.getRegisteredEventListeners().hasNext());
}
@Test
public void testMove() throws RepositoryException, ExecutionException, InterruptedException {
Node testNode = getNode(TEST_PATH);
Session session = testNode.getSession();
Node nodeA = testNode.addNode("a");
Node nodeAA = nodeA.addNode("aa");
Node nodeT = testNode.addNode("t");
Node nodeS = testNode.addNode("s");
session.save();
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, NODE_MOVED, "/", true, null, null, false);
String src1 = nodeA.getPath();
String dst1 = nodeT.getPath() + "/b";
session.move(src1, dst1);
listener.expectMove(src1, dst1);
String src2 = nodeT.getPath() + "/b/aa";
String dst2 = nodeS.getPath() + "/bb";
session.move(src2, dst2);
listener.expectMove(src1 + "/aa", dst2);
session.save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void testRename() throws RepositoryException, ExecutionException, InterruptedException {
Node testNode = getNode(TEST_PATH);
Session session = testNode.getSession();
Node nodeA = testNode.addNode("a");
String parentPath = testNode.getPath();
session.save();
assumeTrue(nodeA instanceof JackrabbitNode);
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, NODE_MOVED, "/", true, null, null, false);
((JackrabbitNode) nodeA).rename("b");
listener.expectMove(parentPath + "/a", parentPath + "/b");
session.save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void testReorder() throws RepositoryException, InterruptedException, ExecutionException {
Node testNode = getNode(TEST_PATH);
Node nodeA = testNode.addNode("a", "nt:unstructured");
Node nodeB = testNode.addNode("b", "nt:unstructured");
testNode.getSession().save();
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, NODE_MOVED, "/", true, null, null, false);
listener.expect(new Expectation("orderBefore") {
@Override
public boolean onEvent(Event event) throws Exception {
if (event.getType() != NODE_MOVED || event.getInfo() == null) {
return false;
}
Map<?, ?> info = event.getInfo();
if (PathUtils.concat(TEST_PATH, "a").equals(event.getPath())) {
return "a".equals(info.get("srcChildRelPath")) &&
"b".equals(info.get("destChildRelPath"));
} else if (PathUtils.concat(TEST_PATH, "b").equals(event.getPath())) {
return "b".equals(info.get("srcChildRelPath")) &&
"a".equals(info.get("destChildRelPath"));
} else {
return false;
}
}
});
testNode.orderBefore(nodeA.getName(), null);
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void addSubtree() throws RepositoryException, ExecutionException, InterruptedException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
Node n = getNode(TEST_PATH);
Node a = listener.expectAdd(n.addNode("a"));
Node b = listener.expectAdd(a.addNode("b"));
listener.expectAdd(b.addNode("c"));
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void removeSubtree() throws RepositoryException, ExecutionException, InterruptedException {
Node n = getNode(TEST_PATH);
n.addNode("a").addNode("b").addNode("c");
getAdminSession().save();
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
listener.expectRemove(n.getNode("a")).remove();
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void moveSubtree() throws RepositoryException, ExecutionException, InterruptedException {
Node n = getNode(TEST_PATH);
n.addNode("a").addNode("b").addNode("c");
getAdminSession().save();
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
getAdminSession().move(TEST_PATH + "/a", TEST_PATH + "/t");
listener.expect(TEST_PATH + "/t", NODE_MOVED);
listener.expect(TEST_PATH + "/a", NODE_REMOVED);
listener.expect(TEST_PATH + "/a/jcr:primaryType", PROPERTY_REMOVED);
listener.expect(TEST_PATH + "/t", NODE_ADDED);
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void filterDisjunctPaths()
throws ExecutionException, InterruptedException, RepositoryException {
assumeTrue(observationManager instanceof JackrabbitObservationManager);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
FilterBuilder builder = new FilterBuilder();
builder.condition(builder.any(
builder.path(TEST_PATH + "/a/b"),
builder.path(TEST_PATH + "/x/y")));
oManager.addEventListener(listener, builder.build());
Node testNode = getNode(TEST_PATH);
Node b = testNode.addNode("a").addNode("b");
b.addNode("c");
Node y = testNode.addNode("x").addNode("y");
y.addNode("z");
listener.expect(b.getPath(), NODE_ADDED);
listener.expect(y.getPath(), NODE_ADDED);
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void disjunctPaths() throws ExecutionException, InterruptedException, RepositoryException {
assumeTrue(observationManager instanceof JackrabbitObservationManager);
JackrabbitObservationManager oManager = (JackrabbitObservationManager) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter()
.setAdditionalPaths(TEST_PATH + "/a", TEST_PATH + "/x")
.setEventTypes(NODE_ADDED);
oManager.addEventListener(listener, filter);
Node testNode = getNode(TEST_PATH);
Node b = testNode.addNode("a").addNode("b");
b.addNode("c");
Node y = testNode.addNode("x").addNode("y");
y.addNode("z");
listener.expect(b.getPath(), NODE_ADDED);
listener.expect(y.getPath(), NODE_ADDED);
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void noDuplicates() throws ExecutionException, InterruptedException, RepositoryException {
assumeTrue(observationManager instanceof JackrabbitObservationManager);
JackrabbitObservationManager oManager = (JackrabbitObservationManager) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter()
.setAdditionalPaths(TEST_PATH + "/a", TEST_PATH + "/a")
.setEventTypes(NODE_ADDED);
oManager.addEventListener(listener, filter);
Node testNode = getNode(TEST_PATH);
Node b = testNode.addNode("a").addNode("b");
b.addNode("c");
Node y = testNode.addNode("x").addNode("y");
y.addNode("z");
listener.expect(b.getPath(), NODE_ADDED);
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void pathExclude() throws ExecutionException, InterruptedException, RepositoryException {
assumeTrue(observationManager instanceof JackrabbitObservationManager);
JackrabbitObservationManager oManager = (JackrabbitObservationManager) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter()
.setAbsPath(TEST_PATH)
.setIsDeep(true)
.setExcludedPaths(TEST_PATH + "/c", TEST_PATH + "/d", "/x/y")
.setEventTypes(ALL_EVENTS);
oManager.addEventListener(listener, filter);
Node n = getNode(TEST_PATH);
listener.expectAdd(listener.expectAdd(
listener.expectAdd(n.addNode("a")).addNode("a1")).setProperty("p", "q"));
listener.expectAdd(
listener.expectAdd(n.addNode("b")).setProperty("p", "q"));
n.addNode("c").addNode("c1").setProperty("p", "q");
n.addNode("d").setProperty("p", "q");
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void parentPathExclude() throws ExecutionException, InterruptedException, RepositoryException {
assumeTrue(observationManager instanceof JackrabbitObservationManager);
Node n = getNode(TEST_PATH).addNode("n");
getAdminSession().save();
JackrabbitObservationManager oManager = (JackrabbitObservationManager) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter()
.setAbsPath(n.getPath())
.setIsDeep(true)
.setExcludedPaths(n.getParent().getPath())
.setEventTypes(ALL_EVENTS);
oManager.addEventListener(listener, filter);
n.addNode("n1");
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void deepNodeTypeMixinHierarchy() throws Exception {
NodeTypeManager ntm = getAdminSession().getWorkspace().getNodeTypeManager();
NodeTypeTemplate parentMixin = ntm.createNodeTypeTemplate();
parentMixin.setName("parentmixin");
parentMixin.setMixin(true);
ntm.registerNodeType(parentMixin, false);
NodeTypeTemplate childMixin = ntm.createNodeTypeTemplate();
childMixin.setName("childmixin");
childMixin.setMixin(true);
childMixin.setDeclaredSuperTypeNames(new String[] {"parentmixin"});
ntm.registerNodeType(childMixin, false);
NodeTypeTemplate mytype = ntm.createNodeTypeTemplate();
mytype.setName("mytype");
mytype.setMixin(false);
mytype.setDeclaredSuperTypeNames(new String[] {"childmixin"});
NodeDefinitionTemplate child = ntm.createNodeDefinitionTemplate();
child.setName("*");
child.setDefaultPrimaryTypeName("nt:base");
child.setRequiredPrimaryTypeNames(new String[] {"nt:base"});
List<NodeDefinition> children = mytype.getNodeDefinitionTemplates();
children.add(child);
ntm.registerNodeType(mytype, false);
getAdminSession().save();
// create a fresh session here to catch the above new node type definitions
observingSession = createAdminSession();
observationManager = observingSession.getWorkspace().getObservationManager();
JackrabbitObservationManager oManager = (JackrabbitObservationManager) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter()
.setAbsPath("/")
.setIsDeep(true)
.setNodeTypes(new String[] {"parentmixin"})
.setEventTypes(ALL_EVENTS);
oManager.addEventListener(listener, filter);
Node n = getNode(TEST_PATH).addNode("n", "mytype");
listener.expect(n.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
Node m = n.addNode("m", "nt:unstructured");
listener.expect(m.getPath(), NODE_ADDED);
getAdminSession().save();
Thread.sleep(1000);
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void filterPropertyOfParent()
throws RepositoryException, ExecutionException, InterruptedException {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
FilterBuilder builder = new FilterBuilder();
// Events for all items whose parent has a property named "foo" with value "bar"
builder.condition(builder.property(Selectors.PARENT, "foo",
new Predicate<PropertyState>() {
@Override
public boolean apply(PropertyState property) {
return "bar".equals(property.getValue(STRING));
}
}));
oManager.addEventListener(listener, builder.build());
Node testNode = getNode(TEST_PATH);
Node a = testNode.addNode("a");
Node x = testNode.addNode("x");
a.setProperty("foo", "bar");
x.setProperty("foo", "baz");
a.addNode("b");
x.addNode("y");
listener.expect(a.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
listener.expect(a.getPath() + "/foo", PROPERTY_ADDED);
listener.expect(a.getPath() + "/b", NODE_ADDED);
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void filterPropertyOfChild()
throws RepositoryException, ExecutionException, InterruptedException {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
FilterBuilder builder = new FilterBuilder();
// Events for all items that have a property "b/c/foo" with value "bar"
builder.condition(builder.property(Selectors.fromThis("b/c"), "foo",
new Predicate<PropertyState>() {
@Override
public boolean apply(PropertyState property) {
return "bar".equals(property.getValue(STRING));
}
}));
oManager.addEventListener(listener, builder.build());
Node testNode = getNode(TEST_PATH);
Node a = testNode.addNode("a");
a.addNode("b").addNode("c").setProperty("foo", "bar");
a.addNode("d");
Node x = testNode.addNode("x");
x.addNode("b").addNode("c").setProperty("foo", "baz");
x.addNode("d");
listener.expect(a.getPath(), NODE_ADDED);
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void addSubtreeFilter() throws RepositoryException, ExecutionException, InterruptedException {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
FilterBuilder builder = new FilterBuilder();
// Only generate events for the root of added sub trees
builder.condition(builder.addSubtree());
oManager.addEventListener(listener, builder.build());
Node testNode = getNode(TEST_PATH);
Node a = listener.expectAdd(testNode.addNode("a"));
a.addNode("c");
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void removeSubtreeFilter() throws RepositoryException, ExecutionException, InterruptedException {
assumeTrue(observationManager instanceof ObservationManagerImpl);
Node testNode = getNode(TEST_PATH);
testNode.addNode("a").addNode("c");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
FilterBuilder builder = new FilterBuilder();
// Only generate events for the root of deleted sub trees
builder.condition(builder.deleteSubtree());
oManager.addEventListener(listener, builder.build());
listener.expectRemove(testNode.getNode("a")).remove();
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void propertyValue() throws RepositoryException, ExecutionException, InterruptedException {
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
try {
Node n = getNode(TEST_PATH);
n.setProperty("added", 42);
listener.expectValue(null, n.getProperty("added").getValue());
Value before = n.getProperty("test_property1").getValue();
n.getProperty("test_property1").setValue(43);
listener.expectValue(before, n.getProperty("test_property1").getValue());
before = n.getProperty("test_property2").getValue();
n.getProperty("test_property2").remove();
listener.expectValue(before, null);
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
@Test
public void propertyValues() throws RepositoryException, ExecutionException, InterruptedException {
Node n = getNode(TEST_PATH);
n.setProperty("toChange", new String[]{"one", "two"}, PropertyType.STRING);
n.setProperty("toDelete", new String[]{"three", "four"}, PropertyType.STRING);
getAdminSession().save();
ExpectationListener listener = new ExpectationListener();
observationManager.addEventListener(listener, ALL_EVENTS, "/", true, null, null, false);
try {
n.setProperty("added", new String[]{"five", "six"}, PropertyType.STRING);
listener.expectValues(null, n.getProperty("added").getValues());
Value[] before = n.getProperty("toChange").getValues();
n.getProperty("toChange").setValue(new String[]{"1", "2"});
listener.expectValues(before, n.getProperty("toChange").getValues());
before = n.getProperty("toDelete").getValues();
n.getProperty("toDelete").remove();
listener.expectValues(before, null);
getAdminSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
finally {
observationManager.removeEventListener(listener);
}
}
//------------------------------------------------------------< private >---
private Node getNode(String path) throws RepositoryException {
return getAdminSession().getNode(path);
}
//------------------------------------------------------------< ExpectationListener >---
private static class Expectation extends ForwardingListenableFuture<Event> {
private final SettableFuture<Event> future = SettableFuture.create();
private final String name;
private volatile boolean enabled = true;
Expectation(String name, boolean enabled) {
this.name = name;
this.enabled = enabled;
}
Expectation(String name) {
this(name, true);
}
@Override
protected ListenableFuture<Event> delegate() {
return future;
}
public void enable(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
public void complete(Event event) {
future.set(event);
}
public boolean isComplete() {
return future.isDone();
}
public void fail(Exception e) {
future.setException(e);
}
public boolean wait(long timeout, TimeUnit unit) {
try {
future.get(timeout, unit);
return true;
}
catch (Exception e) {
return false;
}
}
public boolean onEvent(Event event) throws Exception {
return true;
}
@Override
public String toString() {
return name;
}
}
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());
private volatile Exception failed;
public Expectation expect(Expectation expectation) {
if (failed != null) {
expectation.fail(failed);
}
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) {
@Override
public boolean onEvent(Event event) throws RepositoryException {
return type == event.getType() && equal(path, event.getPath());
}
});
}
public Future<Event> expect(final String path, final String identifier, final int type) {
return expect(new Expectation("path = " + path + ", identifier = " + identifier + ", type = " + type) {
@Override
public boolean onEvent(Event event) throws RepositoryException {
return type == event.getType() && equal(path, event.getPath()) && equal(identifier, event.getIdentifier());
}
});
}
public Node expectAdd(Node node) throws RepositoryException {
expect(node.getPath(), NODE_ADDED);
expect(node.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
return node;
}
public Node expectRemove(Node node) throws RepositoryException {
expect(node.getPath(), NODE_REMOVED);
expect(node.getPath() + "/jcr:primaryType", PROPERTY_REMOVED);
return node;
}
public Property expectAdd(Property property) throws RepositoryException {
expect(property.getPath(), PROPERTY_ADDED);
return property;
}
public Property expectRemove(Property property) throws RepositoryException {
expect(property.getPath(), PROPERTY_REMOVED);
return property;
}
public Property expectChange(Property property) throws RepositoryException {
expect(property.getPath(), PROPERTY_CHANGED);
return property;
}
public void expectMove(final String src, final String dst) {
expect(new Expectation('>' + src + ':' + dst){
@Override
public boolean onEvent(Event event) throws Exception {
return event.getType() == NODE_MOVED &&
equal(dst, event.getPath()) &&
equal(src, event.getInfo().get("srcAbsPath")) &&
equal(dst, event.getInfo().get("destAbsPath"));
}
});
}
public void expectValue(final Value before, final Value after) {
expect(new Expectation("Before value " + before + " after value " + after) {
@Override
public boolean onEvent(Event event) throws Exception {
return equal(before, event.getInfo().get("beforeValue")) &&
equal(after, event.getInfo().get("afterValue"));
}
});
}
public void expectValues(final Value[] before, final Value[] after) {
expect(new Expectation("Before valuse " + before + " after values " + after) {
@Override
public boolean onEvent(Event event) throws Exception {
return Arrays.equals(before, (Object[])event.getInfo().get("beforeValue")) &&
Arrays.equals(after, (Object[]) event.getInfo().get("afterValue"));
}
});
}
public Future<Event> expectBeforeValue(final String path, final int type, final String beforeValue) {
return expect(new Expectation("path = " + path + ", type = " + type + ", beforeValue = " + beforeValue) {
@Override
public boolean onEvent(Event event) throws RepositoryException {
return type == event.getType() && equal(path, event.getPath()) && event.getInfo().containsKey("beforeValue") && beforeValue.equals(((Value)event.getInfo().get("beforeValue")).getString());
}
});
}
public List<Expectation> getMissing(int time, TimeUnit timeUnit)
throws ExecutionException, InterruptedException {
List<Expectation> missing = Lists.newArrayList();
long t0 = System.nanoTime();
try {
Futures.allAsList(expected).get(time, timeUnit);
}
catch (TimeoutException e) {
for (Expectation exp : expected) {
if (!exp.isDone()) {
missing.add(exp);
}
}
}
return missing;
}
public List<Event> getUnexpected() {
return Lists.newArrayList(unexpected);
}
@Override
public void onEvent(EventIterator events) {
try {
while (events.hasNext() && failed == null) {
Event event = events.nextEvent();
boolean found = false;
for (Expectation exp : expected) {
if (exp.isEnabled() && !exp.isComplete() && exp.onEvent(event)) {
found = true;
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);
}
}
} catch (Exception e) {
for (Expectation exp : expected) {
exp.fail(e);
}
failed = e;
}
}
private static String key(String path, int type) {
return path + ':' + type;
}
}
//------------------------------------------------------------< OakEventFilter tests >---
@Test
public void applyNodeTypeOnSelf() throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
Node testNode = getNode(TEST_PATH);
testNode.addNode("a", "nt:unstructured").addNode("b", "oak:Unstructured").addNode("c", "nt:unstructured");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter.setAbsPath("/");
filter.setIsDeep(true);
filter.setNodeTypes(new String[] {"oak:Unstructured"});
filter = FilterFactory.wrap(filter).withApplyNodeTypeOnSelf();
oManager.addEventListener(listener, filter);
testNode.getNode("a").getNode("b").getNode("c").remove();
testNode.getSession().save();
// wait 1 sec to give failures a chance (we're not expecting anything, but perhaps
// something would come, after 1sec more likely than after 0sec)
Thread.sleep(1000);
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
Node b = testNode.getNode("a").getNode("b");
// OAK-5061 : the event NODE_REMOVED on /a/b is actually expected and was missing in the test:
listener.expect(b.getPath(), NODE_REMOVED);
b.remove();
testNode.getSession().save();
Thread.sleep(1000);
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
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();
}
/**
* This tests a filter which registers a few paths and then expects
* NOT to get any event if an unrelated parent is removed
*/
@Test
public void includeAncestorsRemove_Unrelated() throws Exception {
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/c/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/unrelated/child", "/a/unrelated",
"/a"},
new String[] {"/a"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/c/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/unrelated/child", "/a/unrelated",
"/a/b"},
new String[] {"/a/b"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/c/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/unrelated/child", "/a/unrelated",
"/a/b/c"},
new String[] {"/a/b/c"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/c/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/unrelated/child", "/a/unrelated",
"/a/b/c/d"},
new String[] {"/a/b/c/d"},
new String[] {"/a/b/c/d/jcr:primaryType"});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/c/d/*.html"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/unrelated/child", "/a/unrelated",
"/a/b/c/d"},
new String[] {"/a/b/c/d"},
new String[] {});
// and some glob tests:
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/*/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/related/unrelatedchild", "/a/b/related", "/a/unrelated/child", "/a/unrelated",
"/a"},
new String[] {"/a", "/a/b/related"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/**/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/related/child", "/a/b/c/related",
"/a/b/related/child", "/a/b/related", "/a/unrelated/child", "/a/unrelated",
"/a"},
new String[] {"/a", "/a/b/related", "/a/b/related/child", "/a/b/c/related/child", "/a/b/c/related"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/b/**/d/*.html"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/related/child", "/a/b/c/related",
"/a/b/related/child", "/a/b/related", "/a/unrelated/child", "/a/unrelated",
"/a"},
new String[] {"/a", "/a/b/related", "/a/b/related/child", "/a/b/c/related/child", "/a/b/c/related"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/*/c/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/related/unrelatedchild", "/a/related",
"/a/b"},
new String[] {"/a/b", "/a/related"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/**/c/d"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/related/child", "/a/b/c/related",
"/a/b/related/child", "/a/b/related", "/a/related/relatedchild", "/a/related",
"/a/b"},
new String[] {"/a/b", "/a/related/relatedchild", "/a/related", "/a/b/related/child", "/a/b/related", "/a/b/c/related/child", "/a/b/c/related"},
new String[] {});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/*/c/*.html"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/unrelated/child", "/a/b/c/unrelated",
"/a/b/unrelated/child", "/a/b/unrelated", "/a/related/unrelatedchild", "/a/related",
"/a/b/c/x.html", "/a/b"},
new String[] {"/a/b", "/a/related", "/a/b/c/x.html"},
new String[] {"/a/b/c/x.html/jcr:primaryType"});
doIncludeAncestorsRemove_Unrelated(
new String[] {"/a/**/c/*.html"},
new String[] {"/unrelated/child", "/unrelated", "/a/b/c/related/child", "/a/b/c/related",
"/a/b/related/child", "/a/b/related", "/a/related/child", "/a/related",
"/a/b"},
new String[] {"/a/b", "/a/related/child", "/a/related", "/a/b/related/child", "/a/b/related", "/a/b/c/related/child", "/a/b/c/related", "/a/b/c/related/child", "/a/b/c/related"},
new String[] {});
}
private void doIncludeAncestorsRemove_Unrelated(String[] absPaths,
String[] createAndRemoveNodes, String[] expectedRemoveNodeEvents, String[] expectedRemovePropertyEvents) throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
OakEventFilter filterWithAncestorsRemove = FilterFactory.wrap(new JackrabbitEventFilter());
filterWithAncestorsRemove.setEventTypes(NODE_REMOVED | PROPERTY_REMOVED);
assertTrue(absPaths.length >= 1);
String[] additionalPaths = new String[absPaths.length];
System.arraycopy(absPaths, 0, additionalPaths, 0, absPaths.length);
if (!absPaths[0].contains("*")) {
filterWithAncestorsRemove.setAbsPath(absPaths[0]);
if (absPaths.length > 1) {
additionalPaths = new String[absPaths.length - 1];
System.arraycopy(absPaths, 1, additionalPaths, 0, absPaths.length - 1);
}
}
filterWithAncestorsRemove.withIncludeGlobPaths(additionalPaths);
filterWithAncestorsRemove.setIsDeep(true);
filterWithAncestorsRemove = filterWithAncestorsRemove.withIncludeAncestorsRemove();
ExpectationListener listenerWithAncestorsRemove = new ExpectationListener();
oManager.addEventListener(listenerWithAncestorsRemove, filterWithAncestorsRemove);
OakEventFilter filterWithoutAncestorsRemove = FilterFactory.wrap(new JackrabbitEventFilter());
filterWithoutAncestorsRemove.setEventTypes(NODE_REMOVED);
if (!absPaths[0].contains("*")) {
filterWithoutAncestorsRemove.setAbsPath(absPaths[0]);
}
filterWithoutAncestorsRemove.withIncludeGlobPaths(additionalPaths);
filterWithoutAncestorsRemove.setIsDeep(true);
ExpectationListener listenerWithoutAncestorsRemove = new ExpectationListener();
oManager.addEventListener(listenerWithoutAncestorsRemove, filterWithoutAncestorsRemove);
Session session = getAdminSession();
for (String path : createAndRemoveNodes) {
Iterator<String> it = PathUtils.elements(path).iterator();
Node node = session.getRootNode();
while(it.hasNext()) {
String elem = it.next();
if (!node.hasNode(elem)) {
node = node.addNode(elem);
} else {
node = node.getNode(elem);
}
}
}
session.save();
for (String nodePath : expectedRemoveNodeEvents) {
listenerWithAncestorsRemove.expect(nodePath, NODE_REMOVED);
}
for (String propertyPath : expectedRemovePropertyEvents) {
listenerWithAncestorsRemove.expect(propertyPath, PROPERTY_REMOVED);
}
for (String path : createAndRemoveNodes) {
Iterator<String> it = PathUtils.elements(path).iterator();
Node node = session.getRootNode();
while(it.hasNext()) {
String elem = it.next();
node = node.getNode(elem);
}
node.remove();
session.save();
}
Thread.sleep(1000);
List<Expectation> missing = listenerWithoutAncestorsRemove.getMissing(TIME_OUT, TimeUnit.SECONDS);
List<Event> unexpected = listenerWithoutAncestorsRemove.getUnexpected();
assertTrue("Unexpected events (listenerWithoutAncestorsRemove): " + unexpected, unexpected.isEmpty());
assertTrue("Missing events (listenerWithoutAncestorsRemove): " + missing, missing.isEmpty());
missing = listenerWithAncestorsRemove.getMissing(TIME_OUT, TimeUnit.SECONDS);
unexpected = listenerWithAncestorsRemove.getUnexpected();
assertTrue("Unexpected events (listenerWithAncestorsRemove): " + unexpected, unexpected.isEmpty());
assertTrue("Missing events (listenerWithAncestorsRemove): " + missing, missing.isEmpty());
}
@Test
public void includeAncestorsRemove() throws Exception {
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter.setAbsPath(TEST_PATH + "/a/b/c/d");
filter.setIsDeep(true);
filter = FilterFactory.wrap(filter).withIncludeAncestorsRemove();
FilterProvider filterProvider = doIncludeAncestorsRemove(filter);
// with 'includeAncestorsRemove' flag the listener is registered at '/'
assertMatches(filterProvider.getSubTrees(), "/");
filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter.setIsDeep(true);
filter = FilterFactory.wrap(filter).withIncludeAncestorsRemove().withIncludeGlobPaths(TEST_PATH + "/a/b/c/**");
filterProvider = doIncludeAncestorsRemove(filter);
// with 'includeAncestorsRemove' flag the listener is registered at '/'
assertMatches(filterProvider.getSubTrees(), "/");
}
private FilterProvider doIncludeAncestorsRemove(JackrabbitEventFilter filter) throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
Node testNode = getNode(TEST_PATH);
testNode.addNode("a").addNode("b").addNode("c").addNode("d").setProperty("e", 42);
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
oManager.addEventListener(listener, filter);
Node d = testNode.getNode("a").getNode("b").getNode("c").getNode("d");
Property e = d.getProperty("e");
listener.expectRemove(e);
// listener.expectRemove(d.getProperty("jcr:primaryType"));
// d.remove();
listener.expectRemove(d).remove();
testNode.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());
oManager.addEventListener(new EventListener() {
@Override
public void onEvent(EventIterator events) {
while(events.hasNext()) {
System.out.println("/a-listener GOT: "+events.next());
}
}
}, NODE_REMOVED, TEST_PATH + "/a", false, null, null, false);
System.out.println("REGISTERED");
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();
Thread.sleep(1000);
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
unexpected = listener.getUnexpected();
assertTrue("Missing events: " + missing, missing.isEmpty());
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
Node a = testNode.getNode("a");
listener.expect(a.getPath(), NODE_REMOVED);
a.remove();
// but not the jcr:primaryType
testNode.getSession().save();
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
assertTrue("Missing events: " + missing, missing.isEmpty());
ChangeProcessor cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
FilterProvider filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
return filterProvider;
}
@Test
public void includeRemovedSubtree_Globs() throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
Node testNode = getNode(TEST_PATH);
testNode.addNode("a").addNode("b").addNode("c").addNode("d.jsp");
testNode.addNode("e").addNode("f").addNode("g.jsp");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
OakEventFilter oef = FilterFactory.wrap(filter);
oef.setEventTypes(ALL_EVENTS);
oef.withIncludeGlobPaths(TEST_PATH + "/a/**/*.jsp");
oef.withNodeTypeAggregate(new String[] {"nt:unstructured"}, new String[] {""});
oef.withIncludeSubtreeOnRemove();
oManager.addEventListener(listener, oef);
// the glob is for a jsp - so we should (only) get an event for that
// but only for the properties of d.jsp that get removed, not of removal of d.jsp itself
// as that would again be reported towards the parent of d.jsp which is /a/b/c
Node dDotJsp = testNode.getNode("a").getNode("b").getNode("c").getNode("d.jsp");
listener.expect(dDotJsp.getPath() + "/jcr:primaryType", dDotJsp.getPath(), PROPERTY_REMOVED);
// but we're removing /a/b
testNode.getNode("a").getNode("b").remove();
// and for removal of /e nothing should be generated
testNode.getNode("e").remove();
testNode.getSession().save();
Thread.sleep(1000);
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void includeRemovedSubtree() throws RepositoryException, ExecutionException, InterruptedException {
assumeTrue(observationManager instanceof ObservationManagerImpl);
Node testNode = getNode(TEST_PATH);
testNode.addNode("a").addNode("c");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter.setAbsPath("/");
filter.setIsDeep(true);
filter = FilterFactory.wrap(filter).withIncludeSubtreeOnRemove();
oManager.addEventListener(listener, filter);
listener.expectRemove(testNode.getNode("a").getNode("c"));
listener.expectRemove(testNode.getNode("a")).remove();
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void includeRemovedSubtree_BeforeValue() throws RepositoryException, ExecutionException, InterruptedException {
assumeTrue(observationManager instanceof ObservationManagerImpl);
Node testNode = getNode(TEST_PATH);
Node a = testNode.addNode("a");
a.setProperty("propA", "24");
a.addNode("c").setProperty("propB", "42");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter.setAbsPath("/");
filter.setIsDeep(true);
filter = FilterFactory.wrap(filter).withIncludeSubtreeOnRemove();
oManager.addEventListener(listener, filter);
Node c = testNode.getNode("a").getNode("c");
listener.expectRemove(c);
listener.expectBeforeValue(c.getProperty("propB").getPath(), PROPERTY_REMOVED, "42");
a = testNode.getNode("a");
listener.expectBeforeValue(a.getProperty("propA").getPath(), PROPERTY_REMOVED, "24");
listener.expectRemove(a).remove();
testNode.getSession().save();
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void includeGlobPaths() throws Exception {
Node testNode = getNode(TEST_PATH);
testNode.addNode("a1").addNode("b").addNode("c");
testNode.addNode("a2").addNode("b").addNode("c");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter = FilterFactory.wrap(filter).withIncludeGlobPaths(TEST_PATH + "/a2/**");
oManager.addEventListener(listener, filter);
ChangeProcessor cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
FilterProvider filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), TEST_PATH + "/a2");
testNode.getNode("a1").getNode("b").remove();
listener.expectRemove(testNode.getNode("a2").getNode("b")).remove();
testNode.getSession().save();
Thread.sleep(1000);
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
Node a3 = testNode.addNode("a3");
Node foo = a3.addNode("bar").addNode("foo");
testNode.getSession().save();
filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
// filter.setAbsPath(TEST_PATH + "/a3/bar/foo/x");
filter = FilterFactory.wrap(filter).withIncludeGlobPaths(TEST_PATH + "/a3/**/x");
oManager.addEventListener(listener, filter);
cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), TEST_PATH + "/a3");
Node x = foo.addNode("x");
listener.expect(x.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
testNode.getSession().save();
Thread.sleep(1000);
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter = FilterFactory.wrap(filter).withIncludeGlobPaths(TEST_PATH + "/a3/**/y");
oManager.addEventListener(listener, filter);
cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), TEST_PATH + "/a3");
Node y = foo.addNode("y");
listener.expect(y.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
testNode.getSession().save();
Thread.sleep(1000);
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void testConsecutiveGlobPaths() throws Exception {
Node testNode = getNode(TEST_PATH);
Node a1 = testNode.addNode("a1");
a1.addNode("b1").addNode("c1");
a1.addNode("b2").addNode("c2");
testNode.addNode("a2").addNode("b").addNode("c");
testNode.getSession().save();
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setEventTypes(ALL_EVENTS);
filter = FilterFactory.wrap(filter).withIncludeGlobPaths(TEST_PATH + "/a2/**").withIncludeGlobPaths(TEST_PATH + "/a1/**");
oManager.addEventListener(listener, filter);
ChangeProcessor cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
FilterProvider filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), TEST_PATH + "/a1", TEST_PATH + "/a2");
listener.expectRemove(testNode.getNode("a1").getNode("b2")).remove();
listener.expectRemove(testNode.getNode("a2").getNode("b")).remove();
testNode.getSession().save();
Thread.sleep(1000);
List<Expectation> missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
assertTrue("Missing events: " + missing, missing.isEmpty());
List<Event> unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
}
@Test
public void testAggregate1() throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setAbsPath("/parent");
filter.setIsDeep(true);
filter.setEventTypes(ALL_EVENTS);
filter = FilterFactory.wrap(filter).withNodeTypeAggregate(new String[] { "oak:Unstructured" },
new String[] { "", "jcr:content", "jcr:content/**" });
oManager.addEventListener(listener, filter);
Node parent = getAdminSession().getRootNode().addNode("parent", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expect(parent.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
Node child = parent.addNode("child", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expectAdd(child);
Node file = child.addNode("file", "oak:Unstructured");
listener.expectAdd(file);
Node jcrContent = file.addNode("jcr:content", "nt:unstructured");
listener.expect(jcrContent.getPath(), "/parent/child/file", NODE_ADDED);
listener.expect(jcrContent.getPath() + "/jcr:primaryType", "/parent/child/file", PROPERTY_ADDED);
Property jcrDataProperty = jcrContent.setProperty("jcr:data", "foo");
listener.expect(jcrDataProperty.getPath(), "/parent/child/file", PROPERTY_ADDED);
parent.getSession().save();
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());
file = getAdminSession().getRootNode().getNode("parent").getNode("child").getNode("file");
jcrContent = file.getNode("jcr:content");
Property newProperty = jcrContent.setProperty("newProperty", "foo");
listener.expect(newProperty.getPath(), "/parent/child/file", PROPERTY_ADDED);
Property lastModifiedBy = jcrContent.setProperty("jcr:lastModifiedBy", "bar");
listener.expect(lastModifiedBy.getPath(), "/parent/child/file", PROPERTY_ADDED);
jcrContent.getSession().save();
Thread.sleep(2000);
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
assertTrue("Missing events: " + missing, missing.isEmpty());
}
@Test
public void testAggregate2() throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setAbsPath("/parent");
filter.setIsDeep(true);
filter.setEventTypes(ALL_EVENTS);
filter = FilterFactory.wrap(filter).withNodeTypeAggregate(new String[] { "oak:Unstructured" },
new String[] { "", "**" });// "file", "file/jcr:content",
// "file/jcr:content/**");
oManager.addEventListener(listener, filter);
Node parent = getAdminSession().getRootNode().addNode("parent", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expect(parent.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
Node child = parent.addNode("child", "oak:Unstructured");
listener.expectAdd(child);
Node file = child.addNode("file", "nt:unstructured");
listener.expectAdd(file);
Node jcrContent = file.addNode("jcr:content", "nt:unstructured");
listener.expect(jcrContent.getPath(), "/parent/child", NODE_ADDED);
listener.expect(jcrContent.getPath() + "/jcr:primaryType", "/parent/child", PROPERTY_ADDED);
Property jcrDataProperty = jcrContent.setProperty("jcr:data", "foo");
listener.expect(jcrDataProperty.getPath(), "/parent/child", PROPERTY_ADDED);
parent.getSession().save();
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());
file = getAdminSession().getRootNode().getNode("parent").getNode("child").getNode("file");
jcrContent = file.getNode("jcr:content");
Property newProperty = jcrContent.setProperty("newProperty", "foo");
listener.expect(newProperty.getPath(), "/parent/child", PROPERTY_ADDED);
Property lastModifiedBy = jcrContent.setProperty("jcr:lastModifiedBy", "bar");
listener.expect(lastModifiedBy.getPath(), "/parent/child", PROPERTY_ADDED);
jcrContent.getSession().save();
Thread.sleep(2000);
missing = listener.getMissing(TIME_OUT, TimeUnit.SECONDS);
unexpected = listener.getUnexpected();
assertTrue("Unexpected events: " + unexpected, unexpected.isEmpty());
assertTrue("Missing events: " + missing, missing.isEmpty());
}
@Test
public void testAggregate3() throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
JackrabbitEventFilter filter = new JackrabbitEventFilter();
filter.setAbsPath("/parent");
filter.setIsDeep(true);
filter.setEventTypes(ALL_EVENTS);
filter = FilterFactory.wrap(filter).withNodeTypeAggregate(new String[] { "oak:Unstructured" },
new String[] { "**" } );
oManager.addEventListener(listener, filter);
Node parent = getAdminSession().getRootNode().addNode("parent", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expect(parent.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
Node child = parent.addNode("child", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expectAdd(child);
Node file = child.addNode("file", "oak:Unstructured");
listener.expect(file.getPath(), "/parent/child/file", NODE_ADDED);
listener.expect(file.getPath() + "/jcr:primaryType", "/parent/child/file", PROPERTY_ADDED);
Node jcrContent = file.addNode("jcr:content", "nt:unstructured");
listener.expect(jcrContent.getPath(), "/parent/child/file", NODE_ADDED);
listener.expect(jcrContent.getPath() + "/jcr:primaryType", "/parent/child/file", PROPERTY_ADDED);
Property jcrDataProperty = jcrContent.setProperty("jcr:data", "foo");
listener.expect(jcrDataProperty.getPath(), "/parent/child/file", 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());
}
@Test
public void testAggregate4() 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[] { "**/foo/**" } )
.withIncludeGlobPaths("/parent/**/bar/**");
oManager.addEventListener(listener, filter);
ChangeProcessor cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
FilterProvider filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), "/parent");
Node parent = getAdminSession().getRootNode().addNode("parent", "nt:unstructured");
Node a = parent.addNode("a", "nt:unstructured");
Node b = a.addNode("b", "nt:unstructured");
Node bar = b.addNode("bar", "oak:Unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expect(bar.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
Node c = bar.addNode("c", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expectAdd(c);
Node foo = c.addNode("foo", "nt:unstructured");
// OAK-5096: in OR mode the following event also gets sent:
listener.expectAdd(foo);
Node jcrContent = foo.addNode("jcr:content", "nt:unstructured");
listener.expectAdd(jcrContent);
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 : new test case for OR mode
*/
@Test
public void testAggregate5() 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[] { "**/foo/**" } )
.withIncludeGlobPaths("/parent/**/bar/**");
oManager.addEventListener(listener, filter);
ChangeProcessor cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
FilterProvider filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), "/parent");
Node parent = getAdminSession().getRootNode().addNode("parent", "nt:unstructured");
Node bar = parent.addNode("bar", "nt:unstructured");
listener.expect(bar.getPath() + "/jcr:primaryType", PROPERTY_ADDED);
Node c = bar.addNode("c", "nt:unstructured");
listener.expectAdd(c);
Node foo = c.addNode("foo", "nt:unstructured");
listener.expectAdd(foo);
Node jcrContent = foo.addNode("jcr:content", "nt:unstructured");
listener.expectAdd(jcrContent);
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());
}
@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 {
OakEventFilter oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef.setIsDeep(true);
oef.withIncludeAncestorsRemove()
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/**/*.jsp");
doTestAggregate6(oef, new String[] {"/"}, new String[] {"/**", "/**/*.jsp", "/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef.setIsDeep(false);
oef.withIncludeAncestorsRemove()
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/**/*.jsp");
doTestAggregate6(oef, new String[] {"/"}, new String[] {"/**", "/**/*.jsp", "/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef.withIncludeAncestorsRemove()
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("**/*.jsp");
doTestAggregate6(oef, new String[] {"/"}, new String[] {"/**", "**/*.jsp", "**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef // without includeAncestorsRemove this time
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/**/*.jsp");
doTestAggregate6(oef, new String[] {""}, new String[] {"/**/*.jsp", "/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef // without includeAncestorsRemove this time
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("**/*.jsp");
doTestAggregate6(oef, new String[] {""}, new String[] {"**/*.jsp", "**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef.withIncludeAncestorsRemove()
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/parent/**/*.jsp");
doTestAggregate6(oef, new String[] {"/"}, new String[] {"/parent", "/parent/**", "/parent/**/*.jsp", "/parent/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef.withIncludeAncestorsRemove()
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/parent/bar/**/*.jsp");
doTestAggregate6(oef, new String[] {"/"}, new String[] {"/parent", "/parent/bar", "/parent/bar/**", "/parent/bar/**/*.jsp", "/parent/bar/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef // without includeAncestorsRemove this time
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/parent/**/*.jsp");
doTestAggregate6(oef, new String[] {"/parent"}, new String[] {"/parent/**/*.jsp", "/parent/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef // without includeAncestorsRemove this time
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/parent/**/*.jsp", "/foo/bar/**");
doTestAggregate6(oef, new String[] {"/parent", "/foo/bar"}, new String[] {"/foo/bar/**", "/parent/**/*.jsp", "/parent/**/*.jsp/**"});
oef = FilterFactory.wrap(new JackrabbitEventFilter());
oef.setEventTypes(ALL_EVENTS);
oef.withIncludeAncestorsRemove()
.withNodeTypeAggregate(new String[] { "oak:Unstructured" }, new String[] { "", "jcr:content" } )
.withIncludeGlobPaths("/parent/**/*.jsp", "/foo/bar/**");
doTestAggregate6(oef, new String[] {"/"}, new String[] {"/parent", "/foo", "/foo/bar", "/foo/bar/**", "/parent/**", "/parent/**/*.jsp", "/parent/**/*.jsp/**"});
}
private void doTestAggregate6(OakEventFilter oef, String[] expectedSubTrees, String[] expectedPrefilterPaths)
throws Exception {
assumeTrue(observationManager instanceof ObservationManagerImpl);
ObservationManagerImpl oManager = (ObservationManagerImpl) observationManager;
ExpectationListener listener = new ExpectationListener();
oManager.addEventListener(listener, oef);
ChangeProcessor cp = oManager.getChangeProcessor(listener);
assertNotNull(cp);
FilterProvider filterProvider = cp.getFilterProvider();
assertNotNull(filterProvider);
assertMatches(filterProvider.getSubTrees(), expectedSubTrees);
ChangeSetFilterImpl changeSetFilter = (ChangeSetFilterImpl)PrivateAccessor.getField(filterProvider, "changeSetFilter");
assertNotNull(changeSetFilter);
assertMatches(changeSetFilter.getRootIncludePaths(), expectedPrefilterPaths);
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());
// cleanup
Session session = getAdminSession();
session.getRootNode().getNode("parent").remove();
session.save();
oManager.removeEventListener(listener);
}
private void assertMatches(Iterable<String> actuals, String... expected) {
assertEquals(newHashSet(expected), newHashSet(actuals));
}
}