You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2009/06/30 14:45:31 UTC

svn commit: r789697 - in /sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal: JcrResourceListener.java JcrResourceResolverFactoryImpl.java

Author: cziegeler
Date: Tue Jun 30 12:45:31 2009
New Revision: 789697

URL: http://svn.apache.org/viewvc?rev=789697&view=rev
Log:
SLING-944 : Add resource listener that fires events for jcr resources, fire events for new resource providers and when they are removed.

Added:
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java   (with props)
Modified:
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java

Added: 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=789697&view=auto
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java (added)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java Tue Jun 30 12:45:31 2009
@@ -0,0 +1,162 @@
+/*
+ * 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.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+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.sling.api.SlingConstants;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.resource.JcrResourceResolverFactory;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JcrResourceListener implements EventListener {
+
+    /** Logger */
+    private static final Logger LOGGER = LoggerFactory.getLogger(JcrResourceListener.class);
+
+    /** The session for observation. */
+    private final Session session;
+
+    /** Everything below this path is observed. */
+    private final String startPath;
+
+    /** The repository is mounted under this path. */
+    private final String mountPrefix;
+
+    /** The resource resolver. */
+    private final ResourceResolver resolver;
+
+    /** The event admin tracker. */
+    private ServiceTracker eventAdminTracker;
+
+    public JcrResourceListener(final SlingRepository repository,
+                               final JcrResourceResolverFactory factory,
+                               final String startPath,
+                               final String mountPrefix,
+                               final ServiceTracker eventAdminTracker)
+    throws RepositoryException {
+        this.session = repository.loginAdministrative(null);
+        this.resolver = factory.getResourceResolver(this.session);
+        this.startPath = startPath;
+        this.eventAdminTracker = eventAdminTracker;
+        this.mountPrefix = (mountPrefix.equals("/") ? null : mountPrefix);
+        this.session.getWorkspace().getObservationManager().addEventListener(this,
+            Event.NODE_ADDED|Event.NODE_REMOVED|Event.PROPERTY_ADDED|Event.PROPERTY_CHANGED|Event.PROPERTY_REMOVED,
+            this.startPath, true, null, null, false);
+    }
+
+    public void dispose() {
+        try {
+            this.session.getWorkspace().getObservationManager().removeEventListener(this);
+        } catch (RepositoryException e) {
+            LOGGER.warn("Unable to remove session listener: " + this, e);
+        }
+        this.session.logout();
+    }
+
+    /**
+     * @see javax.jcr.observation.EventListener#onEvent(javax.jcr.observation.EventIterator)
+     */
+    public void onEvent(EventIterator events) {
+        final EventAdmin localEA = (EventAdmin) this.eventAdminTracker.getService();
+        if ( localEA == null ) {
+            return;
+        }
+        final Set<String>addedPaths = new HashSet<String>();
+        final Set<String>removedPaths = new HashSet<String>();
+        final Set<String>changedPaths = new HashSet<String>();
+        while ( events.hasNext() ) {
+            final Event event = events.nextEvent();
+            try {
+                Set<String> set = null;
+                String nodePath = event.getPath();
+                if ( event.getType() == Event.PROPERTY_ADDED
+                     || event.getType() == Event.PROPERTY_REMOVED
+                     || event.getType() == Event.PROPERTY_CHANGED ) {
+                    final int lastSlash = nodePath.lastIndexOf('/');
+                    nodePath = nodePath.substring(0, lastSlash);
+                    set = changedPaths;
+                } else if ( event.getType() == Event.NODE_ADDED ) {
+                    set = addedPaths;
+                } else if ( event.getType() == Event.NODE_REMOVED) {
+                    set = removedPaths;
+                }
+                if ( set != null ) {
+                    if ( this.mountPrefix != null ) {
+                        set.add(this.mountPrefix + nodePath);
+                    } else {
+                        set.add(nodePath);
+                    }
+                }
+            } catch (RepositoryException e) {
+                LOGGER.error("Error during modification: {}", e.getMessage());
+            }
+        }
+        // remove is the strongest oberation, therefore remove all removed
+        // paths from changed and added
+        addedPaths.removeAll(removedPaths);
+        changedPaths.removeAll(removedPaths);
+        // add is stronger than changed
+        changedPaths.removeAll(addedPaths);
+
+        // send events
+        for(final String path : addedPaths) {
+            final Resource resource = this.resolver.getResource(path);
+            if ( resource != null ) {
+                final Dictionary<String, String> properties = new Hashtable<String, String>();
+                properties.put(SlingConstants.PROPERTY_PATH, resource.getPath());
+                properties.put(SlingConstants.PROPERTY_RESOURCE_TYPE, resource.getResourceType());
+                properties.put(SlingConstants.PROPERTY_RESOURCE_SUPER_TYPE, resource.getResourceSuperType());
+
+                localEA.postEvent(new org.osgi.service.event.Event(SlingConstants.TOPIC_RESOURCE_ADDED, properties));
+            }
+        }
+        for(final String path : changedPaths) {
+            final Resource resource = this.resolver.getResource(path);
+            if ( resource != null ) {
+                final Dictionary<String, String> properties = new Hashtable<String, String>();
+                properties.put(SlingConstants.PROPERTY_PATH, resource.getPath());
+                properties.put(SlingConstants.PROPERTY_RESOURCE_TYPE, resource.getResourceType());
+                properties.put(SlingConstants.PROPERTY_RESOURCE_SUPER_TYPE, resource.getResourceSuperType());
+
+                localEA.postEvent(new org.osgi.service.event.Event(SlingConstants.TOPIC_RESOURCE_CHANGED, properties));
+            }
+        }
+        for(final String path : removedPaths) {
+            final Dictionary<String, String> properties = new Hashtable<String, String>();
+            properties.put(SlingConstants.PROPERTY_PATH, path);
+
+            localEA.postEvent(new org.osgi.service.event.Event(SlingConstants.TOPIC_RESOURCE_REMOVED, properties));
+        }
+    }
+}

Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java?rev=789697&r1=789696&r2=789697&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java Tue Jun 30 12:45:31 2009
@@ -22,6 +22,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -31,6 +32,7 @@
 
 import org.apache.commons.collections.BidiMap;
 import org.apache.commons.collections.bidimap.TreeBidiMap;
