You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2008/07/28 17:54:15 UTC

svn commit: r680383 - in /incubator/sling/whiteboard/jcrbundles: ./ src/main/java/org/apache/sling/jcr/jcrbundles/

Author: bdelacretaz
Date: Mon Jul 28 08:54:12 2008
New Revision: 680383

URL: http://svn.apache.org/viewvc?rev=680383&view=rev
Log:
SLING-587 - work in progress, simple bundles folders watching implemented

Added:
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java   (with props)
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java   (with props)
Modified:
    incubator/sling/whiteboard/jcrbundles/pom.xml
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java

Modified: incubator/sling/whiteboard/jcrbundles/pom.xml
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/pom.xml?rev=680383&r1=680382&r2=680383&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/pom.xml (original)
+++ incubator/sling/whiteboard/jcrbundles/pom.xml Mon Jul 28 08:54:12 2008
@@ -72,6 +72,11 @@
       <artifactId>org.osgi.compendium</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.jcr.api</artifactId>
+      <version>2.0.2-incubator</version>
+    </dependency>
+    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
     </dependency>

Added: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java?rev=680383&view=auto
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java (added)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java Mon Jul 28 08:54:12 2008
@@ -0,0 +1,207 @@
+/*
+ * 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.jcrbundles;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+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.jcr.api.SlingRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Manages a folder that contains bundles and configurations: listens to
+ *  changes in the folder, and rescan it when needed, passing nodes to
+ *  NodeProcessors that want to process them.   
+ */
+class BundlesFolder implements EventListener {
+    protected static final Logger log = LoggerFactory.getLogger(BundlesFolder.class);
+    
+    private final String path;
+    private final Session session;
+    private long nextScan;
+    
+    /** After receiving JCR events, we wait for this many msec before
+     *  re-scanning the folder, as events often come in bursts.
+     */
+    public static final long SCAN_DELAY_MSEC = 1000;
+    
+    /** List of processors for our bundles and configs */
+    private final List<NodeProcessor> processors;
+
+    /** Only folders having this name can contain bundles and configs */
+    public static final String BUNDLES_NODENAME = "bundles";
+    
+    /** Only folders having this type can contain bundles and configs */
+    public static final String BUNDLES_NODETYPE = "nt:hierarchyNode";
+    
+    /** Create a BundlesFolder on the given Repository, at the
+     *  given path
+     */
+    BundlesFolder(SlingRepository r, String path, List<NodeProcessor> processors) throws RepositoryException {
+        this.path = path;
+        this.processors = processors;
+        session = r.loginAdministrative(r.getDefaultWorkspace());
+        
+        // observe any changes in our folder, recursively
+        final int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED 
+            | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED;
+        final boolean isDeep = true;
+        final boolean noLocal = true;
+        session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, path, 
+                isDeep, null, null, noLocal);
+        
+        // trigger the initial scan without waiting
+        setScanTimer(0);
+        
+        log.info("{} created for path {}", getClass().getSimpleName(), path);
+    }
+    
+    /** MUST be called when done using this object */
+    void cleanup() throws RepositoryException {
+        session.getWorkspace().getObservationManager().removeEventListener(this);
+        session.logout();
+    }
+    
+    /** Any event causes us to rescan our folder, once there are no more
+     *  events during SCAN_DELAY_MSEC
+     */
+    public void onEvent(EventIterator it) {
+        setScanTimer(SCAN_DELAY_MSEC);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if( !(obj instanceof BundlesFolder) ) {
+            return false;
+        }
+        
+        final BundlesFolder other = (BundlesFolder)obj;
+        return path.equals(other.path);
+    }
+
+    @Override
+    public int hashCode() {
+        return path.hashCode();
+    }
+
+    /** Find all bundles folders under rootPath */
+    static Set<BundlesFolder> findBundlesFolders(SlingRepository r, String rootPath, 
+            List<NodeProcessor> processors) 
+    throws RepositoryException {
+        final Set<BundlesFolder> result = new HashSet<BundlesFolder>();
+        Session s = null;
+        
+        try {
+            s = r.loginAdministrative(r.getDefaultWorkspace());
+            if(!s.getRootNode().hasNode(relPath(rootPath))) {
+                log.info("Bundles root node {} not found, ignored", rootPath);
+            } else {
+                log.debug("Bundles root node {} found, looking for bundle folders inside it", rootPath);
+                final Node n = s.getRootNode().getNode(relPath(rootPath));
+                findBundlesFolders(r, n, result, processors);
+            }
+        } finally {
+            if(s != null) {
+                s.logout();
+            }
+        }
+        
+        return result;
+    }
+
+    /** Add n to setToUpdate if it is a bundle folder, and recurse into its children
+     *  to do the same.  
+     */
+    static void findBundlesFolders(SlingRepository r, Node n, 
+            Set<BundlesFolder> setToUpdate, List<NodeProcessor> processors) 
+    throws RepositoryException {
+        if(n.getName().equals(BUNDLES_NODENAME)) {
+            /*
+            if(!n.isNodeType(BUNDLES_NODETYPE)) {
+                log.debug("{} node does not have {} type, ignored", n.getPath(), BUNDLES_NODETYPE);
+            }*/
+            setToUpdate.add(new BundlesFolder(r, n.getPath(), processors));
+        }
+        final NodeIterator it = n.getNodes();
+        while(it.hasNext()) {
+            findBundlesFolders(r, it.nextNode(), setToUpdate, processors);
+        }
+ 
+    }
+    
+    /** Set or reset our scanning timer: scanIfNeeded() will do nothing 
+     *  unless this timer has expired */
+    protected void setScanTimer(long delayMsec) {
+        nextScan = System.currentTimeMillis() + delayMsec;
+    }
+    
+    /** If our timer allows it, recursively call processNode 
+     *  on our Node and its children */
+    void scanIfNeeded() throws RepositoryException {
+        if(nextScan != -1 && System.currentTimeMillis() > nextScan) {
+            nextScan = -1;
+            log.debug("Timer expired, scanning {}", path);
+            
+            // TODO handle case where folder disappears
+            final Node n = session.getRootNode().getNode(relPath(path));
+            processNode(n);
+        }
+    }
+    
+    /** Install/update/remove the bundle or config that Node n
+     *  represents, if any. */
+    protected void processNode(Node n) throws RepositoryException {
+        
+        boolean accepted = false;
+        
+        for(NodeProcessor p : processors) {
+            if(p.accepts(n)) {
+                accepted = true;
+                p.process(n);
+                break;
+            }
+        }
+        
+        if(!accepted) {
+            log.debug("No NodeProcessor found for node {}, ignored", n.getPath());
+        }
+        
+        final NodeIterator it = n.getNodes();
+        while(it.hasNext()) {
+            processNode(it.nextNode());
+        }
+    }
+    
+    static String relPath(String path) {
+        if(path.startsWith("/")) {
+            return path.substring(1);
+        }
+        return path;
+    }
+    
+}
\ No newline at end of file

