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/12/02 14:48:49 UTC

svn commit: r722457 - in /incubator/sling/trunk/extensions/jcrinstall/src: main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ main/java/org/apache/sling/jcr/jcrinstall/osgi/ main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/ main/resources/OSGI-INF/...

Author: bdelacretaz
Date: Tue Dec  2 05:48:48 2008
New Revision: 722457

URL: http://svn.apache.org/viewvc?rev=722457&view=rev
Log:
SLING-747 - Use OSGi start levels in jcrinstall, to better control bundles start/stop - work in progress, manual tests ok

Added:
    incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/
    incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/
    incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/metatype.properties   (with props)
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java   (with props)
Removed:
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java
Modified:
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ConfigInstallableData.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileInstallableData.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileNodeConverter.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/InstallableData.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiController.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockRepositoryObserver.java
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessorTest.java
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/MockInstallableData.java

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ConfigInstallableData.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ConfigInstallableData.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ConfigInstallableData.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ConfigInstallableData.java Tue Dec  2 05:48:48 2008
@@ -67,4 +67,9 @@
 		final BigInteger bigInt = new BigInteger(1, d.digest());
 		return new String(bigInt.toString(16));
 	}
+
+	/** Not applicable for config nodes, return 0 */
+	public int getBundleStartLevel() {
+	    return 0;
+	}
 }
\ No newline at end of file

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileInstallableData.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileInstallableData.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileInstallableData.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileInstallableData.java Tue Dec  2 05:48:48 2008
@@ -41,14 +41,16 @@
     private final Property dataProperty;
     private final String digest;
     private final String path;
+    private final int bundleStartLevel;
     
     @Override
     public String toString() {
     	return getClass().getSimpleName() + ": " + path;
     }
     