+import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.commons.osgi.OsgiUtil;
@@ -45,6 +47,9 @@
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.ComponentContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -193,6 +198,12 @@
     // whether to mangle paths with namespaces or not
     private boolean mangleNamespacePrefixes;
 
+    /** The resource listenr for the observation events. */
+    private JcrResourceListener resourceListener;
+
+    /** The service tracker for the event admin
+     */
+    private ServiceTracker eventAdminTracker;
 
     public JcrResourceResolverFactoryImpl() {
         this.rootProviderEntry = new ResourceProviderEntry("/", null, null);
@@ -270,6 +281,11 @@
 
     /** Activates this component, called by SCR before registering as a service */
     protected void activate(ComponentContext componentContext) {
+        // setup tracker first as this is used in the bind/unbind methods
+        this.eventAdminTracker = new ServiceTracker(componentContext.getBundleContext(),
+                EventAdmin.class.getName(), null);
+        this.eventAdminTracker.open();
+
         this.componentContext = componentContext;
 
         Dictionary<?, ?> properties = componentContext.getProperties();
@@ -339,6 +355,15 @@
                 "activate: Cannot access repository, failed setting up Mapping Support",
                 e);
         }
+
+        // start observation listener
+        try {
+            this.resourceListener = new JcrResourceListener(this.repository, this, "/", "/", this.eventAdminTracker);
+        } catch (Exception e) {
+            log.error(
+                "activate: Cannot create resource listener; resource events for JCR resources will be disabled.",
+                e);
+        }
     }
 
     private JcrResourceResolverWebConsolePlugin plugin;
@@ -354,7 +379,14 @@
             mapEntries.dispose();
             mapEntries = MapEntries.EMPTY;
         }
-
+        if ( this.eventAdminTracker != null ) {
+            this.eventAdminTracker.close();
+            this.eventAdminTracker = null;
+        }
+        if ( this.resourceListener != null ) {
+            this.resourceListener.dispose();
+            this.resourceListener = null;
+        }
         this.componentContext = null;
     }
 
@@ -418,6 +450,7 @@
 
             String[] roots = OsgiUtil.toStringArray(reference.getProperty(ResourceProvider.ROOTS));
             if (roots != null && roots.length > 0) {
+                final EventAdmin localEA = (EventAdmin) this.eventAdminTracker.getService();
 
                 ResourceProvider provider = (ResourceProvider) componentContext.locateService(
                     "ResourceProvider", reference);
@@ -444,6 +477,12 @@
                                 new Object[] { provider, root,
                                     rpee.getExisting().getResourceProvider() });
                         }
+                        if ( localEA != null ) {
+                            final Dictionary<String, Object> props = new Hashtable<String, Object>();
+                            props.put(SlingConstants.PROPERTY_PATH, root);
+                            localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_ADDED,
+                                    props));
+                        }
                     }
                 }
             }
@@ -461,6 +500,8 @@
         String[] roots = OsgiUtil.toStringArray(reference.getProperty(ResourceProvider.ROOTS));
         if (roots != null && roots.length > 0) {
 
+            final EventAdmin localEA = (EventAdmin) ( this.eventAdminTracker != null ? this.eventAdminTracker.getService() : null);
+
             // synchronized insertion of new resource providers into
             // the tree to not inadvertently loose an entry
             synchronized (this) {
@@ -478,6 +519,12 @@
 
                     log.debug("unbindResourceProvider: root={} ({})", root,
                         serviceName);
+                    if ( localEA != null ) {
+                        final Dictionary<String, Object> props = new Hashtable<String, Object>();
+                        props.put(SlingConstants.PROPERTY_PATH, root);
+                        localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_REMOVED,
+                                props));
+                    }
                 }
             }
         }