Propchange: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java?rev=680383&r1=680382&r2=680383&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java Mon Jul 28 08:54:12 2008
@@ -18,5 +18,134 @@
  */
 package org.apache.sling.jcr.jcrbundles;
 
-public class JcrBundlesManager {
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** JcrBundlesManager service, manages OSGi bundles and
+ *  configurations stored in the JCR Repository.
+ *  
+ * @scr.service
+ *
+ * @scr.component
+ *  immediate="true"
+ *  metatype="no"
+ *
+ * @scr.property
+ *  name="service.description"
+ *  value="Sling JCR Bundles Manager Service"
+ *
+ * @scr.property
+ *  name="service.vendor"
+ *  value="The Apache Software Foundation"
+ */
+
+public class JcrBundlesManager implements Runnable {
+    /** Ordered list of root paths to observe for bundles and configs
+     *  TODO should be configurable
+     */
+    private String [] bundleRoots = { "/libs", "/apps" };
+    
+    /** List of processors for our bundles and configs */
+    private List<NodeProcessor> processors;
+    
+    /** Set of BundleFolders that we manage */
+    private Set<BundlesFolder> folders;
+    
+    /** @scr.reference */
+    private SlingRepository repository;
+    
+    /** Default log. */
+    protected final Logger log = LoggerFactory.getLogger(this.getClass());
+    
+    /** When activated, collect the list of bundle folders to scan
+     *  and register as a listener for future updates.
+     */
+    protected void activate(ComponentContext context) throws RepositoryException {
+
+        // setup our processors
+        processors = new LinkedList<NodeProcessor>();
+        processors.add(new NodeProcessor() {
+            public boolean accepts(Node n) throws RepositoryException {
+                return true;
+            }
+
+            public void process(Node n) throws RepositoryException {
+                log.debug("Dummy NodeProcessor would process Node {}", n.getPath());
+            }
+            
+        });
+        
+        // find "bundles" folders and add them to processing queue
+        folders = new HashSet<BundlesFolder>();
+        for(String rootPath : bundleRoots) {
+            folders.addAll(BundlesFolder.findBundlesFolders(repository, rootPath, processors));
+        }
+        
+        // TODO: should listen for any new "bundles" folders created after activation
+        
+        // start queue processing
+        final Thread t = new Thread(this, getClass().getSimpleName());
+        t.setDaemon(true);
+        t.start();
+    }
+    
+    /** Cleanup */
+    protected void deactivate(ComponentContext oldContext) {
+        for(BundlesFolder bf : folders) {
+            try {
+                bf.cleanup();
+            } catch(RepositoryException e) {
+                log.warn("RepositoryException in deactivate", e);
+            }
+        }
+        
+        folders.clear();
+    }
+    
+    /** Scan paths once their timer expires */
+    public void run() {
+        Session s = null;
+        
+        // We could use the scheduler service but that makes things harder to test
+        while(true) {
+            try {
+                s = repository.loginAdministrative(repository.getDefaultWorkspace());
+                runOneCycle(s);
+            } catch(IllegalArgumentException ie) {
+                log.warn("IllegalArgumentException  in " + getClass().getSimpleName(), ie);
+            } catch(RepositoryException re) {
+                log.warn("RepositoryException in " + getClass().getSimpleName(), re);
+                
+            } finally {
+                if(s!= null) {
+                    s.logout();
+                    s = null;
+                }
+                try {
+                    Thread.sleep(1000L);
+                } catch(InterruptedException ignore) {
+                    // ignore
+                }
+            }
+        }
+        
+    }
+    
+    /** Run one cycle of processing our scanTimes queue */
+    void runOneCycle(Session s) throws RepositoryException {
+        for(BundlesFolder bf : folders) {
+            bf.scanIfNeeded();
+        }
+    }
 }
\ No newline at end of file

Added: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java?rev=680383&view=auto
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java (added)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java Mon Jul 28 08:54:12 2008
@@ -0,0 +1,11 @@
+package org.apache.sling.jcr.jcrbundles;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+/** Interface for accepting and processing Nodes */
+
+interface NodeProcessor {
+    boolean accepts(Node n) throws RepositoryException;
+    void process(Node n) throws RepositoryException;
+}

Propchange: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/NodeProcessor.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL