Index: modeshape-graph/src/main/java/org/modeshape/graph/request/SetPropertyRequest.java =================================================================== --- modeshape-graph/src/main/java/org/modeshape/graph/request/SetPropertyRequest.java (revision 2408) +++ modeshape-graph/src/main/java/org/modeshape/graph/request/SetPropertyRequest.java (working copy) @@ -35,7 +35,7 @@ import org.modeshape.graph.property.Property; * Instruction to set a particular property on the node at the specified location. This request never removes the node, * even if the property is empty. */ -public class SetPropertyRequest extends ChangeRequest { +public class SetPropertyRequest extends ChangeRequest implements PropertyChangeRequest { private static final long serialVersionUID = 1L; Index: modeshape-graph/src/main/java/org/modeshape/graph/request/UpdatePropertiesRequest.java =================================================================== --- modeshape-graph/src/main/java/org/modeshape/graph/request/UpdatePropertiesRequest.java (revision 2408) +++ modeshape-graph/src/main/java/org/modeshape/graph/request/UpdatePropertiesRequest.java (working copy) @@ -53,7 +53,7 @@ import org.modeshape.graph.property.Property; * is possible for a property to have no values. *

*/ -public class UpdatePropertiesRequest extends ChangeRequest { +public class UpdatePropertiesRequest extends ChangeRequest implements PropertyChangeRequest { private static final long serialVersionUID = 1L; Index: modeshape-graph/src/main/java/org/modeshape/graph/request/UpdateValuesRequest.java =================================================================== --- modeshape-graph/src/main/java/org/modeshape/graph/request/UpdateValuesRequest.java (revision 2408) +++ modeshape-graph/src/main/java/org/modeshape/graph/request/UpdateValuesRequest.java (working copy) @@ -27,7 +27,7 @@ import org.modeshape.graph.property.Property; * is possible for a property to have no values. *

*/ -public class UpdateValuesRequest extends ChangeRequest { +public class UpdateValuesRequest extends ChangeRequest implements PropertyChangeRequest { private static final long serialVersionUID = 1L; Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java (revision 2408) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrObservationManager.java (working copy) @@ -63,6 +63,7 @@ import org.modeshape.graph.property.ValueFactories; import org.modeshape.graph.property.ValueFactory; import org.modeshape.graph.property.ValueFormatException; import org.modeshape.graph.request.ChangeRequest; +import org.modeshape.graph.request.PropertyChangeRequest; /** * The implementation of JCR {@link ObservationManager}. @@ -805,7 +806,14 @@ final class JcrObservationManager implements ObservationManager { if (shouldCheckNodeType()) { ValueFactory stringFactory = getValueFactories().getStringFactory(); - Location parentLocation = Location.create(change.getLocation().getPath().getParent()); + Path changePath = null; + if (change.includes(ChangeType.NODE_ADDED, ChangeType.NODE_REMOVED)) { + changePath = change.getPath().getParent(); + } else { + changePath = change.getPath(); + } + + Location parentLocation = Location.create(changePath); Map propMap = this.propertiesByLocation.get(parentLocation); assert (propMap != null); @@ -933,6 +941,7 @@ final class JcrObservationManager implements ObservationManager { List changedLocations = new ArrayList(); // loop through changes saving the parent locations of the changed locations + Location root = null; for (ChangeRequest request : changes.getChangeRequests()) { String changedWorkspaceName = request.changedWorkspace(); // If this event is not from this session's workspace ... @@ -945,13 +954,26 @@ final class JcrObservationManager implements ObservationManager { } } Path changedPath = request.changedLocation().getPath(); - Path parentPath = changedPath.getParent(); - changedLocations.add(Location.create(parentPath)); + if (!(request instanceof PropertyChangeRequest)) { + // We want the primary type and mixin types of the parent node ... + changedPath = changedPath.getParent(); + } + Location location = Location.create(changedPath); + if (root == null && changedPath.isRoot()) root = location; + changedLocations.add(location); } if (!changedLocations.isEmpty()) { // more efficient to get all of the locations at once then it is one at a time using the NetChange Graph graph = getGraph(); this.propertiesByLocation = graph.getProperties(PRIMARY_TYPE, MIXIN_TYPES).on(changedLocations); + if (root != null && !propertiesByLocation.containsKey(root)) { + // The connector doesn't actually have any properties for the root node, so assume 'mode:root' ... + // For details, see MODE-959. + Property primaryType = graph.getContext().getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE, + ModeShapeLexicon.ROOT); + Map props = Collections.singletonMap(primaryType.getName(), primaryType); + this.propertiesByLocation.put(root, props); + } } } Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrObservationManagerTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrObservationManagerTest.java (revision 2408) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrObservationManagerTest.java (working copy) @@ -458,6 +458,27 @@ public final class JcrObservationManagerTest extends TestSuite { + propPath, containsPath(listener, propPath)); } + @Test + public void shouldReceivePropertyAddedEventWhenRegisteredToReceiveEventsBasedUponNodeTypeName() throws Exception { + // register listener + String[] nodeTypeNames = {"mode:root"}; + TestListener listener = addListener(1, ALL_EVENTS, null, true, null, nodeTypeNames, false); + + // add the property + this.session.getRootNode().setProperty("fooProp", "bar"); + save(); + + // event handling + listener.waitForEvents(); + removeListener(listener); + + // tests + checkResults(listener); + String propPath = "/fooProp"; + assertTrue("Path for added property is wrong: actual=" + listener.getEvents().get(0).getPath() + ", expected=" + propPath, + containsPath(listener, propPath)); + } + // =========================================================================================================================== // @see org.apache.jackrabbit.test.api.observation.EventIteratorTest // ===========================================================================================================================