Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java (revision 1875) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java (working copy) @@ -91,6 +91,11 @@ final class JcrObservationManager implements ObservationManager { private final ValueFactories valueFactories; /** + * The name of the session's workspace; cached for performance reasons. + */ + private final String workspaceName; + + /** * @param session the owning session (never null) * @param repositoryObservable the repository observable used to register JCR listeners (never null) * @throws IllegalArgumentException if either parameter is null @@ -105,6 +110,7 @@ final class JcrObservationManager implements ObservationManager { this.listeners = new ConcurrentHashMap(); this.namespaceRegistry = this.session.getExecutionContext().getNamespaceRegistry(); this.valueFactories = this.session.getExecutionContext().getValueFactories(); + this.workspaceName = this.session.getWorkspace().getName(); } /** @@ -194,6 +200,13 @@ final class JcrObservationManager implements ObservationManager { } /** + * @return workspaceName + */ + final String getWorkspaceName() { + return workspaceName; + } + + /** * Remove all of the listeners. This is typically called when the {@link JcrSession#logout() session logs out}. */ synchronized void removeAllEventListeners() { @@ -698,6 +711,10 @@ final class JcrObservationManager implements ObservationManager { // loop through changes saving the parent locations of the changed locations for (ChangeRequest request : changes.getChangeRequests()) { + // ignore all events other than those on this workspace ... + if (!getWorkspaceName().equals(request.changedWorkspace())) { + continue; + } Path changedPath = request.changedLocation().getPath(); Path parentPath = changedPath.getParent(); changedLocations.add(Location.create(parentPath)); @@ -725,6 +742,11 @@ final class JcrObservationManager implements ObservationManager { Collection events = new ArrayList(); for (NetChange change : netChanges.getNetChanges()) { + // ignore all events other than those on this workspace ... + if (!getWorkspaceName().equals(change.getRepositoryWorkspaceName())) { + continue; + } + // ignore if lock/unlock if (change.includes(ChangeType.NODE_LOCKED) || change.includes(ChangeType.NODE_UNLOCKED)) { continue; Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrObservationManagerTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrObservationManagerTest.java (revision 1875) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrObservationManagerTest.java (working copy) @@ -113,10 +113,15 @@ public final class JcrObservationManagerTest extends TestSuite { // =========================================================================================================================== // Fields // =========================================================================================================================== + protected static final String WORKSPACE = "ws1"; + protected static final String WORKSPACE2 = "ws2"; + protected static final String REPOSITORY = "r1"; + protected static final String SOURCE = "store"; private JcrEngine engine; private Session session; private Node testRootNode; + private List sessions; // =========================================================================================================================== // Methods @@ -140,26 +145,49 @@ public final class JcrObservationManagerTest extends TestSuite { String[] uuids, String[] nodeTypeNames, boolean noLocal ) throws Exception { + return addListener(this.session, eventsExpected, numIterators, eventTypes, absPath, isDeep, uuids, nodeTypeNames, noLocal); + } + + TestListener addListener( Session session, + int eventsExpected, + int eventTypes, + String absPath, + boolean isDeep, + String[] uuids, + String[] nodeTypeNames, + boolean noLocal ) throws Exception { + return addListener(session, eventsExpected, 1, eventTypes, absPath, isDeep, uuids, nodeTypeNames, noLocal); + } + + TestListener addListener( Session session, + int eventsExpected, + int numIterators, + int eventTypes, + String absPath, + boolean isDeep, + String[] uuids, + String[] nodeTypeNames, + boolean noLocal ) throws Exception { TestListener listener = new TestListener(eventsExpected, numIterators, eventTypes); - this.session.getWorkspace().getObservationManager().addEventListener(listener, - eventTypes, - absPath, - isDeep, - uuids, - nodeTypeNames, - noLocal); + session.getWorkspace().getObservationManager().addEventListener(listener, + eventTypes, + absPath, + isDeep, + uuids, + nodeTypeNames, + noLocal); return listener; } @After public void afterEach() { try { - if (this.session != null) { - this.session.logout(); + for (Session session : sessions) { + if (session != null && session.isLive()) session.logout(); } } finally { this.session = null; - + this.sessions.clear(); try { this.engine.shutdown(); } finally { @@ -170,14 +198,13 @@ public final class JcrObservationManagerTest extends TestSuite { @Before public void beforeEach() throws RepositoryException { - final String WORKSPACE = "ws1"; - final String REPOSITORY = "r1"; - final String SOURCE = "store"; + sessions = new ArrayList(); JcrConfiguration config = new JcrConfiguration(); config.repositorySource("store") .usingClass(InMemoryRepositorySource.class) .setRetryLimit(100) + .setProperty("predefinedWorkspaceNames", new String[] {WORKSPACE, WORKSPACE2}) .setProperty("defaultWorkspaceName", WORKSPACE); config.repository(REPOSITORY).setSource(SOURCE).setOption(Option.JAAS_LOGIN_CONFIG_NAME, "modeshape-jcr"); config.save(); @@ -187,14 +214,25 @@ public final class JcrObservationManagerTest extends TestSuite { this.engine.start(); // Create repository and session - Repository repository = this.engine.getRepository(REPOSITORY); - Credentials credentials = new SimpleCredentials(USER_ID, USER_ID.toCharArray()); - this.session = repository.login(credentials, WORKSPACE); + this.session = login(REPOSITORY, WORKSPACE, USER_ID, USER_ID.toCharArray()); this.testRootNode = this.session.getRootNode().addNode("testroot", UNSTRUCTURED); save(); } + protected Session login( String repositoryName, + String workspaceName, + String userId, + char[] password ) throws RepositoryException { + Repository repository = this.engine.getRepository(repositoryName); + Credentials credentials = new SimpleCredentials(userId, password); + Session session = repository.login(credentials, workspaceName); + if (session != null) { + sessions.add(session); + } + return session; + } + void checkResults( TestListener listener ) { if (listener.getActualEventCount() != listener.getExpectedEventCount()) { // Wrong number ... @@ -1643,6 +1681,42 @@ public final class JcrObservationManagerTest extends TestSuite { + ", expected=" + oldPath, containsPath(removeNodeListener, oldPath)); } + @Test + public void shouldNotReceiveEventsFromOtherWorkspaces() throws Exception { + // Log into a second workspace ... + Session session2 = login(REPOSITORY, "ws2", USER_ID, USER_ID.toCharArray()); + + // Register 2 listeners in the first session ... + TestListener listener1 = addListener(session, 4, ALL_EVENTS, "/", true, null, null, false); + TestListener addListener1 = addListener(session, 1, Event.NODE_ADDED, "/", true, null, null, false); + + // Register 2 listeners in the second session ... + TestListener listener2 = addListener(session2, 0, ALL_EVENTS, "/", true, null, null, false); + TestListener addListener2 = addListener(session2, 0, Event.NODE_ADDED, "/", true, null, null, false); + + // Add a node to the first session ... + session.getRootNode().addNode("nodeA", "nt:unstructured"); + session.save(); + + // Wait for the events on the first session's listeners (that should get the events) ... + listener1.waitForEvents(); + addListener1.waitForEvents(); + removeListener(listener1); + removeListener(addListener1); + + // Wait for the events on the second session's listeners (that should NOT get the events) ... + listener2.waitForEvents(); + // addListener2.waitForEvents(); + removeListener(listener2); + removeListener(addListener2); + + // Verify the expected events were received ... + checkResults(listener1); + checkResults(addListener1); + checkResults(listener2); + checkResults(addListener2); + } + // =========================================================================================================================== // Inner Class // ===========================================================================================================================