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 2008/08/22 08:03:30 UTC

svn commit: r687956 - in /incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles: AbstractNodeProcessor.java BundleNodeProcessor.java BundlesFolder.java JcrBundlesManager.java

Author: cziegeler
Date: Thu Aug 21 23:03:29 2008
New Revision: 687956

URL: http://svn.apache.org/viewvc?rev=687956&view=rev
Log:
SLING-587 - Apply patch from Tobias Bocanegra adding a queue to install bundles

Modified:
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java
    incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/JcrBundlesManager.java

Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java?rev=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/AbstractNodeProcessor.java Thu Aug 21 23:03:29 2008
@@ -18,8 +18,6 @@
  */
 package org.apache.sling.jcr.jcrbundles;
 
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
-
 import java.io.InputStream;
 import java.util.Calendar;
 import java.util.regex.Pattern;
@@ -29,98 +27,108 @@
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/** NodeProcessor that accepts nodes based on a regexp on
- * 	their name, but does nothing with them */ 
+/**
+ * NodeProcessor that accepts nodes based on a regexp on
+ * their name, but does nothing with them
+ */
 abstract class AbstractNodeProcessor implements NodeProcessor {
-	
-    /** The relative path of the data and last modified date of an nt:file node */
-	public static final String JCR_CONTENT = "jcr:content";
+
+    /**
+     * The relative path of the data and last modified date of an nt:file node
+     */
+    public static final String JCR_CONTENT = "jcr:content";
     public static final String JCR_CONTENT_DATA = JCR_CONTENT + "/jcr:data";
     public static final String JCR_LAST_MODIFIED = "jcr:lastModified";
     public static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + "/" + JCR_LAST_MODIFIED;
 
     protected final Logger log = LoggerFactory.getLogger(this.getClass());
-	private final Pattern pattern;
-	
-	AbstractNodeProcessor(String regexp) {
-		pattern = Pattern.compile(regexp);
-	}
+    private final Pattern pattern;
 
-	public boolean accepts(Node n) throws RepositoryException {
-		boolean result = pattern.matcher(n.getName()).matches();
-		if(result) {
-			log.debug("Node {} accepted by {}", n.getPath(), getClass().getName());
-		}
-		return result;
-	}
-	
-	static InputStream getInputStream(Node fileNode) throws RepositoryException 
-    {
-    	Property prop = fileNode.getProperty(JCR_CONTENT_DATA);
-    	return prop.getStream();
-    }
-	
-	/** Return the Node to use to store status of given fileNode.
-	 * 	Session is not saved if status node is created */
-	protected Node getStatusNode(Node fileNode, boolean createIfNotFound) throws RepositoryException {
-		final String path = STATUS_BASE_PATH + fileNode.getPath();
-		final Session s = fileNode.getSession();
-		Node result = null;
-		
-		if(s.itemExists(path)) {
-			result = (Node)s.getItem(path);
-		} else if(createIfNotFound) {
-			result = deepCreate(s, path);
-		}
-		
-		if(result != null) {
-			log.debug("Status node for {} is at {}", fileNode.getPath(), result.getPath());
-		} else {
-			log.debug("No status node found for {}", fileNode.getPath());
-		}
-		
-		return result;
-	}
-	
-	/** Find the path of the node in the main tree that corresponds to the given status node path */
-	protected String getMainNodePath(String statusNodePath) {
-		return statusNodePath.substring(STATUS_BASE_PATH.length());
-	}
-	
-	/** Find the node in the main tree that corresponds to the given status node */
-	protected Node getMainNode(Node statusNode) throws RepositoryException {
-		final String path = getMainNodePath(statusNode.getPath());
-		final Session s = statusNode.getSession();
-		Node result = null;
-		
-		if(s.itemExists(path)) {
-			result = (Node)s.getItem(path);
-		}
-		
-		return result;
-	}
-	
-	protected Calendar getLastModified(Node fileNode) throws RepositoryException {
-		if(fileNode.hasProperty(JCR_CONTENT_LAST_MODIFIED)) {
-			return fileNode.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate();
-		}
-		return null;
-	}
-	
-	protected Node deepCreate(Session s, String path) throws RepositoryException {
-		if(path.startsWith("/")) {
-			path = path.substring(1);
-		}
-		final String [] names = path.split("/");
-		String currentPath = "";
-		
-		for(int i=0; i < names.length; i++) {
-			currentPath += "/" + names[i];
-			if(!s.itemExists(currentPath)) {
-				s.getRootNode().addNode(currentPath.substring(1));
+    AbstractNodeProcessor(String regexp) {
+        pattern = Pattern.compile(regexp);
+    }
+
+    public boolean accepts(Node n) throws RepositoryException {
+        boolean result = pattern.matcher(n.getName()).matches();
+        if (result) {
+            log.debug("Node {} accepted by {}", n.getPath(), getClass().getName());
+        }
+        return result;
+    }
+
+    static InputStream getInputStream(Node fileNode) throws RepositoryException {
+        Property prop = fileNode.getProperty(JCR_CONTENT_DATA);
+        return prop.getStream();
+    }
+
+    /**
+     * Return the Node to use to store status of given fileNode.
+     * Session is not saved if status node is created
+     */
+    protected Node getStatusNode(Node fileNode, boolean createIfNotFound) throws RepositoryException {
+        final String path = STATUS_BASE_PATH + fileNode.getPath();
+        final Session s = fileNode.getSession();
+        Node result = null;
+
+        if (s.itemExists(path)) {
+            result = (Node) s.getItem(path);
+        } else if (createIfNotFound) {
+            result = deepCreate(s, path);
+        }
+
+        if (result != null) {
+            log.debug("Status node for {} is at {}", fileNode.getPath(), result.getPath());
+        } else {
+            log.debug("No status node found for {}", fileNode.getPath());
+        }
+
+        return result;
+    }
+
+    /**
+     * Find the path of the node in the main tree that corresponds to the given status node path
+     */
+    protected String getMainNodePath(String statusNodePath) {
+        return statusNodePath.substring(STATUS_BASE_PATH.length());
+    }
+
+    /**
+     * Find the node in the main tree that corresponds to the given status node
+     */
+    protected Node getMainNode(Node statusNode) throws RepositoryException {
+        final String path = getMainNodePath(statusNode.getPath());
+        final Session s = statusNode.getSession();
+        Node result = null;
+
+        if (s.itemExists(path)) {
+            result = (Node) s.getItem(path);
+        }
+
+        return result;
+    }
+
+    protected Calendar getLastModified(Node fileNode) throws RepositoryException {
+        if (fileNode.hasProperty(JCR_CONTENT_LAST_MODIFIED)) {
+            return fileNode.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate();
+        }
+        return null;
+    }
+
+    protected Node deepCreate(Session s, String path) throws RepositoryException {
+        if (path.startsWith("/")) {
+            path = path.substring(1);
+        }
+        final String[] names = path.split("/");
+        String currentPath = "";
+
+        for (int i = 0; i < names.length; i++) {
+            currentPath += "/" + names[i];
+            if (!s.itemExists(currentPath)) {
+                s.getRootNode().addNode(currentPath.substring(1));
 			}
 		}
 

Modified: incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java?rev=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundleNodeProcessor.java Thu Aug 21 23:03:29 2008
@@ -18,8 +18,6 @@
  */
 package org.apache.sling.jcr.jcrbundles;
 
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.JCRBUNDLES_LOCATION_PREFIX;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Calendar;
@@ -28,121 +26,140 @@
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.JCRBUNDLES_LOCATION_PREFIX;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.packageadmin.PackageAdmin;
 
-/** Process nodes that look like bundles, based on their node name */
+/**
+ * Process nodes that look like bundles, based on their node name
+ */
 class BundleNodeProcessor extends AbstractNodeProcessor {
     private final ComponentContext context;
-    private final PackageAdmin padmin; 
-	
-	public BundleNodeProcessor(ComponentContext ctx, PackageAdmin pa) {
-		super("[a-zA-Z0-9].*\\.jar$");
-		context = ctx;
-		padmin = pa;
-	}
-	
-	public void process(Node n, Map<String, Boolean> flags) throws RepositoryException {
-		
-		// Do we have file data?
-		InputStream is = null;
-		try {
-			is = getInputStream(n);
-		} catch(Exception e) {
-			log.info("Unable to get InputStream on Node {}, will be ignored ({})", n.getPath(), e);
-			return;
-		}
+    private final PackageAdmin padmin;
+    private final JcrBundlesManager mgr;
+
+    public BundleNodeProcessor(JcrBundlesManager mgr, ComponentContext ctx, PackageAdmin pa) {
+        super("[a-zA-Z0-9].*\\.jar$");
+        this.mgr = mgr;
+        context = ctx;
+        padmin = pa;
+    }
+
+    public void process(Node n, Map<String, Boolean> flags) throws RepositoryException {
 
-		// We have data - install, update or do nothing with bundle
-		// TODO handle deletes (store a list of bundles that were installed)
+        // Do we have file data?
+        InputStream is = null;
+        try {
+            is = getInputStream(n);
+        } catch (Exception e) {
+            log.info("Unable to get InputStream on Node {}, will be ignored ({})", n.getPath(), e);
+            return;
+        }
+
+        // We have data - install, update or do nothing with bundle
+        // TODO handle deletes (store a list of bundles that were installed)
         final String location = getBundleLocation(n.getPath());
-		try {
-	        final Node status = getStatusNode(n, true);
-			final Bundle oldBundle = getBundleByLocation(location);
-			final Calendar lastModified = getLastModified(n);
-			Bundle newBundle = null;
-			boolean changed = false;
-			
-			if(oldBundle == null) {
-				// Bundle not installed yet, install it
-				changed = true;
-				newBundle = getBundleContext().installBundle(location, is);
-		        if (!isFragment(newBundle)) {
-		        	newBundle.start();
-			        log.info("Bundle {} successfully installed and started", location);
-		        } else {
-			        log.info("Fragment bundle {} successfully installed", location);
-		        }
-		        status.setProperty("status", "installed");
-		        
-			} else {
-				// Bundle already installed, did it change?
-				Calendar savedLastModified = null;
-				if(status.hasProperty(JCR_LAST_MODIFIED)) {
-					savedLastModified = status.getProperty(JCR_LAST_MODIFIED).getDate();
-				}
-				
-				changed = 
-					savedLastModified == null 
-					|| lastModified == null 
-					|| !(lastModified.equals(savedLastModified))
-				;
-				
-				if(changed) {
-					oldBundle.update(is);
-			        log.info("Bundle {} successfully updated", location);
-			        status.setProperty("status", "updated");
-				} else {
-			        log.debug("Bundle {} unchanged, no update needed", location);
-				}
-			}
-			
-			if(changed) {
-		        flags.put("refresh.packages", Boolean.TRUE);
-		        if(lastModified != null) {
-		        	status.setProperty(JCR_LAST_MODIFIED, lastModified);
-		        }
-		        n.getSession().save();
-			}
-	        
-		} catch(Exception e) {
-			log.warn("Exception while processing bundle {}", e);
-			
-		} finally {
-			try {
-				is.close();
-			} catch(IOException ioe) {
-				log.error("IOException on closing InputStream", ioe);
-			}
-		}
-	}
+        try {
+            final Node status = getStatusNode(n, true);
+            final Bundle oldBundle = getBundleByLocation(location);
+            final Calendar lastModified = getLastModified(n);
+            Bundle newBundle = null;
+            boolean changed = false;
+
+            if (oldBundle == null) {
+                // Bundle not installed yet, install it
+                changed = true;
+                newBundle = getBundleContext().installBundle(location, is);
+                if (!isFragment(newBundle)) {
+                    if ((newBundle.getState() & Bundle.RESOLVED) > 0) {
+                        newBundle.start();
+                        log.info("Bundle {} successfully installed and started", location);
+                    } else {
+                        mgr.addPendingBundle(location, newBundle);
+                        log.info("Bundle {} not resolved. delay start", location);
+                    }
+                } else {
+                    log.info("Fragment bundle {} successfully installed", location);
+                }
+                status.setProperty("status", "installed");
+                flags.put("refresh.packages", Boolean.TRUE);
+
+            } else {
+                // Bundle already installed, did it change?
+                Calendar savedLastModified = null;
+                if (status.hasProperty(JCR_LAST_MODIFIED)) {
+                    savedLastModified = status.getProperty(JCR_LAST_MODIFIED).getDate();
+                }
+
+                changed = savedLastModified == null
+                        || lastModified == null
+                        || !(lastModified.equals(savedLastModified));
+
+                if (changed) {
+                    try {
+                        oldBundle.update(is);
+                        log.info("Bundle {} successfully updated", location);
+                        status.setProperty("status", "updated");
+                    } catch (BundleException e) {
+                        log.info("Bundle {} could not be updated. adding to pending list", location, e);
+                        mgr.addPendingBundle(location, oldBundle);
+                    }
+                } else {
+                    log.debug("Bundle {} unchanged, no update needed", location);
+                }
+            }
+
+            if (changed) {
+                flags.put("refresh.packages", Boolean.TRUE);
+                if (lastModified != null) {
+                    status.setProperty(JCR_LAST_MODIFIED, lastModified);
+                }
+                n.getSession().save();
+            }
+
+        } catch (Exception e) {
+            log.warn("Exception while processing bundle {}", e);
+
+        } finally {
+            try {
+                is.close();
+            } catch (IOException ioe) {
+                log.error("IOException on closing InputStream", ioe);
+            }
+        }
+    }
 
-	/** Check if the given statusNode still has an equivalent in the main tree, and if not
-	 * 	uninstall the corresponding bundle, if found. Do the same thing for statusNodes' children */
+    /**
+     * Check if the given statusNode still has an equivalent in the main tree, and if not
+     * uninstall the corresponding bundle, if found. Do the same thing for statusNodes' children
+     */
     public void checkDeletions(Node statusNode, Map<String, Boolean> flags) throws Exception {
-    	final Node mainNode = getMainNode(statusNode);
-    	if(mainNode == null) {
-    		final String mainPath = getMainNodePath(statusNode.getPath());
-    		final String location = getBundleLocation(mainPath);
-    		final Bundle b = getBundleByLocation(location);
-    		if(b == null) {
-    			log.info("Node {} has been deleted, but corresponding bundle {} not found - deleting status node only", 
-    					mainPath, location);
-    		} else {
-    			try {
-        			b.uninstall();
-    		        flags.put("refresh.packages", Boolean.TRUE);
-        			log.info("Node {} has been deleted, bundle {} uninstalled", mainPath, location);
-    			} catch(Exception e) {
-        			log.error("Exception while trying to uninstall bundle " + location, e); 
-    			}
-    		}
-    		
-    		statusNode.remove();
-    		statusNode.getSession().save();
-    	}
+        final Node mainNode = getMainNode(statusNode);
+        if (mainNode == null) {
+            final String mainPath = getMainNodePath(statusNode.getPath());
+            final String location = getBundleLocation(mainPath);
+            final Bundle b = getBundleByLocation(location);
+            // make sure it's removed from the pending list
+            mgr.removePendingBundle(location);
+            if (b == null) {
+                log.info("Node {} has been deleted, but corresponding bundle {} not found - deleting status node only",
+                        mainPath, location);
+            } else {
+                try {
+                    b.uninstall();
+                    flags.put("refresh.packages", Boolean.TRUE);
+                    log.info("Node {} has been deleted, bundle {} uninstalled", mainPath, location);
+                } catch (Exception e) {
+                    log.error("Exception while trying to uninstall bundle " + location, e);
+                }
+            }
+
+            statusNode.remove();
+            statusNode.getSession().save();
+        }
     }
 
     private boolean isFragment(Bundle bundle) {
@@ -155,15 +172,15 @@
 
     protected Bundle getBundleByLocation(String location) {
         Bundle bundles[] = getBundleContext().getBundles();
-        for(Bundle b : bundles) {
-        	if(location.equals(b.getLocation())) {
-        		return b;
-        	}
+        for (Bundle b : bundles) {
+            if (location.equals(b.getLocation())) {
+                return b;
+            }
         }
         return null;
     }
-    
+
     protected String getBundleLocation(String bundleNodePath) {
-    	return JCRBUNDLES_LOCATION_PREFIX + bundleNodePath;
+        return JCRBUNDLES_LOCATION_PREFIX + bundleNodePath;
     }
 }
\ No newline at end of file

Modified: 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=687956&r1=687955&r2=687956&view=diff
==============================================================================
--- incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java (original)
+++ incubator/sling/whiteboard/jcrbundles/src/main/java/org/apache/sling/jcr/jcrbundles/BundlesFolder.java Thu Aug 21 23:03:29 2008
@@ -18,8 +18,6 @@
  */
 package org.apache.sling.jcr.jcrbundles;
 
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -34,58 +32,68 @@
 import javax.jcr.observation.EventListener;
 
 import org.apache.sling.jcr.api.SlingRepository;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
 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.   
+/**
+ * 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.
+
+    /**
+     * 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 */
+
+    /**
+     * List of processors for our bundles and configs
+     */
     private final List<NodeProcessor> processors;
 
-    /** Create a BundlesFolder on the given Repository, at the
-     *  given path
+    /**
+     * 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 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, 
+        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 */
+
+    /**
+     * 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
+
+    /**
+     * 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);
@@ -93,11 +101,11 @@
 
     @Override
     public boolean equals(Object obj) {
-        if( !(obj instanceof BundlesFolder) ) {
+        if (!(obj instanceof BundlesFolder)) {
             return false;
         }
-        
-        final BundlesFolder other = (BundlesFolder)obj;
+
+        final BundlesFolder other = (BundlesFolder) obj;
         return path.equals(other.path);
     }
 
@@ -106,16 +114,18 @@
         return path.hashCode();
     }
 
-    /** Find all bundles folders under rootPath */
-    static Set<BundlesFolder> findBundlesFolders(SlingRepository r, String rootPath, 
-            List<NodeProcessor> processors) 
-    throws RepositoryException {
+    /**
+     * 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))) {
+            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);
@@ -123,110 +133,121 @@
                 findBundlesFolders(r, n, result, processors);
             }
         } finally {
-            if(s != null) {
+            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)) {
+    /**
+     * 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)) {
             setToUpdate.add(new BundlesFolder(r, n.getPath(), processors));
         }
         final NodeIterator it = n.getNodes();
-        while(it.hasNext()) {
+        while (it.hasNext()) {
             findBundlesFolders(r, it.nextNode(), setToUpdate, processors);
         }
- 
+
     }
-    
-    /** Set or reset our scanning timer: scanIfNeeded() will do nothing 
-     *  unless this timer has expired */
+
+    /**
+     * 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 */
+
+    /**
+     * If our timer allows it, recursively call processNode
+     * on our Node and its children
+     */
     void scanIfNeeded(Map<String, Boolean> flags) throws Exception {
-        if(nextScan != -1 && System.currentTimeMillis() > nextScan) {
+        if (nextScan != -1 && System.currentTimeMillis() > nextScan) {
             nextScan = -1;
             log.debug("Timer expired, scanning {}", path);
             checkDeletions(flags);
             checkUpdatesAndDeletes(flags);
         }
     }
-    
-    /** Let our processors handle all nodes under the main tree */
+
+    /**
+     * Let our processors handle all nodes under the main tree
+     */
     void checkUpdatesAndDeletes(Map<String, Boolean> flags) throws Exception {
-	    if(session.getRootNode().hasNode(relPath(path))) {
-	        final Node n = session.getRootNode().getNode(relPath(path));
-	        processNode(n, flags, false);
-	    } else {
-	    	log.info("Bundles folder {} does not exist anymore", path);
-	    }
-    }
-    
-    /** Check for nodes in the status tree that have disappeared from their main
-     * 	locations, and let the processors handle these deletes.
+        if (session.getRootNode().hasNode(relPath(path))) {
+            final Node n = session.getRootNode().getNode(relPath(path));
+            processNode(n, flags, false);
+        } else {
+            log.info("Bundles folder {} does not exist anymore", path);
+        }
+    }
+
+    /**
+     * Check for nodes in the status tree that have disappeared from their main
+     * locations, and let the processors handle these deletes.
      */
     void checkDeletions(Map<String, Boolean> flags) throws Exception {
-    	final String statusPath = STATUS_BASE_PATH + path;
-        if(session.getRootNode().hasNode(relPath(statusPath))) {
+        final String statusPath = STATUS_BASE_PATH + path;
+        if (session.getRootNode().hasNode(relPath(statusPath))) {
             final Node n = session.getRootNode().getNode(relPath(statusPath));
             processNode(n, flags, true);
         } else {
-        	log.info("Status folder {} does not exist, checkDeletions does nothing", statusPath);
+            log.info("Status folder {} does not exist, checkDeletions does nothing", statusPath);
         }
     }
-    
-    /** Let the first NodeProcessor that accepts n process it (for normal processing
-     * 	or deletions), and recurse into n's children to do the same 
+
+    /**
+     * Let the first NodeProcessor that accepts n process it (for normal processing
+     * or deletions), and recurse into n's children to do the same
      */
     protected void processNode(Node n, Map<String, Boolean> flags, boolean checkDeletions) throws Exception {
-        
+
         boolean accepted = false;
         final String path = n.getPath();
         final Session s = n.getSession();
-        
-        for(NodeProcessor p : processors) {
-            if(p.accepts(n)) {
+
+        for (NodeProcessor p : processors) {
+            if (p.accepts(n)) {
                 accepted = true;
-                if(checkDeletions) {
-                	p.checkDeletions(n, flags);
+                if (checkDeletions) {
+                    p.checkDeletions(n, flags);
                 } else {
-                	p.process(n, flags);
+                    p.process(n, flags);
                 }
                 break;
             }
         }
-        
-        if(!accepted) {
+
+        if (!accepted) {
             log.debug("No NodeProcessor found for node {}, ignored", n.getPath());
         }
-        
+
         // n might have been deleted above, if it's a status done
-        if(s.itemExists(path)) {
-	        final NodeIterator it = n.getNodes();
-	        while(it.hasNext()) {
-	            processNode(it.nextNode(), flags, checkDeletions);
-	        }
+        if (s.itemExists(path)) {
+            final NodeIterator it = n.getNodes();
+            while (it.hasNext()) {
+                processNode(it.nextNode(), flags, checkDeletions);
+            }
         }
     }
-    
-    /** Return the relative path for supplied path */
+
+    /**
+     * Return the relative path for supplied path
+     */
     static String relPath(String path) {
-        if(path.startsWith("/")) {
+        if (path.startsWith("/")) {
             return path.substring(1);
         }
         return path;
     }
-    
+
 }
\ No newline at end of file

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=687956&r1=687955&r2=687956&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 Thu Aug 21 23:03:29 2008
@@ -18,15 +18,13 @@
  */
 package org.apache.sling.jcr.jcrbundles;
 
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
-import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
-
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Iterator;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
@@ -34,66 +32,80 @@
 import javax.jcr.observation.Event;
 
 import org.apache.sling.jcr.api.SlingRepository;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.BUNDLES_NODENAME;
+import static org.apache.sling.jcr.jcrbundles.JcrBundlesConstants.STATUS_BASE_PATH;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.framework.Bundle;
 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"
+/**
+ * JcrBundlesManager service, manages OSGi bundles and
+ * configurations stored in the JCR Repository.
  *
- * @scr.property
- *  name="service.vendor"
- *  value="The Apache Software Foundation"
+ * @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
+    /**
+     * 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 String [] bundleRoots = { "/libs", "/apps" };
-    
-    /** List of processors for our bundles and configs */
     private List<NodeProcessor> processors;
-    
-    /** Set of BundleFolders that we manage */
+
+    /**
+     * Set of BundleFolders that we manage
+     */
     private Set<BundlesFolder> folders;
-    
-    /** @scr.reference */
+
+    /**
+     * @scr.reference
+     */
     private SlingRepository repository;
-    
-    /** @scr.reference */
+
+    /**
+     * @scr.reference
+     */
     private PackageAdmin padmin;
-    
-    /** @scr.reference */
+
+    /**
+     * @scr.reference
+     */
     private ConfigurationAdmin cadmin;
 
-    /** Listeners for new BundleFolders under our bundleRoots */
+    /**
+     * Listeners for new BundleFolders under our bundleRoots
+     */
     private List<BundlesFolderCreationListener> listeners = new LinkedList<BundlesFolderCreationListener>();
-    
+
     private Session session;
     protected final Logger log = LoggerFactory.getLogger(this.getClass());
     private boolean running;
-    
-    /** When activated, collect the list of bundle folders to scan
-     *  and register as a listener for future updates.
+
+    private final Map<String, Bundle> pendingBundles = new HashMap<String, Bundle>();
+
+    /**
+     * 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 BundleNodeProcessor(context, padmin));
+        processors.add(new BundleNodeProcessor(this, context, padmin));
         processors.add(new ConfigNodeProcessor(cadmin));
         processors.add(new NodeProcessor() {
             public boolean accepts(Node n) throws RepositoryException {
@@ -103,148 +115,211 @@
             public void process(Node n, Map<String, Boolean> flags) throws RepositoryException {
                 log.debug("Node {} ignored in process() call, no NodeProcessor accepts it", n.getPath());
             }
-            
+
             public void checkDeletions(Node statusNode, Map<String, Boolean> flags) throws Exception {
                 log.debug("Node {} ignored in checkDeletions() call, no NodeProcessor accepts it", statusNode.getPath());
             }
-            
+
         });
-        
+
         // find "bundles" folders and add them to processing queue
         folders = new HashSet<BundlesFolder>();
-        for(String rootPath : bundleRoots) {
+        for (String rootPath : bundleRoots) {
             folders.addAll(BundlesFolder.findBundlesFolders(repository, rootPath, processors));
         }
-        
+
         // Listen for any new "bundles" folders created after activation
         session = repository.loginAdministrative(repository.getDefaultWorkspace());
         final int eventTypes = Event.NODE_ADDED;
         final boolean isDeep = true;
         final boolean noLocal = true;
-        for(String path : this.bundleRoots) {
+        for (String path : this.bundleRoots) {
             final BundlesFolderCreationListener bfc = new BundlesFolderCreationListener();
             listeners.add(bfc);
-            session.getWorkspace().getObservationManager().addEventListener(bfc, eventTypes, path, 
+            session.getWorkspace().getObservationManager().addEventListener(bfc, eventTypes, path,
                     isDeep, null, null, noLocal);
         }
-        
+
         // start queue processing
         final Thread t = new Thread(this, getClass().getSimpleName() + "_" + System.currentTimeMillis());
         t.setDaemon(true);
         running = true;
         t.start();
     }
-    
-    /** Cleanup */
+
+    /**
+     * Cleanup
+     */
     protected void deactivate(ComponentContext oldContext) {
-    	running = false;
-    	
-        for(BundlesFolder bf : folders) {
+        running = false;
+
+        for (BundlesFolder bf : folders) {
             try {
                 bf.cleanup();
-            } catch(RepositoryException e) {
+            } catch (RepositoryException e) {
                 log.warn("RepositoryException in deactivate/cleanup", e);
             }
         }
-        
+
         folders.clear();
-        
-        if(session != null) {
+
+        if (session != null) {
             try {
-                for(BundlesFolderCreationListener bfc : listeners) {
+                for (BundlesFolderCreationListener bfc : listeners) {
                     session.getWorkspace().getObservationManager().removeEventListener(bfc);
                 }
-            } catch(RepositoryException re) {
+            } catch (RepositoryException re) {
                 log.warn("RepositoryException in deactivate()/removeEventListener", re);
             }
-            
+
             listeners.clear();
-            
+
             session.logout();
             session = null;
         }
     }
+
+    protected void addPendingBundle(String path, Bundle bundle) {
+        synchronized (pendingBundles) {
+            pendingBundles.put(path, bundle);
+        }
+    }
     
-    /** Scan paths once their timer expires */
+    protected void removePendingBundle(String path) {
+        synchronized (pendingBundles) {
+            if (pendingBundles.remove(path) != null) {
+                log.info("Removed bundle {} from pending list.", path);
+            }
+        }
+    }
+    
+    /**
+     * Scan paths once their timer expires
+     */
     public void run() {
         log.info("{} thread {} starts", getClass().getSimpleName(), Thread.currentThread().getName());
         Session s = null;
-        
+
         // First check for deletions, using the status folder root path
         BundlesFolder statusFolder = null;
         try {
-        	statusFolder = new BundlesFolder(repository, STATUS_BASE_PATH, processors);
-        	final Map<String, Boolean> flags = new HashMap<String, Boolean>();
-        	statusFolder.checkDeletions(flags);
+            statusFolder = new BundlesFolder(repository, STATUS_BASE_PATH, processors);
+            final Map<String, Boolean> flags = new HashMap<String, Boolean>();
+            statusFolder.checkDeletions(flags);
             refreshPackagesIfNeeded(flags);
-        } catch(Exception e) {
-        	log.error("Exception during initial scanning of " + STATUS_BASE_PATH, e);
+        } catch (Exception e) {
+            log.error("Exception during initial scanning of " + STATUS_BASE_PATH, e);
         } finally {
-        	if(statusFolder != null) {
-        		try {
-        			statusFolder.cleanup();
-        		} catch(Exception e) {
-                	log.error("Exception during BundlesFolder cleanup of " + STATUS_BASE_PATH, e);
-        		}
-        	}
+            if (statusFolder != null) {
+                try {
+                    statusFolder.cleanup();
+                } catch (Exception e) {
+                    log.error("Exception during BundlesFolder cleanup of " + STATUS_BASE_PATH, e);
+                }
+            }
         }
-        
+
         // We could use the scheduler service but that makes things harder to test
-        while(running) {
+        while (running) {
             try {
-        		s = repository.loginAdministrative(repository.getDefaultWorkspace());
-        		runOneCycle(s);
-            } catch(IllegalArgumentException ie) {
+                s = repository.loginAdministrative(repository.getDefaultWorkspace());
+                runOneCycle(s);
+            } catch (IllegalArgumentException ie) {
                 log.warn("IllegalArgumentException  in " + getClass().getSimpleName(), ie);
-            } catch(RepositoryException re) {
+            } catch (RepositoryException re) {
                 log.warn("RepositoryException in " + getClass().getSimpleName(), re);
-            } catch(Throwable t) {
-                log.error("Unhandled Throwable in runOneCycle() - " 
-                		+ getClass().getSimpleName() + " thread will be stopped", t);
+            } catch (Throwable t) {
+                log.error("Unhandled Throwable in runOneCycle() - "
+                        + getClass().getSimpleName() + " thread will be stopped", t);
             } finally {
-                if(s!= null) {
+                if (s != null) {
                     s.logout();
                     s = null;
                 }
                 try {
                     Thread.sleep(1000L);
-                } catch(InterruptedException ignore) {
+                } catch (InterruptedException ignore) {
                     // ignore
                 }
             }
         }
-        
+
         log.info("{} thread {} ends", getClass().getSimpleName(), Thread.currentThread().getName());
     }
-    
-    /** Run one cycle of processing our scanTimes queue */
+
+    /**
+     * Run one cycle of processing our scanTimes queue
+     */
     void runOneCycle(Session s) throws Exception {
-        
+
         // Add new bundle folders that onEvent created
-        for(BundlesFolderCreationListener bfc : listeners) {
-            for(String path : bfc.getAndClearPaths()) {
+        for (BundlesFolderCreationListener bfc : listeners) {
+            for (String path : bfc.getAndClearPaths()) {
                 log.info("New \"" + BUNDLES_NODENAME + "\" node was detected at {}, creating BundlesFolder to watch it", path);
                 folders.add(new BundlesFolder(repository, path, processors));
             }
         }
-        
+
         // Let ouf BundlesFolders do their work
-    	final Map<String, Boolean> flags = new HashMap<String, Boolean>();
-    	
-        for(BundlesFolder bf : folders) {
-        	if(!running) {
-        		break;
-        	}
+        final Map<String, Boolean> flags = new HashMap<String, Boolean>();
+
+        for (BundlesFolder bf : folders) {
+            if (!running) {
+                break;
+            }
             bf.scanIfNeeded(flags);
         }
+
+        // try to resolve pending bundles
+        synchronized (pendingBundles) {
+            boolean tryStart = true;
+            while (tryStart) {
+                tryStart = false;
+                Iterator<String> iter = pendingBundles.keySet().iterator();
+                while (iter.hasNext()) {
+                    String location = iter.next();
+                    Bundle bundle = pendingBundles.get(location);
+                    log.info("Checking bundle {} which status is " + bundle.getState(), location);
+                    if ((bundle.getState() & Bundle.ACTIVE) > 0) {
+                        log.info("Bundle {} is active.", location);
+                        flags.put("refresh.packages", Boolean.TRUE);
+                        flags.put("resolve.bundles", Boolean.TRUE);
+                        iter.remove();
+                    } else if ((bundle.getState() & Bundle.STARTING) > 0) {
+                        log.info("Bundle {} is starting.", location);
+                    } else if ((bundle.getState() & Bundle.STOPPING) > 0) {
+                        log.info("Bundle {} is stopping.", location);
+                    } else if ((bundle.getState() & Bundle.UNINSTALLED) > 0) {
+                        log.info("Bundle {} is uninstalled.", location);
+                        iter.remove();
+                    } else if ((bundle.getState() & Bundle.RESOLVED) > 0) {
+                        log.info("Bundle {} is resolved, starting it.", location);
+                        flags.put("resolve.bundles", Boolean.TRUE);
+                        bundle.start();
+                        tryStart = true;
+                    } else if ((bundle.getState() & Bundle.INSTALLED) > 0) {
+                        log.info("Bundle {} is installed but not resolved.", location);
+                        flags.put("resolve.bundles", Boolean.TRUE);
+                    }
+                }
+            }
+            if (!pendingBundles.isEmpty()) {
+                log.info("Could not start all pending bundles. Still {} remaining.", pendingBundles.size());
+            }
+        }
+
         refreshPackagesIfNeeded(flags);
     }
-    
+
     void refreshPackagesIfNeeded(Map<String, Boolean> flags) {
-    	if(Boolean.TRUE.equals(flags.get("refresh.packages"))) {
-        	log.info("Refreshing packages");
-        	padmin.refreshPackages(null);
-    	}
+        if (Boolean.TRUE.equals(flags.get("resolve.bundles"))) {
+            log.info("Resolving bundles");
+            padmin.resolveBundles(null);
+        }
+        if (Boolean.TRUE.equals(flags.get("refresh.packages"))) {
+            log.info("Refreshing packages");
+            padmin.refreshPackages(null);
+        }
     }
-    
- }
\ No newline at end of file
+
+}
\ No newline at end of file