You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by to...@apache.org on 2016/01/28 14:41:33 UTC
svn commit: r1727345 - in /sling/trunk:
bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/
bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/
bundles/jcr/resource/src/test/java/org/apache/sling...
Author: tomekr
Date: Thu Jan 28 13:41:32 2016
New Revision: 1727345
URL: http://svn.apache.org/viewvc?rev=1727345&view=rev
Log:
SLING-5163 Rewrite the JCR Resource to post events using the new observation API
Added:
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceChange.java
Removed:
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ObservationListenerSupport.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousJcrResourceListener.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousOakResourceListener.java
Modified:
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/OakResourceListener.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/AbstractListenerTest.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerScalabilityTest.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/OakResourceListenerTest.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java
sling/trunk/launchpad/builder/src/main/provisioning/sling.txt
Added: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceChange.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceChange.java?rev=1727345&view=auto
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceChange.java (added)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceChange.java Thu Jan 28 13:41:32 2016
@@ -0,0 +1,149 @@
+/*
+ * 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.sling.jcr.resource.internal;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.sling.api.resource.observation.ResourceChange;
+
+public class JcrResourceChange extends ResourceChange {
+
+ private final String userId;
+
+ private JcrResourceChange(Builder builder) {
+ super(builder.changeType, builder.path, builder.isExternal, builder.addedAttributeNames, builder.changedAttributeNames, builder.removedAttributeNames);
+ this.userId = builder.userId;
+ }
+
+ @Override
+ public String getUserId() {
+ return userId;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("ResourceChange[type=").append(this.getType()).append(", path=").append(this.getPath());
+ if (getAddedPropertyNames() != null && !getAddedPropertyNames().isEmpty()) {
+ b.append(", added=").append(getAddedPropertyNames());
+ }
+ if (getChangedPropertyNames() != null && !getChangedPropertyNames().isEmpty()) {
+ b.append(", changed=").append(getChangedPropertyNames());
+ }
+ if (getRemovedPropertyNames() != null && !getRemovedPropertyNames().isEmpty()) {
+ b.append(", removed=").append(getRemovedPropertyNames());
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ public static class Builder {
+
+ private String path;
+
+ private ChangeType changeType;
+
+ private boolean isExternal;
+
+ private String userId;
+
+ private Set<String> changedAttributeNames;
+
+ private Set<String> addedAttributeNames;
+
+ private Set<String> removedAttributeNames;
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public ChangeType getChangeType() {
+ return changeType;
+ }
+
+ public void setChangeType(ChangeType changeType) {
+ this.changeType = changeType;
+ }
+
+ public boolean isExternal() {
+ return isExternal;
+ }
+
+ public void setExternal(boolean isExternal) {
+ this.isExternal = isExternal;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public Set<String> getChangedAttributeNames() {
+ return changedAttributeNames;
+ }
+
+ public void addChangedAttributeName(String propName) {
+ if (changedAttributeNames == null) {
+ changedAttributeNames = new LinkedHashSet<String>();
+ }
+ if (!changedAttributeNames.contains(propName)) {
+ changedAttributeNames.add(propName);
+ }
+ }
+
+ public Set<String> getAddedAttributeNames() {
+ return addedAttributeNames;
+ }
+
+ public void addAddedAttributeName(String propName) {
+ if (addedAttributeNames == null) {
+ addedAttributeNames = new LinkedHashSet<String>();
+ }
+ if (!addedAttributeNames.contains(propName)) {
+ addedAttributeNames.add(propName);
+ }
+ }
+
+ public Set<String> getRemovedAttributeNames() {
+ return removedAttributeNames;
+ }
+
+ public void addRemovedAttributeName(String propName) {
+ if (removedAttributeNames == null) {
+ removedAttributeNames = new LinkedHashSet<String>();
+ }
+ if (!removedAttributeNames.contains(propName)) {
+ removedAttributeNames.add(propName);
+ }
+ }
+
+ public ResourceChange build() {
+ return new JcrResourceChange(this);
+ }
+ }
+
+}
\ No newline at end of file
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java Thu Jan 28 13:41:32 2016
@@ -18,29 +18,42 @@
*/
package org.apache.sling.jcr.resource.internal;
+import static javax.jcr.observation.Event.NODE_ADDED;
+import static javax.jcr.observation.Event.NODE_REMOVED;
+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 java.io.Closeable;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
+import org.apache.commons.lang.StringUtils;
+import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.api.observation.JackrabbitEvent;
-import org.apache.sling.api.SlingConstants;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.api.resource.util.Path;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.resource.internal.JcrResourceChange.Builder;
import org.apache.sling.jcr.resource.internal.helper.jcr.PathMapper;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.event.EventConstants;
-import org.osgi.service.event.EventProperties;
+import org.apache.sling.spi.resource.provider.ObserverConfiguration;
+import org.apache.sling.spi.resource.provider.ProviderContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,36 +68,30 @@ public class JcrResourceListener impleme
private final Logger logger = LoggerFactory.getLogger(JcrResourceListener.class);
/** The repository is mounted under this path. */
- private final String mountPrefix;
+ private final String mountPrefix;
/** Is the Jackrabbit event class available? */
private final boolean hasJackrabbitEventClass;
- /**
- * A queue of OSGi Events created by
- * {@link #sendOsgiEvent(String, Event, String, EventAdmin, ChangedAttributes)}
- * waiting for actual dispatching to the OSGi Event Admin in
- * {@link #processOsgiEventQueue()}
- */
- private final LinkedBlockingQueue<Map<String, Object>> osgiEventQueue;
-
- /** Helper object. */
- final ObservationListenerSupport support;
+ private final Session session;
private final PathMapper pathMapper;
- /**
- * Marker event for {@link #processOsgiEventQueue()} to be signaled to
- * terminate processing Events.
- */
- private final Map<String, Object> TERMINATE_PROCESSING = new HashMap<String, Object>(1);
+ private final ProviderContext ctx;
+ private final boolean includeExternal;
+
+ @SuppressWarnings("deprecation")
public JcrResourceListener(
+ final ProviderContext ctx,
final String mountPrefix,
- final ObservationListenerSupport support,
- final PathMapper pathMapper)
+ final PathMapper pathMapper,
+ final SlingRepository repository)
throws RepositoryException {
+ this.includeExternal = isIncludeExternal(ctx);
this.pathMapper = pathMapper;
+ this.mountPrefix = mountPrefix;
+ this.ctx = ctx;
boolean foundClass = false;
try {
this.getClass().getClassLoader().loadClass(JackrabbitEvent.class.getName());
@@ -93,21 +100,19 @@ public class JcrResourceListener impleme
// we ignore this
}
this.hasJackrabbitEventClass = foundClass;
- this.mountPrefix = (mountPrefix == null || mountPrefix.length() == 0 || mountPrefix.equals("/") ? null : mountPrefix);
+ this.session = repository.loginAdministrative(repository.getDefaultWorkspace());
+ final String absPath = getAbsPath(pathMapper, ctx);
+ final int types = getTypes(ctx);
+ this.session.getWorkspace().getObservationManager().addEventListener(this, types, absPath, true, null, null, false);
+ }
- this.support = support;
- this.support.getSession().getWorkspace().getObservationManager().addEventListener(this,
- Event.NODE_ADDED|Event.NODE_REMOVED|Event.PROPERTY_ADDED|Event.PROPERTY_CHANGED|Event.PROPERTY_REMOVED,
- "/", true, null, null, false);
-
- this.osgiEventQueue = new LinkedBlockingQueue<Map<String,Object>>();
- final Thread oeqt = new Thread(new Runnable() {
- @Override
- public void run() {
- processOsgiEventQueue();
+ private boolean isIncludeExternal(ProviderContext ctx) {
+ for (ObserverConfiguration c : ctx.getObservationReporter().getObserverConfigurations()) {
+ if (c.includeExternal()) {
+ return true;
}
- }, "Apache Sling JCR Resource Event Queue Processor");
- oeqt.start();
+ }
+ return false;
}
/**
@@ -117,16 +122,11 @@ public class JcrResourceListener impleme
public void close() throws IOException {
// unregister from observations
try {
- this.support.getSession().getWorkspace().getObservationManager().removeEventListener(this);
+ this.session.getWorkspace().getObservationManager().removeEventListener(this);
} catch (RepositoryException e) {
logger.warn("Unable to remove session listener: " + this, e);
}
-
- // drop any remaining OSGi Events not processed yet
- this.osgiEventQueue.clear();
- this.osgiEventQueue.offer(TERMINATE_PROCESSING);
-
- this.support.dispose();
+ this.session.logout();
}
/**
@@ -134,273 +134,224 @@ public class JcrResourceListener impleme
*/
@Override
public void onEvent(final EventIterator events) {
- // if the event admin is currently not available, we just skip this
- final EventAdmin localEA = this.support.getEventAdmin();
- if ( localEA == null ) {
- return;
- }
- final Map<String, Map<String, Object>> addedEvents = new HashMap<String, Map<String, Object>>();
- final Map<String, ChangedAttributes> changedEvents = new HashMap<String, ChangedAttributes>();
- final Map<String, Map<String, Object>> removedEvents = new HashMap<String, Map<String, Object>>();
+ final Map<String, Builder> addedEvents = new HashMap<String, Builder>();
+ final Map<String, Builder> changedEvents = new HashMap<String, Builder>();
+ final Map<String, Builder> removedEvents = new HashMap<String, Builder>();
+
while ( events.hasNext() ) {
final Event event = events.nextEvent();
+ if (isExternal(event) && !includeExternal) {
+ continue;
+ }
+
try {
- final String eventPath;
- if ( this.mountPrefix != null ) {
- eventPath = this.mountPrefix + event.getPath();
- } else {
- eventPath = event.getPath();
- }
- if ( event.getType() == Event.PROPERTY_ADDED
- || event.getType() == Event.PROPERTY_REMOVED
- || event.getType() == Event.PROPERTY_CHANGED ) {
+ final String eventPath = event.getPath();
+ final int type = event.getType();
+ if ( type == PROPERTY_ADDED
+ || type == PROPERTY_REMOVED
+ || type == PROPERTY_CHANGED ) {
final int lastSlash = eventPath.lastIndexOf('/');
final String nodePath = eventPath.substring(0, lastSlash);
final String propName = eventPath.substring(lastSlash + 1);
- this.updateChangedEvent(changedEvents, nodePath, event, propName);
-
- } else if ( event.getType() == Event.NODE_ADDED ) {
- addedEvents.put(eventPath, createEventProperties(event));
-
- } else if ( event.getType() == Event.NODE_REMOVED) {
+ Builder builder = changedEvents.get(nodePath);
+ if (builder == null) {
+ changedEvents.put(nodePath, builder = createResourceChange(event, nodePath, ChangeType.CHANGED));
+ }
+ this.updateResourceChanged(builder, event.getType(), propName);
+ } else if ( type == NODE_ADDED ) {
+ addedEvents.put(eventPath, createResourceChange(event, ChangeType.ADDED));
+ } else if ( type == NODE_REMOVED) {
// remove is the strongest operation, therefore remove all removed
// paths from added
addedEvents.remove(eventPath);
- removedEvents.put(eventPath, createEventProperties(event));
+ removedEvents.put(eventPath, createResourceChange(event, ChangeType.REMOVED));
}
} catch (final RepositoryException e) {
- logger.error("Error during modification: {}", e.getMessage());
+ logger.error("Error during modification: {}", e);
}
}
- for (final Entry<String, Map<String, Object>> e : removedEvents.entrySet()) {
- // Launch an OSGi event
- sendOsgiEvent(e.getKey(), e.getValue(), SlingConstants.TOPIC_RESOURCE_REMOVED,
- null);
+ final List<ResourceChange> changes = new ArrayList<ResourceChange>();
+ for (Entry<String, Builder> e : addedEvents.entrySet()) {
+ String path = e.getKey();
+ if (changedEvents.containsKey(path)) {
+ Builder builder = changedEvents.remove(path);
+ builder.setChangeType(ChangeType.ADDED);
+ changes.add(builder.build());
+ } else {
+ changes.add(e.getValue().build());
+ }
}
+ buildResourceChanges(changes, removedEvents);
+ buildResourceChanges(changes, changedEvents);
+ filterChanges(changes);
+ ctx.getObservationReporter().reportChanges(changes, false);
+ }
- for (final Entry<String, Map<String, Object>> e : addedEvents.entrySet()) {
- // Launch an OSGi event.
- sendOsgiEvent(e.getKey(), e.getValue(), SlingConstants.TOPIC_RESOURCE_ADDED,
- changedEvents.remove(e.getKey()));
+ private void buildResourceChanges(List<ResourceChange> result, Map<String, Builder> builders) {
+ for (Entry<String, Builder> e : builders.entrySet()) {
+ result.add(e.getValue().build());
}
+ }
- // Send the changed events.
- for (final Entry<String, ChangedAttributes> e : changedEvents.entrySet()) {
- // Launch an OSGi event.
- sendOsgiEvent(e.getKey(), e.getValue().toEventProperties(), SlingConstants.TOPIC_RESOURCE_CHANGED, null);
+ private void updateResourceChanged(Builder builder, int eventType, final String propName) {
+ switch (eventType) {
+ case Event.PROPERTY_ADDED:
+ builder.addAddedAttributeName(propName);
+ break;
+ case Event.PROPERTY_CHANGED:
+ builder.addChangedAttributeName(propName);
+ break;
+ case Event.PROPERTY_REMOVED:
+ builder.addRemovedAttributeName(propName);
+ break;
}
}
- private static final class ChangedAttributes {
-
- private final Map<String, Object> properties;
-
- public ChangedAttributes(final Map<String, Object> properties) {
- this.properties = properties;
+ private Builder createResourceChange(final Event event, final String path, final ChangeType changeType) throws RepositoryException {
+ Builder builder = new Builder();
+ String strippedPath;
+ if (event.getType() == Event.NODE_REMOVED) {
+ strippedPath = path;
+ } else {
+ strippedPath = stripNtFilePath(path, session);
}
+ String pathWithPrefix = addMountPrefix(mountPrefix, strippedPath);
+ builder.setPath(pathMapper.mapJCRPathToResourcePath(pathWithPrefix));
+ builder.setChangeType(changeType);
+ boolean isExternal = this.isExternal(event);
+ builder.setExternal(isExternal);
+ if (!isExternal) {
+ final String userID = event.getUserID();
+ if (userID != null) {
+ builder.setUserId(userID);
+ }
+ }
+ return builder;
+ }
- public Set<String> addedAttributes, changedAttributes, removedAttributes;
+ private Builder createResourceChange(final Event event, final ChangeType changeType) throws RepositoryException {
+ return createResourceChange(event, event.getPath(), changeType);
+ }
- public void addEvent(final Event event, final String propName) {
- if ( event.getType() == Event.PROPERTY_ADDED ) {
- if ( removedAttributes != null ) {
- removedAttributes.remove(propName);
- }
- if ( addedAttributes == null ) {
- addedAttributes = new HashSet<String>();
- }
- addedAttributes.add(propName);
- } else if ( event.getType() == Event.PROPERTY_REMOVED ) {
- if ( addedAttributes != null ) {
- addedAttributes.remove(propName);
- }
- if ( removedAttributes == null ) {
- removedAttributes = new HashSet<String>();
- }
- removedAttributes.add(propName);
- } else if ( event.getType() == Event.PROPERTY_CHANGED ) {
- if ( changedAttributes == null ) {
- changedAttributes = new HashSet<String>();
- }
- changedAttributes.add(propName);
- }
+ private boolean isExternal(final Event event) {
+ if ( this.hasJackrabbitEventClass && event instanceof JackrabbitEvent) {
+ final JackrabbitEvent jEvent = (JackrabbitEvent)event;
+ return jEvent.isExternal();
}
+ return false;
+ }
- /**
- * Merges lists of added, changed, and removed properties to the given
- * non-{@code null} {@code properties} and returns that object.
- *
- * @param properties The {@code Dictionary} to add the attribute lists
- * to.
- * @return The {@code properties} object is returned.
- */
- public final Map<String, Object> mergeAttributesInto(final Map<String, Object> properties) {
- if ( addedAttributes != null ) {
- properties.put(SlingConstants.PROPERTY_ADDED_ATTRIBUTES, addedAttributes.toArray(new String[addedAttributes.size()]));
- }
- if ( changedAttributes != null ) {
- properties.put(SlingConstants.PROPERTY_CHANGED_ATTRIBUTES, changedAttributes.toArray(new String[changedAttributes.size()]));
- }
- if ( removedAttributes != null ) {
- properties.put(SlingConstants.PROPERTY_REMOVED_ATTRIBUTES, removedAttributes.toArray(new String[removedAttributes.size()]));
+ static String getAbsPath(PathMapper pathMapper, ProviderContext ctx) {
+ final Set<String> paths = new HashSet<String>();
+ for (ObserverConfiguration c : ctx.getObservationReporter().getObserverConfigurations()) {
+ final Set<String> includePaths = new HashSet<String>();
+ final Set<String> excludePaths = new HashSet<String>();
+ for (Path p : c.getExcludedPaths()) {
+ excludePaths.add(pathMapper.mapResourcePathToJCRPath(p.getPath()));
+ }
+ for (Path p : c.getPaths()) {
+ includePaths.add(pathMapper.mapResourcePathToJCRPath(p.getPath()));
}
- return properties;
+ includePaths.removeAll(excludePaths);
+ paths.addAll(includePaths);
}
-
- /**
- * @return a {@code Dictionary} with all changes recorded including
- * original JCR event information.
- */
- public final Map<String, Object> toEventProperties() {
- return mergeAttributesInto(properties);
+ for (Path p : ctx.getExcludedPaths()) {
+ paths.remove(pathMapper.mapResourcePathToJCRPath(p.getPath()));
}
+ return getLongestCommonPrefix(paths);
}
- private void updateChangedEvent(final Map<String, ChangedAttributes> changedEvents, final String path,
- final Event event, final String propName) {
- ChangedAttributes storedEvent = changedEvents.get(path);
- if ( storedEvent == null ) {
- storedEvent = new ChangedAttributes(createEventProperties(event));
- changedEvents.put(path, storedEvent);
+ private static String getLongestCommonPrefix(Set<String> paths) {
+ String prefix = null;
+ Iterator<String> it = paths.iterator();
+ if (it.hasNext()) {
+ prefix = it.next();
+ }
+ while (it.hasNext()) {
+ prefix = getCommonPrefix(prefix, it.next());
}
- storedEvent.addEvent(event, propName);
+ return StringUtils.defaultIfEmpty(prefix, "/");
}
- /**
- * Create the base OSGi event properties based on the JCR event object
- */
- private Map<String, Object> createEventProperties(final Event event) {
- final Map<String, Object> properties = new HashMap<String, Object>();
-
- if (this.isExternal(event)) {
- properties.put("event.application", "unknown");
- } else {
- final String userID = event.getUserID();
- if (userID != null) {
- properties.put(SlingConstants.PROPERTY_USERID, userID);
+ private static String getCommonPrefix(String s1, String s2) {
+ int length = Math.min(s1.length(), s2.length());
+ StringBuilder prefix = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ if (s1.charAt(i) == s2.charAt(i)) {
+ prefix.append(s1.charAt(i));
+ } else {
+ break;
}
}
-
- return properties;
+ return prefix.toString();
}
- /**
- * Send an OSGi event based on a JCR Observation Event.
- *
- * @param path The path too the node where the event occurred.
- * @param properties The base properties for this event.
- * @param topic The topic that should be used for the OSGi event.
- */
- private void sendOsgiEvent(final String path,
- final Map<String, Object> properties,
- final String topic,
- final ChangedAttributes changedAttributes) {
-
- final String resourcePath = pathMapper.mapJCRPathToResourcePath(path);
- if ( resourcePath != null && !this.support.isExcluded(resourcePath)) {
- if (changedAttributes != null) {
- changedAttributes.mergeAttributesInto(properties);
- }
-
- // set the path (might have been changed for nt:file content)
- properties.put(SlingConstants.PROPERTY_PATH, resourcePath);
- properties.put(EventConstants.EVENT_TOPIC, topic);
+ private int getTypes(ProviderContext ctx) {
+ int result = 0;
+ for (ObserverConfiguration c : ctx.getObservationReporter().getObserverConfigurations()) {
+ for (ChangeType t : c.getChangeTypes()) {
+ switch (t) {
+ case ADDED:
+ result = result | Event.NODE_ADDED;
+ break;
+ case REMOVED:
+ result = result | Event.NODE_REMOVED;
+ break;
+ case CHANGED:
+ result = result | Event.PROPERTY_ADDED;
+ result = result | Event.PROPERTY_CHANGED;
+ result = result | Event.PROPERTY_REMOVED;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return result;
+ }
- // enqueue event for dispatching
- this.osgiEventQueue.offer(properties);
+ static String addMountPrefix(final String mountPrefix, final String path) {
+ final String result;
+ if (mountPrefix == null || mountPrefix.isEmpty() || "/".equals(mountPrefix)) {
+ result = path;
} else {
- logger.error("Dropping observation event for {}", path);
+ result = new StringBuilder(mountPrefix).append(path).toString();
}
+ return result;
}
- /**
- * Called by the Runnable.run method of the JCR Event Queue processor to
- * process the {@link #osgiEventQueue} until the
- * {@link #TERMINATE_PROCESSING} event is received.
- */
- void processOsgiEventQueue() {
- while (true) {
- final Map<String, Object> event;
- try {
- event = this.osgiEventQueue.take();
- } catch (InterruptedException e) {
- // interrupted waiting for the event; keep on waiting
- continue;
- }
-
- if (event == null || event == TERMINATE_PROCESSING) {
- break;
+ private void filterChanges(List<ResourceChange> changes) {
+ Iterator<ResourceChange> it = changes.iterator();
+ while (it.hasNext()) {
+ String path = it.next().getPath();
+ if (ctx.getExcludedPaths().matches(path) != null) {
+ it.remove();
}
+ }
+ }
+ static String stripNtFilePath(String path, Session session) {
+ if (!path.endsWith("/" + JcrConstants.JCR_CONTENT)) {
+ return path;
+ }
+ try {
+ Node node;
try {
- final EventAdmin localEa = this.support.getEventAdmin();
- final ResourceResolver resolver = this.support.getResourceResolver();
- if (localEa != null && resolver != null ) {
- final String topic = (String) event.remove(EventConstants.EVENT_TOPIC);
- final String path = (String) event.get(SlingConstants.PROPERTY_PATH);
- Resource resource = resolver.getResource(path);
- boolean sendEvent = true;
- if (!SlingConstants.TOPIC_RESOURCE_REMOVED.equals(topic)) {
- if (resource != null) {
- // check if this is a JCR backed resource, otherwise it is not visible!
- final Node node = resource.adaptTo(Node.class);
- if (node != null) {
- // check for nt:file nodes
- if (path.endsWith("/jcr:content")) {
- try {
- if (node.getParent().isNodeType("nt:file")) {
- final Resource parentResource = resource.getParent();
- if (parentResource != null) {
- resource = parentResource;
- event.put(SlingConstants.PROPERTY_PATH, resource.getPath());
- }
- }
- } catch (final RepositoryException re) {
- // ignore this
- }
- }
-
- final String resourceType = resource.getResourceType();
- if (resourceType != null) {
- event.put(SlingConstants.PROPERTY_RESOURCE_TYPE, resource.getResourceType());
- }
- final String resourceSuperType = resource.getResourceSuperType();
- if (resourceSuperType != null) {
- event.put(SlingConstants.PROPERTY_RESOURCE_SUPER_TYPE, resource.getResourceSuperType());
- }
- } else {
- // this is not a jcr backed resource
- sendEvent = false;
- }
-
- } else {
- // take a quite silent note of not being able to
- // resolve the resource
- logger.debug(
- "processOsgiEventQueue: Resource at {} not found, which is not expected for an added or modified node",
- path);
- sendEvent = false;
- }
- }
-
- if ( sendEvent ) {
- localEa.sendEvent(new org.osgi.service.event.Event(topic, new EventProperties(event)));
- }
- }
- } catch (final Exception e) {
- logger.warn("processOsgiEventQueue: Unexpected problem processing event " + event, e);
+ node = session.getNode(path);
+ } catch(PathNotFoundException e) {
+ session.refresh(false);
+ node = session.getNode(path);
+ }
+ Node parent = node.getParent();
+ if (parent.isNodeType(JcrConstants.NT_FILE)) {
+ return parent.getPath();
+ } else {
+ return path;
}
+ } catch (RepositoryException e) {
+ return path;
}
-
- this.osgiEventQueue.clear();
}
- private boolean isExternal(final Event event) {
- if ( this.hasJackrabbitEventClass && event instanceof JackrabbitEvent) {
- final JackrabbitEvent jEvent = (JackrabbitEvent)event;
- return jEvent.isExternal();
- }
- return false;
- }
}
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/OakResourceListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/OakResourceListener.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/OakResourceListener.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/OakResourceListener.java Thu Jan 28 13:41:32 2016
@@ -18,48 +18,38 @@
*/
package org.apache.sling.jcr.resource.internal;
-import static org.apache.sling.api.SlingConstants.PROPERTY_ADDED_ATTRIBUTES;
-import static org.apache.sling.api.SlingConstants.PROPERTY_CHANGED_ATTRIBUTES;
-import static org.apache.sling.api.SlingConstants.PROPERTY_PATH;
-import static org.apache.sling.api.SlingConstants.PROPERTY_REMOVED_ATTRIBUTES;
-import static org.apache.sling.api.SlingConstants.PROPERTY_RESOURCE_SUPER_TYPE;
-import static org.apache.sling.api.SlingConstants.PROPERTY_RESOURCE_TYPE;
-import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
-import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
-import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
-
import java.io.Closeable;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Dictionary;
-import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
-import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
import org.apache.jackrabbit.oak.plugins.observation.NodeObserver;
import org.apache.jackrabbit.oak.spi.commit.BackgroundObserver;
import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.Observer;
-import org.apache.sling.api.SlingConstants;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.resource.internal.JcrResourceChange.Builder;
import org.apache.sling.jcr.resource.internal.helper.jcr.PathMapper;
+import org.apache.sling.spi.resource.provider.ObservationReporter;
+import org.apache.sling.spi.resource.provider.ProviderContext;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.event.EventProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This {@code OakResourceListener} implementation translates and relays
- * all events to the OSGi {@code EventAdmin}.
+ * all events to an {@link ObservationReporter}
*/
public class OakResourceListener extends NodeObserver implements Closeable {
@@ -72,23 +62,27 @@ public class OakResourceListener extends
private final ServiceRegistration mbeanRegistration;
- /** Helper object. */
- final ObservationListenerSupport support;
-
private final PathMapper pathMapper;
+ private final ProviderContext ctx;
+
+ private final Session session;
+
+ @SuppressWarnings("deprecation")
public OakResourceListener(
final String mountPrefix,
- final ObservationListenerSupport support,
+ final ProviderContext ctx,
final BundleContext bundleContext,
final Executor executor,
final PathMapper pathMapper,
- final int observationQueueLength)
+ final int observationQueueLength,
+ final SlingRepository repository)
throws RepositoryException {
- super("/", "jcr:primaryType", "sling:resourceType", "sling:resourceSuperType");
- this.support = support;
+ super(JcrResourceListener.getAbsPath(pathMapper, ctx), "jcr:primaryType", "sling:resourceType", "sling:resourceSuperType");
+ this.ctx = ctx;
this.pathMapper = pathMapper;
- this.mountPrefix = (mountPrefix == null || mountPrefix.length() == 0 || mountPrefix.equals("/") ? null : mountPrefix);
+ this.mountPrefix = mountPrefix;
+ this.session = repository.loginAdministrative(repository.getDefaultWorkspace());
final Hashtable<String, Object> props = new Hashtable<String, Object>();
props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
@@ -120,7 +114,7 @@ public class OakResourceListener extends
public void close() throws IOException {
mbeanRegistration.unregister();
serviceRegistration.unregister();
- this.support.dispose();
+ session.logout();
}
@Override
@@ -130,12 +124,15 @@ public class OakResourceListener extends
final Set<String> changed,
final Map<String, String> properties,
final CommitInfo commitInfo) {
- final Map<String, Object> changes = toEventProperties(added, deleted, changed);
- addCommitInfo(changes, commitInfo);
+ final Builder builder = toEventProperties(JcrResourceListener.stripNtFilePath(path, session), added, deleted, changed, commitInfo);
+ if (ctx.getExcludedPaths().matches(builder.getPath()) != null) {
+ return;
+ }
+ builder.setChangeType(ChangeType.ADDED);
if ( logger.isDebugEnabled() ) {
logger.debug("added(path={}, added={}, deleted={}, changed={})", new Object[] {path, added, deleted, changed});
}
- sendOsgiEvent(path, TOPIC_RESOURCE_ADDED, changes, properties);
+ ctx.getObservationReporter().reportChanges(Arrays.asList(builder.build()), false);
}
@Override
@@ -145,12 +142,15 @@ public class OakResourceListener extends
final Set<String> changed,
final Map<String, String> properties,
final CommitInfo commitInfo) {
- final Map<String, Object> changes = toEventProperties(added, deleted, changed);
- addCommitInfo(changes, commitInfo);
+ final Builder builder = toEventProperties(path, added, deleted, changed, commitInfo);
+ if (ctx.getExcludedPaths().matches(builder.getPath()) != null) {
+ return;
+ }
+ builder.setChangeType(ChangeType.REMOVED);
if ( logger.isDebugEnabled() ) {
logger.debug("deleted(path={}, added={}, deleted={}, changed={})", new Object[] {path, added, deleted, changed});
}
- sendOsgiEvent(path, TOPIC_RESOURCE_REMOVED, changes, properties);
+ ctx.getObservationReporter().reportChanges(Arrays.asList(builder.build()), false);
}
@Override
@@ -160,133 +160,38 @@ public class OakResourceListener extends
final Set<String> changed,
final Map<String, String> properties,
final CommitInfo commitInfo) {
- final Map<String, Object> changes = toEventProperties(added, deleted, changed);
- addCommitInfo(changes, commitInfo);
+ final Builder builder = toEventProperties(JcrResourceListener.stripNtFilePath(path, session), added, deleted, changed, commitInfo);
+ if (ctx.getExcludedPaths().matches(builder.getPath()) != null) {
+ return;
+ }
+ builder.setChangeType(ChangeType.CHANGED);
if ( logger.isDebugEnabled() ) {
logger.debug("changed(path={}, added={}, deleted={}, changed={})", new Object[] {path, added, deleted, changed});
}
- sendOsgiEvent(path, TOPIC_RESOURCE_CHANGED, changes, properties);
- }
-
- private static void addCommitInfo(final Map<String, Object> changes, final CommitInfo commitInfo) {
- if ( commitInfo.getUserId() != null ) {
- changes.put(SlingConstants.PROPERTY_USERID, commitInfo.getUserId());
- }
- if (commitInfo == CommitInfo.EMPTY) {
- changes.put("event.application", "unknown");
- }
+ ctx.getObservationReporter().reportChanges(Arrays.asList(builder.build()), false);
}
- private static Map<String, Object> toEventProperties(final Set<String> added, final Set<String> deleted, final Set<String> changed) {
- final Map<String, Object> properties = new HashMap<String, Object>();
+ private Builder toEventProperties(final String path, final Set<String> added, final Set<String> deleted, final Set<String> changed, final CommitInfo commitInfo) {
+ Builder builder = new Builder();
+ String pathWithPrefix = JcrResourceListener.addMountPrefix(mountPrefix, path);
+ builder.setPath(pathMapper.mapJCRPathToResourcePath(pathWithPrefix));
if ( added != null && added.size() > 0 ) {
- properties.put(PROPERTY_ADDED_ATTRIBUTES, added.toArray(new String[added.size()]));
+ for (String propName : added) {
+ builder.addAddedAttributeName(propName);
+ }
}
if ( changed != null && changed.size() > 0 ) {
- properties.put(PROPERTY_CHANGED_ATTRIBUTES, changed.toArray(new String[changed.size()]));
+ for (String propName : changed) {
+ builder.addChangedAttributeName(propName);
+ }
}
if ( deleted != null && deleted.size() > 0 ) {
- properties.put(PROPERTY_REMOVED_ATTRIBUTES, deleted.toArray(new String[deleted.size()]));
- }
- return properties;
- }
-
- private void sendOsgiEvent(final String path,
- final String topic,
- final Map<String, Object> changes,
- final Map<String, String> properties) {
- // set the path (will be changed for nt:file jcr:content sub resource)
- final String changePath;
- if ( this.mountPrefix == null ) {
- changePath = path;
- } else {
- changePath = this.mountPrefix + path;
- }
- changes.put(PROPERTY_PATH, changePath);
-
- try {
- final EventAdmin localEa = this.support.getEventAdmin();
- if (localEa != null ) {
- boolean sendEvent = true;
- if (!TOPIC_RESOURCE_REMOVED.equals(topic)) {
- String resourceType = properties.get("sling:resourceType");
- String resourceSuperType = properties.get("sling:resourceSuperType");
- String nodeType = properties.get("jcr:primaryType");
-
- // check for nt:file nodes
- if (path.endsWith("/jcr:content")) {
- final ResourceResolver resolver = this.support.getResourceResolver();
- if ( resolver == null ) {
- sendEvent = false;
- logger.debug("resource resolver is null");
- } else {
- final Resource rsrc = resolver.getResource(changePath);
- if ( rsrc == null ) {
- resolver.refresh();
- sendEvent = false;
- logger.debug("not able to get resource for changes path {}", changePath);
- } else {
- // check if this is a JCR backed resource, otherwise it is not visible!
- final Node node = rsrc.adaptTo(Node.class);
- if (node != null) {
- try {
- if (node.getParent().isNodeType("nt:file")) {
- final Resource parentResource = rsrc.getParent();
- if (parentResource != null) {
- // update resource type and path to parent node
- resourceType = parentResource.getResourceType();
- resourceSuperType = parentResource.getResourceSuperType();
- changes.put(PROPERTY_PATH, parentResource.getPath());
- }
- }
- } catch (RepositoryException re) {
- // ignore this
- logger.error(re.getMessage(), re);
- }
-
- } else {
- // this is not a jcr backed resource
- sendEvent = false;
- logger.debug("not able to adapt resource {} to node", changePath);
- }
-
- }
- }
- if ( !sendEvent ) {
- // take a quite silent note of not being able to
- // resolve the resource
- logger.debug(
- "processOsgiEventQueue: Resource at {} not found, which is not expected for an added or modified node",
- changePath);
- }
- }
-
- // update resource type properties
- if ( sendEvent ) {
- if ( resourceType == null ) {
- changes.put(PROPERTY_RESOURCE_TYPE, nodeType);
- } else {
- changes.put(PROPERTY_RESOURCE_TYPE, resourceType);
- }
- if ( resourceSuperType != null ) {
- changes.put(PROPERTY_RESOURCE_SUPER_TYPE, resourceSuperType);
- }
- }
- }
-
- if ( sendEvent ) {
- final String resourcePath = pathMapper.mapJCRPathToResourcePath(changes.get(SlingConstants.PROPERTY_PATH).toString());
- if ( resourcePath != null && !this.support.isExcluded(resourcePath)) {
- changes.put(SlingConstants.PROPERTY_PATH, resourcePath);
-
- localEa.sendEvent(new org.osgi.service.event.Event(topic, new EventProperties(changes)));
- } else {
- logger.debug("Dropping observation event for {}", changes.get(SlingConstants.PROPERTY_PATH));
- }
- }
+ for (String propName : deleted) {
+ builder.addRemovedAttributeName(propName);
}
- } catch (final Exception e) {
- logger.warn("sendOsgiEvent: Unexpected problem processing event " + topic + " at " + path + " with " + changes, e);
}
+ builder.setUserId(commitInfo.getUserId());
+ builder.setExternal(false);
+ return builder;
}
}
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java Thu Jan 28 13:41:32 2016
@@ -64,7 +64,6 @@ import org.apache.sling.jcr.resource.int
import org.apache.sling.jcr.resource.internal.JcrResourceListener;
import org.apache.sling.jcr.resource.internal.NodeUtil;
import org.apache.sling.jcr.resource.internal.OakResourceListener;
-import org.apache.sling.jcr.resource.internal.ObservationListenerSupport;
import org.apache.sling.spi.resource.provider.QueryLanguageProvider;
import org.apache.sling.spi.resource.provider.ProviderContext;
import org.apache.sling.spi.resource.provider.ResolverContext;
@@ -169,22 +168,27 @@ public class JcrResourceProvider extends
@Deactivate
protected void deactivate() {
- unregisterLegacyListener();
}
-
@Override
public void start(final ProviderContext ctx) {
super.start(ctx);
- registerLegacyListener();
+ registerListener(ctx);
}
@Override
public void stop() {
- unregisterLegacyListener();
+ unregisterListener();
super.stop();
}
+ @Override
+ public void update(long changeSet) {
+ super.update(changeSet);
+ unregisterListener();
+ registerListener(getProviderContext());
+ }
+
@SuppressWarnings("unused")
private void bindRepository(final ServiceReference ref) {
this.repositoryReference = ref;
@@ -199,7 +203,7 @@ public class JcrResourceProvider extends
}
}
- private void registerLegacyListener() {
+ private void registerListener(ProviderContext ctx) {
// check for Oak
boolean isOak = false;
if ( optimizeForOak ) {
@@ -212,13 +216,10 @@ public class JcrResourceProvider extends
}
}
}
- ObservationListenerSupport support = null;
- boolean closeSupport = true;
try {
- support = new ObservationListenerSupport(bundleCtx, repository, this.getProviderContext().getExcludedPaths());
if (isOak) {
try {
- this.listener = new OakResourceListener(root, support, bundleCtx, executor, pathMapper, observationQueueLength);
+ this.listener = new OakResourceListener(root, ctx, bundleCtx, executor, pathMapper, observationQueueLength, repository);
log.info("Detected Oak based repository. Using improved JCR Resource Listener with observation queue length {}", observationQueueLength);
} catch ( final RepositoryException re ) {
throw new SlingException("Can't create the OakResourceListener", re);
@@ -227,19 +228,14 @@ public class JcrResourceProvider extends
}
}
if (this.listener == null) {
- this.listener = new JcrResourceListener(root, support, pathMapper);
+ this.listener = new JcrResourceListener(ctx, root, pathMapper, repository);
}
- closeSupport = false;
} catch (RepositoryException e) {
throw new SlingException("Can't create the listener", e);
- } finally {
- if ( closeSupport && support != null ) {
- support.dispose();
- }
}
}
- private void unregisterLegacyListener() {
+ private void unregisterListener() {
if ( this.listener != null ) {
try {
this.listener.close();
Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/AbstractListenerTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/AbstractListenerTest.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/AbstractListenerTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/AbstractListenerTest.java Thu Jan 28 13:41:32 2016
@@ -23,9 +23,9 @@ import static org.junit.Assert.assertTru
import static org.junit.Assert.fail;
import java.util.ArrayList;
-import java.util.Dictionary;
+import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashSet;
-import java.util.Hashtable;
import java.util.List;
import java.util.Set;
@@ -33,10 +33,13 @@ import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
-import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.api.resource.util.PathSet;
import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.spi.resource.provider.ObservationReporter;
+import org.apache.sling.spi.resource.provider.ObserverConfiguration;
import org.junit.Test;
-import org.osgi.service.event.Event;
/**
* Base for the two different listener tests.
@@ -49,21 +52,7 @@ public abstract class AbstractListenerTe
private String pathToModify = "/test" + System.currentTimeMillis() + "-modify";
- private final List<Event> events = synchronizedList(new ArrayList<Event>());
-
- protected void addEvent(final Event e) {
- final Dictionary<String, Object> props = new Hashtable<String, Object>();
- for(final String name : e.getPropertyNames()) {
- props.put(name, e.getProperty(name));
- }
- this.events.add(new Event(e.getTopic(), props) {
-
- @Override
- public String toString() {
- return "Event(topic=" + e.getTopic() + ", properties=" + props + ")";
- }
- });
- }
+ private final List<ResourceChange> events = synchronizedList(new ArrayList<ResourceChange>());
@Test public void testSimpleOperations() throws Exception {
generateEvents();
@@ -73,17 +62,18 @@ public abstract class AbstractListenerTe
final Set<String> modifyPaths = new HashSet<String>();
final Set<String> removePaths = new HashSet<String>();
- for(final Event event : events) {
- if ( event.getTopic().equals(SlingConstants.TOPIC_RESOURCE_ADDED) ) {
- addPaths.add((String)event.getProperty(SlingConstants.PROPERTY_PATH));
- } else if ( event.getTopic().equals(SlingConstants.TOPIC_RESOURCE_CHANGED) ) {
- modifyPaths.add((String)event.getProperty(SlingConstants.PROPERTY_PATH));
- } else if ( event.getTopic().equals(SlingConstants.TOPIC_RESOURCE_REMOVED) ) {
- removePaths.add((String)event.getProperty(SlingConstants.PROPERTY_PATH));
+ for (final ResourceChange event : events) {
+ if (event.getType() == ChangeType.ADDED) {
+ addPaths.add(event.getPath());
+ } else if (event.getType() == ChangeType.CHANGED) {
+ modifyPaths.add(event.getPath());
+ assertEquals(Collections.singleton("foo"), event.getAddedPropertyNames());
+ } else if (event.getType() == ChangeType.REMOVED) {
+ removePaths.add(event.getPath());
} else {
fail("Unexpected event: " + event);
}
- assertNotNull(event.getProperty(SlingConstants.PROPERTY_USERID));
+ assertNotNull(event.getUserId());
}
assertEquals(3, addPaths.size());
assertTrue("Added set should contain " + createdPath, addPaths.contains(createdPath));
@@ -103,6 +93,7 @@ public abstract class AbstractListenerTe
}
private void generateEvents() throws Exception {
+ @SuppressWarnings("deprecation")
final Session session = getRepository().loginAdministrative(null);
try {
@@ -132,4 +123,51 @@ public abstract class AbstractListenerTe
}
public abstract SlingRepository getRepository();
+
+ protected ObservationReporter getObservationReporter() {
+ return new SimpleObservationReporter();
+ }
+
+ private class SimpleObservationReporter implements ObservationReporter {
+
+ @Override
+ public void reportChanges(Iterable<ResourceChange> changes, boolean distribute) {
+ for (ResourceChange c : changes) {
+ events.add(c);
+ }
+ }
+
+ @Override
+ public List<ObserverConfiguration> getObserverConfigurations() {
+ ObserverConfiguration config = new ObserverConfiguration() {
+
+ @Override
+ public boolean includeExternal() {
+ return true;
+ }
+
+ @Override
+ public PathSet getPaths() {
+ return PathSet.fromStrings("/");
+ }
+
+ @Override
+ public PathSet getExcludedPaths() {
+ return PathSet.fromPaths();
+ }
+
+ @Override
+ public Set<ChangeType> getChangeTypes() {
+ return EnumSet.allOf(ChangeType.class);
+ }
+
+ @Override
+ public boolean matches(String path) {
+ return true;
+ }
+ };
+ return Collections.singletonList(config);
+ }
+ }
+
}
Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerScalabilityTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerScalabilityTest.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerScalabilityTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerScalabilityTest.java Thu Jan 28 13:41:32 2016
@@ -16,10 +16,14 @@
*/
package org.apache.sling.jcr.resource.internal;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
@@ -27,14 +31,18 @@ import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.ObservationManager;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.api.resource.util.PathSet;
+import org.apache.sling.commons.testing.jcr.RepositoryUtil;
import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.spi.resource.provider.ObservationReporter;
+import org.apache.sling.spi.resource.provider.ObserverConfiguration;
+import org.apache.sling.spi.resource.provider.ProviderContext;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.event.EventAdmin;
/**
* This test case asserts that JcrResourceListener scales to an
@@ -45,6 +53,7 @@ public class JcrResourceListenerScalabil
private JcrResourceListener jcrResourceListener;
private EventIterator events;
+ @SuppressWarnings("deprecation")
@Before
public void setUp() throws RepositoryException, InvalidSyntaxException {
ObservationManager observationManager = mock(ObservationManager.class);
@@ -58,14 +67,7 @@ public class JcrResourceListenerScalabil
SlingRepository repository = mock(SlingRepository.class);
when(repository.loginAdministrative(null)).thenReturn(session);
- EventAdmin eventAdmin = mock(EventAdmin.class);
- ServiceReference serviceRef = mock(ServiceReference.class);
- ServiceReference[] serviceRefs = new ServiceReference[]{serviceRef};
- BundleContext bundleContext = mock(BundleContext.class);
- when(bundleContext.getServiceReferences(anyString(), anyString())).thenReturn(serviceRefs);
- when(bundleContext.getService(serviceRef)).thenReturn(eventAdmin);
-
- jcrResourceListener = new JcrResourceListener("/", new ObservationListenerSupport(bundleContext, repository, null), new PathMapperImpl());
+ jcrResourceListener = new JcrResourceListener(new SimpleProviderContext(), "/", new PathMapperImpl(), RepositoryUtil.getRepository());
Event event = mock(MockEvent.class);
events = mock(EventIterator.class);
@@ -89,4 +91,53 @@ public class JcrResourceListenerScalabil
return "path-" + count++;
}
}
+
+ private static class SimpleProviderContext implements ProviderContext {
+ @Override
+ public ObservationReporter getObservationReporter() {
+ return new ObservationReporter() {
+
+ @Override
+ public void reportChanges(Iterable<ResourceChange> changes, boolean distribute) {
+ }
+
+ @Override
+ public List<ObserverConfiguration> getObserverConfigurations() {
+ ObserverConfiguration config = new ObserverConfiguration() {
+
+ @Override
+ public boolean includeExternal() {
+ return true;
+ }
+
+ @Override
+ public PathSet getPaths() {
+ return PathSet.fromStrings("/");
+ }
+
+ @Override
+ public PathSet getExcludedPaths() {
+ return PathSet.fromPaths();
+ }
+
+ @Override
+ public Set<ChangeType> getChangeTypes() {
+ return EnumSet.allOf(ChangeType.class);
+ }
+
+ @Override
+ public boolean matches(String path) {
+ return true;
+ }
+ };
+ return Collections.singletonList(config);
+ }
+ };
+ }
+
+ @Override
+ public PathSet getExcludedPaths() {
+ return PathSet.fromPaths();
+ }
+ }
}
Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java Thu Jan 28 13:41:32 2016
@@ -16,31 +16,22 @@
*/
package org.apache.sling.jcr.resource.internal;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
import javax.jcr.Session;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.util.PathSet;
import org.apache.sling.commons.testing.jcr.RepositoryUtil;
import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.resource.internal.helper.jcr.JcrTestNodeResource;
+import org.apache.sling.spi.resource.provider.ObservationReporter;
+import org.apache.sling.spi.resource.provider.ProviderContext;
import org.junit.After;
import org.junit.Before;
-import org.mockito.Mockito;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.util.tracker.ServiceTracker;
/**
* Test of JcrResourceListener.
*/
public class JcrResourceListenerTest extends AbstractListenerTest {
- private SynchronousJcrResourceListener listener;
+ private JcrResourceListener listener;
private Session adminSession;
@@ -52,44 +43,28 @@ public class JcrResourceListenerTest ext
}
RepositoryUtil.stopRepository();
if ( listener != null ) {
- listener.dispose();
+ listener.close();
listener = null;
}
}
+ @SuppressWarnings("deprecation")
@Before
public void setUp() throws Exception {
RepositoryUtil.startRepository();
this.adminSession = RepositoryUtil.getRepository().loginAdministrative(null);
RepositoryUtil.registerSlingNodeTypes(adminSession);
- final ResourceResolver resolver = Mockito.mock(ResourceResolver.class);
- Mockito.when(resolver.adaptTo(Mockito.any(Class.class))).thenReturn(this.adminSession);
- Mockito.when(resolver.getResource(Mockito.anyString())).thenReturn(new JcrTestNodeResource(resolver, this.adminSession.getNode("/"), null));
-
- final ResourceResolverFactory factory = Mockito.mock(ResourceResolverFactory.class);
- Mockito.when(factory.getAdministrativeResourceResolver(Mockito.anyMap())).thenReturn(resolver);
-
- final EventAdmin mockEA = new EventAdmin() {
-
- public void postEvent(final Event event) {
- addEvent(event);
+ this.listener = new JcrResourceListener(new ProviderContext() {
+ @Override
+ public ObservationReporter getObservationReporter() {
+ return JcrResourceListenerTest.this.getObservationReporter();
}
- public void sendEvent(final Event event) {
- addEvent(event);
+ @Override
+ public PathSet getExcludedPaths() {
+ return PathSet.fromPaths();
}
- };
-
- final ServiceTracker tracker = mock(ServiceTracker.class);
- when(tracker.getService()).thenReturn(mockEA);
-
- final BundleContext bundleContext = mock(BundleContext.class);
- when(bundleContext.createFilter(any(String.class))).thenReturn(null);
- when(bundleContext.getServiceReference(any(String.class))).thenReturn(null);
- when(bundleContext.getService(null)).thenReturn(mockEA);
-
- this.listener = new SynchronousJcrResourceListener(RepositoryUtil.getRepository(),
- bundleContext, resolver, tracker);
+ }, "/", new PathMapperImpl(), RepositoryUtil.getRepository());
}
@Override
Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/OakResourceListenerTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/OakResourceListenerTest.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/OakResourceListenerTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/OakResourceListenerTest.java Thu Jan 28 13:41:32 2016
@@ -17,11 +17,7 @@
package org.apache.sling.jcr.resource.internal;
import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerObserver;
-import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyMap;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -30,37 +26,30 @@ import java.util.concurrent.ExecutorServ
import java.util.concurrent.Executors;
import javax.jcr.Repository;
-import javax.jcr.Session;
import org.apache.jackrabbit.oak.Oak;
import org.apache.jackrabbit.oak.jcr.Jcr;
import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
-import org.apache.jackrabbit.oak.spi.commit.BackgroundObserverMBean;
import org.apache.jackrabbit.oak.spi.commit.Observer;
import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.util.PathSet;
import org.apache.sling.commons.testing.jcr.RepositoryUtil.RepositoryWrapper;
import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.resource.internal.helper.jcr.JcrTestNodeResource;
+import org.apache.sling.spi.resource.provider.ObservationReporter;
+import org.apache.sling.spi.resource.provider.ProviderContext;
import org.junit.After;
import org.junit.Before;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
/**
* Test of OakResourceListener.
*/
public class OakResourceListenerTest extends AbstractListenerTest {
- private Session session;
- private SynchronousOakResourceListener listener;
+ private OakResourceListener listener;
private ExecutorService executor;
private Whiteboard whiteboard;
private SlingRepository slingRepository;
@@ -73,59 +62,33 @@ public class OakResourceListenerTest ext
final Repository repository = new Jcr(oak).createRepository();
this.slingRepository = new RepositoryWrapper(repository);
- session = this.slingRepository.loginAdministrative(null);
-
- ResourceResolver resolver = mock(ResourceResolver.class);
- when(resolver.adaptTo(any(Class.class))).thenReturn(session);
- when(resolver.getResource(anyString())).thenReturn(new JcrTestNodeResource(resolver, session.getNode("/"), null));
-
- ResourceResolverFactory factory = mock(ResourceResolverFactory.class);
- when(factory.getAdministrativeResourceResolver(anyMap())).thenReturn(resolver);
-
- EventAdmin mockEA = new EventAdmin() {
- public void postEvent(Event event) {
- addEvent(event);
- }
-
- public void sendEvent(Event event) {
- addEvent(event);
- }
- };
-
- ServiceTracker tracker = mock(ServiceTracker.class);
- when(tracker.getService()).thenReturn(mockEA);
-
BundleContext bundleContext = mock(BundleContext.class);
- when(bundleContext.createFilter(any(String.class))).thenReturn(null);
- when(bundleContext.getServiceReference(any(String.class))).thenReturn(null);
- when(bundleContext.getService(null)).thenReturn(mockEA);
- when(bundleContext.registerService(eq(Observer.class.getName()), any(Object.class), any(Dictionary.class)))
- .thenAnswer(new Answer<ServiceRegistration>() {
- public ServiceRegistration answer(InvocationOnMock invocation) throws Throwable {
- Object[] arguments = invocation.getArguments();
- registerObserver(whiteboard, (Observer) arguments[1]);
- return mock(ServiceRegistration.class);
- }
- });
-
- when(bundleContext.registerService(eq(BackgroundObserverMBean.class.getName()), any(Object.class), any
- (Dictionary.class)))
+ when(bundleContext.registerService(any(String.class), any(Object.class), any(Dictionary.class)))
.thenAnswer(new Answer<ServiceRegistration>() {
public ServiceRegistration answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
- assertNotNull(((Dictionary)arguments[2]).get("jmx.objectname"));
+ if (arguments[1] instanceof Observer) {
+ registerObserver(whiteboard, (Observer) arguments[1]);
+ }
return mock(ServiceRegistration.class);
}
});
+ listener = new OakResourceListener("/", new ProviderContext() {
+ @Override
+ public ObservationReporter getObservationReporter() {
+ return OakResourceListenerTest.this.getObservationReporter();
+ }
- listener = new SynchronousOakResourceListener(
- this.slingRepository, bundleContext, resolver, tracker, executor);
+ @Override
+ public PathSet getExcludedPaths() {
+ return PathSet.fromPaths();
+ }
+ }, bundleContext, executor, new PathMapperImpl(), 1000, slingRepository);
}
@After
public void tearDown() throws Exception {
- listener.dispose();
- session.logout();
+ listener.close();
executor.shutdown();
}
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java Thu Jan 28 13:41:32 2016
@@ -46,10 +46,11 @@ import org.osgi.service.event.EventAdmin
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@Component(policy = ConfigurationPolicy.REQUIRE)
+@Component(policy = ConfigurationPolicy.REQUIRE, metatype = true,
+ label="Apache Sling OSGi Observation Bridge", description="Legacy bridge which converts resource change events to OSGi events")
@Service(ResourceChangeListener.class)
@Properties({ @Property(name = ResourceChangeListener.CHANGES, value = { "ADDED", "CHANGED", "REMOVED" }),
- @Property(name = ResourceChangeListener.PATHS, value = ".") })
+ @Property(name = ResourceChangeListener.PATHS, value = "/") })
public class OsgiObservationBridge implements ResourceChangeListener, ExternalResourceChangeListener {
private static final Logger logger = LoggerFactory.getLogger(OsgiObservationBridge.class);
Modified: sling/trunk/launchpad/builder/src/main/provisioning/sling.txt
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/builder/src/main/provisioning/sling.txt?rev=1727345&r1=1727344&r2=1727345&view=diff
==============================================================================
--- sling/trunk/launchpad/builder/src/main/provisioning/sling.txt (original)
+++ sling/trunk/launchpad/builder/src/main/provisioning/sling.txt Thu Jan 28 13:41:32 2016
@@ -168,3 +168,5 @@
org.apache.sling.jcr.davex.impl.servlets.SlingDavExServlet
alias="/server"
+ org.apache.sling.resourceresolver.impl.observation.OsgiObservationBridge
+ enabled=B"true"