-	FileInstallableData(Node n) throws RepositoryException {
+	FileInstallableData(Node n, int bundleStartLevel) throws RepositoryException {
 		this.path = n.getPath();
+		this.bundleStartLevel = bundleStartLevel;
         if (n.hasProperty(JCR_CONTENT_LAST_MODIFIED)) {
         	digest = String.valueOf(n.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate().getTimeInMillis());
         } else {
@@ -79,4 +81,8 @@
 	public String getDigest() {
 		return digest;
 	}
+	
+	public int getBundleStartLevel() {
+	    return bundleStartLevel;
+	}
 }
\ No newline at end of file

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileNodeConverter.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileNodeConverter.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileNodeConverter.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileNodeConverter.java Tue Dec  2 05:48:48 2008
@@ -15,12 +15,17 @@
     
     private final RegexpFilter filenameFilter = new RegexpFilter(FILENAME_REGEXP);
     private final Logger log = LoggerFactory.getLogger(getClass());
+    private final int bundleStartLevel;
+    
+    public FileNodeConverter(int bundleStartLevel) {
+        this.bundleStartLevel = bundleStartLevel;
+    }
     
 	public InstallableData convertNode(Node n) throws RepositoryException {
 		InstallableData result = null;
 		if(n.hasProperty(FileInstallableData.JCR_CONTENT_DATA) && n.hasProperty(FileInstallableData.JCR_CONTENT_LAST_MODIFIED)) {
 			if(filenameFilter.accept(n.getName())) {
-				result = new FileInstallableData(n);
+				result = new FileInstallableData(n, bundleStartLevel);
 			} else {
 				log.debug("Node {} ignored due to {}", n.getPath(), filenameFilter);
 			}

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java Tue Dec  2 05:48:48 2008
@@ -39,9 +39,10 @@
 import org.apache.sling.jcr.jcrinstall.jcr.NodeConverter;
 import org.apache.sling.jcr.jcrinstall.osgi.OsgiController;
 import org.apache.sling.jcr.jcrinstall.osgi.ResourceOverrideRules;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleListener;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
 import org.osgi.service.component.ComponentContext;
+import org.osgi.service.startlevel.StartLevel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,9 +52,10 @@
  *  
  *  Calls the OsgiController to install/remove resources.
  *   
- *  @scr.component 
- *      immediate="true"
- *      metatype="no"
+ * @scr.component 
+ *  label="%jcrinstall.name" 
+ *  description="%jcrinstall.description"
+ *  immediate="true"
  *  @scr.property 
  *      name="service.description" 
  *      value="Sling jcrinstall OsgiController Service"
@@ -61,28 +63,38 @@
  *      name="service.vendor" 
  *      value="The Apache Software Foundation"
  */
-public class RepositoryObserver implements Runnable, BundleListener {
+public class RepositoryObserver implements Runnable, FrameworkListener {
 
-    protected SortedSet<WatchedFolder> folders;
+    private final SortedSet<WatchedFolder> folders = new TreeSet<WatchedFolder>();
     private RegexpFilter folderNameFilter;
     private RegexpFilter filenameFilter;
     private ResourceOverrideRules roRules;
+    private ComponentContext componentContext;
     private final PropertiesUtil propertiesUtil = new PropertiesUtil();
-    private boolean running;
+    private boolean observationCycleActive;
+    private boolean activated;
     
     /** @scr.reference */
     protected OsgiController osgiController;
     
-    /** @scr.reference */
+    /** @scr.reference cardinality="0..1" policy="dynamic" */
     protected SlingRepository repository;
     
+    /** @scr.reference */
+    protected StartLevel startLevel;
+    
+    /** @scr.property type="Integer" valueRef="DEFAULT_BUNDLES_START_LEVEL" */
+    private static final String BUNDLES_START_LEVEL__PROPERTY = "bundles.startlevel";
+    private static final int DEFAULT_BUNDLES_START_LEVEL = 30;
+    private int installedBundlesStartLevel = DEFAULT_BUNDLES_START_LEVEL;
+
     private Session session;
     private File serviceDataFile;
-    private long lastBundleEvent;
+    private int startLevelToSetAtStartup;
     
     private final List<NodeConverter> converters = new ArrayList<NodeConverter>();
     
-    private List<WatchedFolderCreationListener> listeners = new LinkedList<WatchedFolderCreationListener>();
+    private final List<WatchedFolderCreationListener> listeners = new LinkedList<WatchedFolderCreationListener>();
     
     /** Default set of root folders to watch */
     public static String[] DEFAULT_ROOTS = {"/libs", "/apps"};
@@ -104,12 +116,56 @@
     /** Upon activation, find folders to watch under our roots, and observe those
      *  roots to detect new folders to watch.
      */
-    protected void activate(ComponentContext context) throws RepositoryException {
-    	
-        if(context!=null) {
-            context.getBundleContext().addBundleListener(this);
+    protected void activate(ComponentContext context) throws Exception {
+        componentContext = context;
+
+        // Context can be null in automated tests, to make them simpler
+        if(context != null) {
+            context.getBundleContext().addFrameworkListener(this);
+        }
+        
+        // Read config
+        if(context != null) {
+            final Object prop = context.getProperties().get(BUNDLES_START_LEVEL__PROPERTY);
+            if(prop != null && prop instanceof Number) {
+                installedBundlesStartLevel = ((Number)prop).intValue();
+                log.info("Start level for installed bundles set to {} by {} property", installedBundlesStartLevel, BUNDLES_START_LEVEL__PROPERTY);
+            } else {
+                installedBundlesStartLevel = DEFAULT_BUNDLES_START_LEVEL;
+                log.info("Start level for installed bundles set to default value {}", installedBundlesStartLevel);
+            }
+        }
+
+        // Check start levels
+        if(context != null) {
+            final int myLevel = startLevel.getBundleStartLevel(context.getBundleContext().getBundle());
+            if(installedBundlesStartLevel < myLevel) {
+                // Running at a lower start level than the bundles that we install
+                // allows us to stop them if the repository goes away (SLING-747)
+                log.warn(
+                        "The configured start level for bundles installed by jcrinstall ({})"
+                        + " should be higher than the jcrinstall start level ({})",
+                        installedBundlesStartLevel, myLevel
+                );
+            }
         }
-        lastBundleEvent = System.currentTimeMillis();
+        
+        // Call startup() if we already have a repository, else that will be called
+        // by the bind method
+        if(repository != null) {
+            log.debug("activate()");
+            startup();
+        } else {
+            log.debug("activate() - Repository not available, cannot install bundles yet");
+        }
+        
+        activated = true;
+    }
+    	
+    /** Called at activation time, or when repository becomes available again
+     *  after going away. */
+    protected void startup() throws Exception {
+        log.debug("startup()");
         
     	// TODO make this more configurable (in sync with ResourceOverrideRulesImpl)
     	final String [] roots = DEFAULT_ROOTS;
@@ -123,10 +179,10 @@
          *	Using services and a whiteboard pattern for these would be nice,
          * 	but that could be problematic at startup due to async loading
          */
-    	converters.add(new FileNodeConverter());
+    	converters.add(new FileNodeConverter(installedBundlesStartLevel));
     	converters.add(new ConfigNodeConverter());
     	
-    	String folderNameRegexp = getPropertyValue(context, FOLDER_NAME_REGEXP_PROPERTY);
+    	String folderNameRegexp = getPropertyValue(componentContext, FOLDER_NAME_REGEXP_PROPERTY);
     	if(folderNameRegexp == null) {
     	    folderNameRegexp = DEFAULT_FOLDER_NAME_REGEXP;
             log.info("Using default folder name regexp '{}'", DEFAULT_FOLDER_NAME_REGEXP);
@@ -134,7 +190,7 @@
             log.info("Using folder name regexp '{}' from context property '{}'", folderNameRegexp, FOLDER_NAME_REGEXP_PROPERTY);
     	}
         folderNameFilter = new RegexpFilter(folderNameRegexp);
-        serviceDataFile = getServiceDataFile(context);
+        serviceDataFile = getServiceDataFile(componentContext);
         
         // Listen for any new WatchedFolders created after activation
         session = repository.loginAdministrative(repository.getDefaultWorkspace());
@@ -149,13 +205,24 @@
         }
         
         // Find folders to watch
-        folders = new TreeSet<WatchedFolder>();
         for(String root : roots) {
             folders.addAll(findWatchedFolders(root));
         }
         
+        // Handle initial uninstalls and installs
+        observationCycleActive = true;
+        final int myStartLevel = startLevel.getStartLevel();
+        handleInitialUninstalls();
+        runOneCycle();
+        
+        // Restore start level if we brought it down due to the repository going away
+        if(startLevelToSetAtStartup > 0 && startLevelToSetAtStartup != myStartLevel) {
+            log.info("startup(): resetting start level to {}", startLevelToSetAtStartup);
+            startLevel.setStartLevel(startLevelToSetAtStartup);
+            startLevelToSetAtStartup = 0;
+        }
+        
         // start queue processing
-        running = true;
         final Thread t = new Thread(this, getClass().getSimpleName() + "_" + System.currentTimeMillis());
         t.setDaemon(true);
         t.start();
@@ -175,11 +242,22 @@
     }
     
     protected void deactivate(ComponentContext context) {
-    	
-        running = false;
+        log.debug("deactivate()");
+        shutdown();
+        if(context != null) {
+            context.getBundleContext().removeFrameworkListener(this);
+        }
+        activated = false;
+        componentContext = null;
+        startLevelToSetAtStartup = 0;
+    }
+    
+    /** Called at deactivation time, or when repository stops being available */
+    protected void shutdown() {
+        log.debug("shutdown()");
+        
+        observationCycleActive = false;
         
-        context.getBundleContext().removeBundleListener(this);
-
     	for(WatchedFolder f : folders) {
     		f.cleanup();
     	}
@@ -199,6 +277,15 @@
     	
     	listeners.clear();
         folders.clear();
+        
+        if(componentContext != null) {
+            final int currentStartLevel = startLevel.getStartLevel();
+            final int myStartLevel = startLevel.getBundleStartLevel(componentContext.getBundleContext().getBundle());
+            if(currentStartLevel > myStartLevel) {
+                log.info("shutdown(): changing start level from {} to the jcrinstall start level of {}", currentStartLevel, myStartLevel);
+                startLevel.setStartLevel(myStartLevel);
+            }
+        }
     }
     
     /** Add WatchedFolders that have been discovered by our WatchedFolderCreationListeners, if any */
@@ -315,34 +402,28 @@
     public void run() {
         log.info("{} thread {} starts", getClass().getSimpleName(), Thread.currentThread().getName());
         
-        // Wait some time after receiving our last bundle event, to
-        // avoid uninstalling stuff while components might be starting
-        final long bundleEventDelayMsec = 1000;
-        boolean logged = false;
-        while(System.currentTimeMillis() - lastBundleEvent < bundleEventDelayMsec) {
-            if(!logged) {
-                log.info("Bundle events were detected in the last {} msec, waiting...", bundleEventDelayMsec);
-            }
-            logged = true;
-            
-            try {
-                Thread.sleep(100);
-            } catch(InterruptedException ignore) {
-                // ignore
-            }
-        }
-        log.info("No bundle events in the last {} msec, starting processing", bundleEventDelayMsec);
-
-        
         // We could use the scheduler service but that makes things harder to test
-        boolean firstCycle = true;
-        while (running) {
+        boolean scanning = false;
+        boolean oldScanning = !scanning;
+        
+        while (observationCycleActive) {
             try {
-            	if(firstCycle) {
-            		handleInitialUninstalls();
-            		firstCycle = false;
-            	}
-                runOneCycle();
+                final int currentLevel = startLevel.getStartLevel(); 
+                scanning = currentLevel >= installedBundlesStartLevel;
+                if(scanning != oldScanning) {
+                    if(scanning) {
+                        log.info("Scanning enabled, current start level ({}) equals or above {} (start level of installed bundles)", 
+                                currentLevel, installedBundlesStartLevel);
+                    } else {
+                        log.info("Scanning disabled, current start level ({}) is below {} (start level of installed bundles)", 
+                                currentLevel, installedBundlesStartLevel);
+                    }
+                    oldScanning = scanning;
+                }
+                
+                if(scanning) {
+                    runOneCycle();
+                }
             } catch (IllegalArgumentException ie) {
                 log.warn("IllegalArgumentException  in " + getClass().getSimpleName(), ie);
             } catch (RepositoryException re) {
@@ -367,6 +448,9 @@
     	// Add any new watched folders, and scan those who need it 
         addNewWatchedFolders();
     	for(WatchedFolder wf : folders) {
+    	    if(!observationCycleActive) {
+    	        break;
+    	    }
     		wf.scanIfNeeded(converters);
         }
     	
@@ -374,8 +458,43 @@
     	// resources that we detected
     	osgiController.executeScheduledOperations();
     }
+
+    /** Called when a SlingRepository becomes available, either at activation time
+     *  or later, if the repository had disappeared since activated.
+     */
+    protected void bindSlingRepository(SlingRepository r) {
+        repository = r;
+        if(activated) {
+            log.debug("bindSlingRepository()");
+            try {
+                startup();
+            } catch(Exception e) {
+                log.error("Exception in bindSlingRepository/startup", e);
+            }
+        } else {
+            log.debug("bindSlingRepository() but not activated yet, startup() not called");
+        }
+    }
+
+    /** Called when a SlingRepository becomes unavailable, either at deactivation time
+     *  or because the repository became unavailable. 
+     */
+    protected void unbindSlingRepository(SlingRepository r) {
+        // Store current start level: shutdown() will bring it down and we want
+        // to go back to it if repository comes back
+        startLevelToSetAtStartup = startLevel.getStartLevel();
+        log.debug("unbindSlingRepository() called at start level {}", startLevelToSetAtStartup);
+        shutdown();
+        repository = null;
+    }
+
+    public void frameworkEvent(FrameworkEvent e) {
+        if(e.getType() == FrameworkEvent.STARTLEVEL_CHANGED) {
+            log.info("FrameworkEvent.STARTLEVEL_CHANGED, start level={}", startLevel.getStartLevel());
+        }
+    }  
     
-    public void bundleChanged(BundleEvent event) {
-        lastBundleEvent = System.currentTimeMillis();
+    protected Set<WatchedFolder> getWatchedFolders() {
+        return folders;
     }
 }
\ No newline at end of file

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/InstallableData.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/InstallableData.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/InstallableData.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/InstallableData.java Tue Dec  2 05:48:48 2008
@@ -32,4 +32,9 @@
 	/** Adapt the underlying data to the provided type.
 	 *	@return null if cannot be adapted */
 	<AdapterType> AdapterType adaptTo(Class<AdapterType> type);
+	
+	/** If data to install is a Bundle, this is the start level
+	 *  to use when installing it.
+	 */
+	int getBundleStartLevel();
 }

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiController.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiController.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiController.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiController.java Tue Dec  2 05:48:48 2008
@@ -34,7 +34,9 @@
      */
     int scheduleInstallOrUpdate(String uri, InstallableData data) throws IOException, JcrInstallException;
     
-    /** Schedule uninstallation of resource that was installed via given uri
+    /** Schedule uninstallation of resource that was installed via given uri.
+     *  Might be called several times for the same URI - needless calls should
+     *  be ignored.
      *  @param uri Unique identifier for the resource
      *  @param attributes metadata stored by the OsgiController, will be
      *      removed after calling this method

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java Tue Dec  2 05:48:48 2008
@@ -38,7 +38,8 @@
      */
     int installOrUpdate(String uri, Map<String, Object> attributes, InstallableData data) throws Exception;
     
-    /** Uninstall the resource that was installed via given uri
+    /** Uninstall the resource that was installed via given uri. Calling this with an uri
+     *  that is not installed is not an error.
      *  @param uri Unique identifier for the resource
      *  @param attributes metadata stored by the OsgiController, will be
      *      removed after calling this method

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java Tue Dec  2 05:48:48 2008
@@ -22,8 +22,6 @@
 import static org.apache.sling.jcr.jcrinstall.osgi.InstallResultCode.INSTALLED;
 import static org.apache.sling.jcr.jcrinstall.osgi.InstallResultCode.UPDATED;
 
-import java.io.BufferedInputStream;
-import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -45,6 +43,7 @@
 import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.Version;
 import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,8 +57,8 @@
     public static final String KEY_BUNDLE_ID = "bundle.id";
 
     private final BundleContext ctx;
-
     private final PackageAdmin packageAdmin;
+    private final StartLevel startLevel;
 
     /**
      * All bundles which were active before {@link #processResourceQueue()}
@@ -89,9 +88,10 @@
     
     private final Logger log = LoggerFactory.getLogger(this.getClass());
 
-    BundleResourceProcessor(BundleContext ctx, PackageAdmin packageAdmin) {
+    BundleResourceProcessor(BundleContext ctx, PackageAdmin packageAdmin, StartLevel startLevel) {
         this.ctx = ctx;
         this.packageAdmin = packageAdmin;
+        this.startLevel = startLevel;
         this.activeBundles = new HashSet<Long>();
         this.installedBundles = new ArrayList<Long>();
 
@@ -175,8 +175,10 @@
 			    needsRefresh = true;
 			} else {
 			    uri = OsgiControllerImpl.getResourceLocation(uri);
-			    log.debug("No matching Bundle for uri {}, installing", uri);
+			    final int level = installableData.getBundleStartLevel();
+			    log.debug("No matching Bundle for uri {}, installing with start level {}", uri, level);
 			    b = ctx.installBundle(uri, data);
+			    startLevel.setBundleStartLevel(b, level);
 			}
 		} finally {
 		    // data is never null here
@@ -261,7 +263,6 @@
             packageAdmin.refreshPackages(null);
             
         } else {
-        	log.debug("Processing resource queue in START mode");
             startBundles();
         }
     }
@@ -366,8 +367,10 @@
      * @param bundleIdCollection The IDs of bundles to be started.
      */
     private void startBundles(Collection<Long> bundleIdCollection, String collectionName) {
-    	log.debug("startBundles({}): {} bundles to process", 
-    			collectionName, bundleIdCollection.size());
+        if(bundleIdCollection.size() > 0) {
+        	log.debug("startBundles({}): {} bundles to process", 
+        			collectionName, bundleIdCollection.size());
+        }
     	
         // get the bundle ids and remove them from the collection
         Long[] bundleIds;

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java Tue Dec  2 05:48:48 2008
@@ -36,6 +36,7 @@
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,6 +68,9 @@
     /** @scr.reference */
     private PackageAdmin packageAdmin;
 
+    /** @scr.reference */
+    protected StartLevel startLevel;
+    
     /** Storage key: digest of an InstallableData */
     public static final String KEY_DIGEST = "data.digest";
 
@@ -78,7 +82,7 @@
     	// Note that, in executeScheduledOperations(),
     	// processors are called in the order of this list
         processors = new LinkedList<OsgiResourceProcessor>();
-        processors.add(new BundleResourceProcessor(context.getBundleContext(), packageAdmin));
+        processors.add(new BundleResourceProcessor(context.getBundleContext(), packageAdmin, startLevel));
         processors.add(new ConfigResourceProcessor(configAdmin));
 
         storage = new Storage(context.getBundleContext().getDataFile(STORAGE_FILENAME));

Added: incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=722457&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/metatype.properties (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/metatype.properties Tue Dec  2 05:48:48 2008
@@ -0,0 +1,35 @@
+#
+#  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.
+#
+
+
+#
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the SCR plugin
+
+jcrinstall.name = jcrinstall
+jcrinstall.description = Installs OSGi bundles and configurations found \
+ in the JCR Repository. 
+
+bundles.startlevel.name = Start level of installed bundles
+bundles.startlevel.description = Bundles installed by jcrinstall are set \
+ to this OSGi start level, which should be higher than the start level \
+ of the jcrinstall bundle. The default start level of the framework should \
+ be equal or higher to the configured value, so that installed bundles are \
+ started right after being installed by jcrinstall.
\ No newline at end of file

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/main/resources/OSGI-INF/metatype/metatype.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java Tue Dec  2 05:48:48 2008
@@ -63,6 +63,7 @@
         mockery.checking(new Expectations() {{
             allowing(osgiController).setResourceOverrideRules(with(any(ResourceOverrideRules.class)));
             allowing(osgiController).getInstalledUris(); will(returnValue(installedUri));
+            allowing(osgiController).executeScheduledOperations();
         }});
     }
     

Modified: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockRepositoryObserver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockRepositoryObserver.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockRepositoryObserver.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockRepositoryObserver.java Tue Dec  2 05:48:48 2008
@@ -21,7 +21,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Properties;
-import java.util.Set;
 
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.jcrinstall.osgi.OsgiController;
@@ -43,19 +42,16 @@
         osgiController = c;
         scanDelayMsec = 0;
         this.serviceDataFile = serviceDataFile;
+        startLevel = new MockStartLevel();
     }
     
     public void run() {
         // Do not run the observation cycle - we do that ourselves in testing
     }
     
-    Set<WatchedFolder> getWatchedFolders() {
-        return folders;
-    }
-    
     boolean folderIsWatched(String path) throws Exception {
         boolean result = false;
-        for(WatchedFolder wf : folders) {
+        for(WatchedFolder wf : getWatchedFolders()) {
             if(wf.getPath().equals("/" + path)) {
                 result = true;
                 break;

Added: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java?rev=722457&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java Tue Dec  2 05:48:48 2008
@@ -0,0 +1,36 @@
+package org.apache.sling.jcr.jcrinstall.jcr.impl;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.startlevel.StartLevel;
+
+public class MockStartLevel implements StartLevel {
+
+    public int getBundleStartLevel(Bundle arg0) {
+        return 0;
+    }
+
+    public int getInitialBundleStartLevel() {
+        return 0;
+    }
+
+    public int getStartLevel() {
+        return 0;
+    }
+
+    public boolean isBundleActivationPolicyUsed(Bundle arg0) {
+        return false;
+    }
+
+    public boolean isBundlePersistentlyStarted(Bundle arg0) {
+        return false;
+    }
+
+    public void setBundleStartLevel(Bundle arg0, int arg1) {
+    }
+
+    public void setInitialBundleStartLevel(int arg0) {
+    }
+
+    public void setStartLevel(int arg0) {
+    }
+}

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MockStartLevel.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java Tue Dec  2 05:48:48 2008
@@ -250,10 +250,14 @@
         	allowing(c).executeScheduledOperations();
             allowing(c).setResourceOverrideRules(with(any(ResourceOverrideRules.class)));
             allowing(c).getInstalledUris(); will(returnValue(installedUri));
-            allowing(c).getDigest(with(any(String.class))); will(returnValue(null)); 
-            one(c).scheduleUninstall("/libs/foo/bar/install/dummy.jar");
-            one(c).scheduleUninstall("/libs/foo/bar/install/dummy.cfg");
-            one(c).scheduleUninstall("/libs/watchfolder-is-gone/install/gone.cfg");
+            allowing(c).getDigest(with(any(String.class))); will(returnValue(null));
+            
+            // scheduleUninstall might be called multiple times for the same resource
+            // during the initial deletion analysis - the OsgiController needs to accept
+            // such multiple calls
+            atLeast(1).of(c).scheduleUninstall("/libs/foo/bar/install/dummy.jar");
+            atLeast(1).of(c).scheduleUninstall("/libs/foo/bar/install/dummy.cfg");
+            atLeast(1).of(c).scheduleUninstall("/libs/watchfolder-is-gone/install/gone.cfg");
         }});
         
         ro.activate(null);
@@ -272,17 +276,18 @@
         final OsgiController c = mockery.mock(OsgiController.class);
         final RepositoryObserver ro = new MockRepositoryObserver(repo, c);
         
+        // See testInitialDeletions() about multiple scheduleUninstall calls
         mockery.checking(new Expectations() {{
         	allowing(c).executeScheduledOperations();
             allowing(c).setResourceOverrideRules(with(any(ResourceOverrideRules.class)));
             allowing(c).getInstalledUris(); will(returnValue(installedUri));
             allowing(c).getDigest(with(any(String.class))); will(returnValue(null)); 
-            one(c).scheduleUninstall("/libs/foo/bar/install/dummy.cfg");
+            atLeast(1).of(c).scheduleUninstall("/libs/foo/bar/install/dummy.cfg");
             inSequence(sequence);
-            one(c).scheduleUninstall("/libs/foo/bar/install/dummy.dp");
+            atLeast(1).of(c).scheduleUninstall("/libs/foo/bar/install/dummy.dp");
             inSequence(sequence);
             will(throwException(new JcrInstallException("Fake BundleException for testing")));
-            one(c).scheduleUninstall("/libs/foo/bar/install/dummy.jar");
+            atLeast(1).of(c).scheduleUninstall("/libs/foo/bar/install/dummy.jar");
             inSequence(sequence);
         }});
         
@@ -360,8 +365,8 @@
             allowing(c).setResourceOverrideRules(with(any(ResourceOverrideRules.class)));
             allowing(c).getInstalledUris(); will(returnValue(installedUri));
             allowing(c).getDigest(with(any(String.class))); will(returnValue(null)); 
-            one(c).scheduleInstallOrUpdate(with(equal(resources[0])), with(any(InstallableData.class)));
-            one(c).scheduleInstallOrUpdate(with(equal(resources[2])), with(any(InstallableData.class)));
+            atLeast(1).of(c).scheduleInstallOrUpdate(with(equal(resources[0])), with(any(InstallableData.class)));
+            atLeast(1).of(c).scheduleInstallOrUpdate(with(equal(resources[2])), with(any(InstallableData.class)));
         }});
         
         final MockRepositoryObserver ro = new MockRepositoryObserver(repo, c, serviceDataFile);
@@ -384,10 +389,10 @@
             allowing(c).setResourceOverrideRules(with(any(ResourceOverrideRules.class)));
             allowing(c).getInstalledUris(); will(returnValue(installedUri));
             allowing(c).getDigest(with(any(String.class))); will(returnValue(null)); 
-            one(c).scheduleUninstall(resources[0]);
-            one(c).scheduleUninstall(resources[2]);
-            one(c).scheduleInstallOrUpdate(with(equal(resources[1])), with(any(InstallableData.class)));
-            one(c).scheduleInstallOrUpdate(with(equal(resources[3])), with(any(InstallableData.class)));
+            atLeast(1).of(c).scheduleUninstall(resources[0]);
+            atLeast(1).of(c).scheduleUninstall(resources[2]);
+            atLeast(1).of(c).scheduleInstallOrUpdate(with(equal(resources[1])), with(any(InstallableData.class)));
+            atLeast(1).of(c).scheduleInstallOrUpdate(with(equal(resources[3])), with(any(InstallableData.class)));
         }});
         
         props.setProperty(RepositoryObserver.FOLDER_NAME_REGEXP_PROPERTY, ".*foo/wii/install$");

Modified: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessorTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessorTest.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessorTest.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessorTest.java Tue Dec  2 05:48:48 2008
@@ -29,7 +29,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-import org.apache.sling.jcr.jcrinstall.osgi.InstallableData;
+import org.apache.sling.jcr.jcrinstall.jcr.impl.MockStartLevel;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
 import org.jmock.Sequence;
@@ -37,6 +37,7 @@
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkListener;
 import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
 
 /** Test the BundleResourceProcessor */
 public class BundleResourceProcessorTest {
@@ -104,7 +105,7 @@
         }});
 
         // Do the calls and check some stuff on the way
-        final BundleResourceProcessor p = new BundleResourceProcessor(bc, pa);
+        final BundleResourceProcessor p = new BundleResourceProcessor(bc, pa, new MockStartLevel());
         Utilities.setProcessors(c, p);
         assertFalse("Before install, uri must not be in list", c.getInstalledUris().contains(uri));
 

Modified: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/MockInstallableData.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/MockInstallableData.java?rev=722457&r1=722456&r2=722457&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/MockInstallableData.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/osgi/impl/MockInstallableData.java Tue Dec  2 05:48:48 2008
@@ -52,4 +52,8 @@
 	public String getDigest() {
 		return digest;
 	}
+
+    public int getBundleStartLevel() {
+        return 0;
+    }
 }