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 2010/09/10 09:41:57 UTC

svn commit: r995689 [1/2] - in /sling/trunk/installer: fileinstall/src/main/java/org/apache/sling/installer/file/impl/ jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/ jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/...

Author: cziegeler
Date: Fri Sep 10 07:41:55 2010
New Revision: 995689

URL: http://svn.apache.org/viewvc?rev=995689&view=rev
Log:
SLING-1737 : Add state management for resources

Added:
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/ChangeStateTask.java   (with props)
Modified:
    sling/trunk/installer/fileinstall/src/main/java/org/apache/sling/installer/file/impl/Installer.java
    sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java
    sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MockOsgiInstaller.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/PersistentResourceList.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/AbstractConfigTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigInstallTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigRemoveTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigTaskCreator.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleTaskCreator.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleUpdateTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/SynchronousRefreshPackagesTask.java
    sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/SystemBundleUpdateTask.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/MockBundleResource.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceComparatorTest.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/TaskOrderingTest.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/tasks/BundleTaskCreatorTest.java
    sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/tasks/MockBundleTaskCreator.java
    sling/trunk/installer/osgi/it/pom.xml
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallStressTest.java
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/BundleInstallUpgradeDowngradeTest.java
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/InvalidBundlesTest.java
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/OsgiInstallerTestBase.java
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/RegisterResourcesTest.java
    sling/trunk/installer/osgi/it/src/test/java/org/apache/sling/osgi/installer/it/RemovedResourceDetectionTest.java

Modified: sling/trunk/installer/fileinstall/src/main/java/org/apache/sling/installer/file/impl/Installer.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/fileinstall/src/main/java/org/apache/sling/installer/file/impl/Installer.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/fileinstall/src/main/java/org/apache/sling/installer/file/impl/Installer.java (original)
+++ sling/trunk/installer/fileinstall/src/main/java/org/apache/sling/installer/file/impl/Installer.java Fri Sep 10 07:41:55 2010
@@ -85,7 +85,7 @@ public class Installer implements FileCh
                 resources.add(resource);
             }
         }
-        this.installer.registerResources(this.scheme, resources);
+        this.installer.registerResources(this.scheme, resources.toArray(new InstallableResource[resources.size()]));
     }
 
     private InstallableResource createResource(final File file) {

Modified: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java (original)
+++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java Fri Sep 10 07:41:55 2010
@@ -190,7 +190,7 @@ public class JcrInstaller implements Eve
                 }
 
                 log.debug("Registering {} resources with OSGi installer: {}", resources.size(), resources);
-                installer.registerResources(URL_SCHEME, resources);
+                installer.registerResources(URL_SCHEME, resources.toArray(new InstallableResource[resources.size()]));
             } catch (final RepositoryException re) {
                 log.error("Repository exception during startup - deactivating installer!", re);
                 active = false;

Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MockOsgiInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MockOsgiInstaller.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MockOsgiInstaller.java (original)
+++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MockOsgiInstaller.java Fri Sep 10 07:41:55 2010
@@ -18,7 +18,6 @@
  */
 package org.apache.sling.jcr.jcrinstall.impl;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
@@ -67,14 +66,16 @@ class MockOsgiInstaller implements OsgiI
     }
 
     /**
-     * @see org.apache.sling.osgi.installer.OsgiInstaller#registerResources(java.lang.String, java.util.Collection)
+     * @see org.apache.sling.osgi.installer.OsgiInstaller#registerResources(java.lang.String, org.apache.sling.osgi.installer.InstallableResource[])
      */
-    public void registerResources(String urlScheme, Collection<InstallableResource> data) {
+    public void registerResources(String urlScheme, final InstallableResource[] data) {
         // Sort the data to allow comparing the recorded calls reliably
         final List<InstallableResource> sorted = new LinkedList<InstallableResource>();
-        sorted.addAll(data);
+        for(final InstallableResource r : data) {
+            sorted.add(r);
+        }
         Collections.sort(sorted, new InstallableResourceComparator());
-        for(InstallableResource r : data) {
+        for(InstallableResource r : sorted) {
         	urls.add(urlScheme + ':' + r.getId());
             recordCall("register", urlScheme, r);
         }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java Fri Sep 10 07:41:55 2010
@@ -18,7 +18,6 @@
  */
 package org.apache.sling.osgi.installer;
 
-import java.util.Collection;
 
 /**
  * OSGi Service that installs/updates/removes installable data
@@ -45,9 +44,9 @@ public interface OsgiInstaller {
      * Invalid resources are ignored.
 	 *
      * @param urlScheme identifies the client.
-	 * @param data the list of available resources
+	 * @param resources the list of available resources
 	 */
-	void registerResources(String urlScheme, Collection<InstallableResource> data);
+	void registerResources(String urlScheme, InstallableResource[] resources);
 
 	/**
 	 * Inform the installer that resources are available for installation

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java Fri Sep 10 07:41:55 2010
@@ -20,11 +20,16 @@ package org.apache.sling.osgi.installer.
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -50,40 +55,59 @@ import org.osgi.framework.FrameworkListe
  */
 public class OsgiInstallerImpl
     extends Thread
-    implements BundleListener, FrameworkListener,
-               OsgiInstaller {
+    implements BundleListener, FrameworkListener, OsgiInstaller {
 
+    /** The bundle context. */
     private final BundleContext ctx;
+
+    /** New clients are joining through this map. */
+    private final Map<String, List<RegisteredResource>> newResourcesSchemes = new HashMap<String, List<RegisteredResource>>();
+
+    /** New resources added by clients. */
     private final List<RegisteredResource> newResources = new LinkedList<RegisteredResource>();
-    private final SortedSet<OsgiInstallerTask> tasksForNextCycle = new TreeSet<OsgiInstallerTask>();
-    private final List<SortedSet<RegisteredResource>> newResourcesSets = new ArrayList<SortedSet<RegisteredResource>>();
-    private final Set<String> newResourcesSchemes = new HashSet<String>();
+
+    /** Removed resources from clients. */
     private final Set<String> urlsToRemove = new HashSet<String>();
+
+    /** Tasks to be scheduled in the next iteration. */
+    private final SortedSet<OsgiInstallerTask> tasksForNextCycle = new TreeSet<OsgiInstallerTask>();
+
+    /** Are we still activate? */
     private volatile boolean active = true;
-    private volatile boolean retriesScheduled;
 
+    /** The persistent resource list. */
     private PersistentResourceList persistentList;
 
+    private volatile boolean retriesScheduled;
+
+
     private BundleTaskCreator bundleTaskCreator;
     private ConfigTaskCreator configTaskCreator;
 
-    OsgiInstallerImpl(final BundleContext ctx) {
+    /** Constructor */
+    public OsgiInstallerImpl(final BundleContext ctx) {
         this.ctx = ctx;
     }
 
-    void deactivate() {
+    /**
+     * Deactivate
+     */
+    public void deactivate() {
         this.active = false;
         this.configTaskCreator.deactivate();
         this.bundleTaskCreator.deactivate();
         ctx.removeBundleListener(this);
         ctx.removeFrameworkListener(this);
+        // wake up sleeping thread
         synchronized (newResources) {
             newResources.notify();
         }
     }
 
-    @Override
-    public void run() {
+    /**
+     * Initialize the installer
+     */
+    private void init() {
         // listen to framework and bundle events
         this.ctx.addFrameworkListener(this);
         this.ctx.addBundleListener(this);
@@ -92,47 +116,48 @@ public class OsgiInstallerImpl
         setName(getClass().getSimpleName());
         final File f = ctx.getDataFile("RegisteredResourceList.ser");
         persistentList = new PersistentResourceList(f);
+    }
+
+    @Override
+    public void run() {
+        this.init();
         while (active) {
-            try {
-            	mergeNewResources();
-                final SortedSet<OsgiInstallerTask> tasks = computeTasks();
+            this.mergeNewResources();
+            final SortedSet<OsgiInstallerTask> tasks = this.computeTasks();
 
-            	if (tasks.isEmpty() && !retriesScheduled) {
-            	    // No tasks to execute - wait until new resources are
-            	    // registered
-            	    cleanupInstallableResources();
-            	    Logger.logDebug("No tasks to process, going idle");
-
-            	    synchronized (newResources) {
-            	        try {
-            	            newResources.wait();
-            	        } catch (InterruptedException ignore) {}
-                    }
-            	    Logger.logDebug("Notified of new resources, back to work");
-            	    continue;
-            	}
-
-            	retriesScheduled = false;
-                if (executeTasks(tasks) > 0) {
-                    Logger.logDebug("Tasks have been executed, saving persistentList");
-                    persistentList.save();
+            if (tasks.isEmpty() && !retriesScheduled) {
+                // No tasks to execute - wait until new resources are
+                // registered
+                this.cleanupInstallableResources();
+                Logger.logDebug("No tasks to process, going idle");
+
+                synchronized (newResources) {
+                    try {
+                        newResources.wait();
+                    } catch (InterruptedException ignore) {}
                 }
+                Logger.logDebug("Notified of new resources, back to work");
+                continue;
+            }
+
+            retriesScheduled = false;
+            // execute tasks
+            this.executeTasks(tasks);
+            // clean up and save
+            this.cleanupInstallableResources();
 
-                // Some integration tests depend on this delay, make sure to
-                // rerun/adapt them if changing this value
+            // Some integration tests depend on this delay, make sure to
+            // rerun/adapt them if changing this value
+            try {
                 Thread.sleep(250);
-                cleanupInstallableResources();
-            } catch(Exception e) {
-                Logger.logWarn(e.toString(), e);
-                try {
-                    Thread.sleep(1500);
-                } catch(InterruptedException ignored) {
-                }
-            }
+            } catch (final InterruptedException ignore) {}
         }
-        Logger.logInfo("Deactivated, exiting");
     }
 
+    /**
+     * Check the scheme
+     * @throws IllegalArgumentException
+     */
     private void checkScheme(final String scheme) {
         if ( scheme == null || scheme.length() == 0 ) {
             throw new IllegalArgumentException("Scheme required");
@@ -143,180 +168,263 @@ public class OsgiInstallerImpl
     }
 
     /**
-     * @see org.apache.sling.osgi.installer.OsgiInstaller#updateResources(java.lang.String, org.apache.sling.osgi.installer.InstallableResource[], java.lang.String[])
+     * Create registered resources for all installable resources.
      */
-    public void updateResources(final String scheme,
-            final InstallableResource[] resources,
-            final String[] ids) {
+    private List<RegisteredResource> createResources(final String scheme,
+                                                     final InstallableResource[] resources) {
         checkScheme(scheme);
-        List<RegisteredResource> updatedResources = null;
+        List<RegisteredResource> createdResources = null;
         if ( resources != null && resources.length > 0 ) {
-            updatedResources = new ArrayList<RegisteredResource>();
+            createdResources = new ArrayList<RegisteredResource>();
             for(final InstallableResource r : resources ) {
-                RegisteredResource rr = null;
                 try {
-                    rr = RegisteredResourceImpl.create(ctx, r, scheme);
-                } catch(IOException ioe) {
+                    final RegisteredResource rr = RegisteredResourceImpl.create(ctx, r, scheme);
+                    createdResources.add(rr);
+                    Logger.logDebug("Adding new resource " + rr);
+                } catch (final IOException ioe) {
                     Logger.logWarn("Cannot create RegisteredResource (resource will be ignored):" + r, ioe);
-                    continue;
                 }
-                updatedResources.add(rr);
             }
         }
-        synchronized (newResources) {
-            boolean doNotify = false;
-            if ( updatedResources != null ) {
-                for(final RegisteredResource rr : updatedResources ) {
-                    Logger.logDebug("Adding new resource " + rr);
-                    newResources.add(rr);
-                    doNotify = true;
+        return createdResources;
+    }
+
+    /**
+     * Try to close all input streams.
+     * This is just a sanity check for input streams which might not have been closed
+     * as either processing threw an exception or the resource type is not supported.
+     */
+    private void closeInputStreams(final InstallableResource[] resources) {
+        if ( resources != null ) {
+            for(final InstallableResource r : resources ) {
+                final InputStream is = r.getInputStream();
+                if ( is != null ) {
+                    try {
+                        is.close();
+                    } catch (final IOException ignore) {
+                        // ignore
+                    }
                 }
             }
-            if ( ids != null ) {
-                for(final String id : ids) {
-                    final String url = scheme + ':' + id;
-                    // Will mark all resources which have r's URL as uninstallable
-                    Logger.logDebug("Adding URL " + url + " to urlsToRemove");
+        }
+    }
 
-                    urlsToRemove.add(url);
+    /**
+     * @see org.apache.sling.osgi.installer.OsgiInstaller#updateResources(java.lang.String, org.apache.sling.osgi.installer.InstallableResource[], java.lang.String[])
+     */
+    public void updateResources(final String scheme,
+                                final InstallableResource[] resources,
+                                final String[] ids) {
+        try {
+            final List<RegisteredResource> updatedResources = this.createResources(scheme, resources);
+
+            synchronized (newResources) {
+                boolean doNotify = false;
+                if ( updatedResources != null && updatedResources.size() > 0 ) {
+                    newResources.addAll(updatedResources);
                     doNotify = true;
                 }
+                if ( ids != null && ids.length > 0 ) {
+                    for(final String id : ids) {
+                        final String url = scheme + ':' + id;
+                        // Will mark all resources which have r's URL as uninstallable
+                        Logger.logDebug("Adding URL " + url + " to urlsToRemove");
+
+                        urlsToRemove.add(url);
+                    }
+                    doNotify = true;
+                }
+                if ( doNotify ) {
+                    newResources.notify();
+                }
             }
-            if ( doNotify ) {
-                newResources.notify();
-            }
+        } finally {
+            // we simply close all input streams now
+            this.closeInputStreams(resources);
         }
     }
 
     /**
-     * @see org.apache.sling.osgi.installer.OsgiInstaller#registerResources(java.lang.String, java.util.Collection)
+     * @see org.apache.sling.osgi.installer.OsgiInstaller#registerResources(java.lang.String, org.apache.sling.osgi.installer.InstallableResource[])
      */
-    public void registerResources(final String scheme, final Collection<InstallableResource> data) {
-        checkScheme(scheme);
-        final SortedSet<RegisteredResource> toAdd = new TreeSet<RegisteredResource>();
-        for(InstallableResource r : data) {
-            RegisteredResource rr =  null;
-            try {
-                rr = RegisteredResourceImpl.create(ctx, r, scheme);
-            } catch(IOException ioe) {
-                Logger.logWarn("Cannot create RegisteredResource (resource will be ignored):" + r, ioe);
-                continue;
+    public void registerResources(final String scheme, final InstallableResource[] resources) {
+        try {
+            List<RegisteredResource> registeredResources = this.createResources(scheme, resources);
+            if ( registeredResources == null ) {
+                // make sure we have a list, this makes processing later on easier
+                registeredResources = Collections.emptyList();
             }
-
-            Logger.logDebug("Adding new resource " + r);
-            toAdd.add(rr);
-        }
-
-        synchronized (newResources) {
-            if(!toAdd.isEmpty()) {
-            	newResourcesSets.add(toAdd);
+            synchronized (newResources) {
+                Logger.logDebug("Registered new resource scheme: " + scheme);
+                newResourcesSchemes.put(scheme, registeredResources);
+                newResources.notify();
             }
-            // Need to manage schemes separately: in case toAdd is empty we
-            // want to mark all such resources as non-installable
-            Logger.logDebug("Adding to newResourcesSchemes: " + scheme);
-            newResourcesSchemes.add(scheme);
-            newResources.notify();
+        } finally {
+            // we simply close all input streams now
+            this.closeInputStreams(resources);
         }
     }
 
+    /**
+     * This is the heart of the installer - it processes new resources and merges them
+     * with existing resources.
+     * The second part consists of detecting the resources to be processsed.
+     */
     private void mergeNewResources() {
         synchronized (newResources) {
-            // If we have sets of new resources, each of them represents the complete list
-            // of available resources for a given scheme. So, before adding them mark
-            // all resources with the same scheme in newResources, and existing
-            // registeredResources, as not installable
-        	for(String scheme : newResourcesSchemes) {
-        	    Logger.logDebug("Processing set of new resources with scheme " + scheme);
-                for(RegisteredResource r : newResources) {
-                    if(r.getScheme().equals(scheme)) {
-                        r.setInstallable(false);
-                        Logger.logDebug("New resource set to non-installable: " + r);
-                    }
-                 }
-                for(SortedSet<RegisteredResource> ss : this.persistentList.getData().values()) {
-                    for(RegisteredResource r : ss) {
-                        if(r.getScheme().equals(scheme)) {
-                            r.setInstallable(false);
-                            Logger.logDebug("Existing resource set to non-installable: " + r);
+            final boolean changed = !this.newResources.isEmpty() || !this.newResourcesSchemes.isEmpty() || !this.urlsToRemove.isEmpty();
+            // check for new resource providers (schemes)
+            // if we have new providers we have to sync them with existing resources
+            for(final Map.Entry<String, List<RegisteredResource>> entry : this.newResourcesSchemes.entrySet()) {
+                Logger.logDebug("Processing set of new resources with scheme " + entry.getKey());
+
+                // set all previously found resources that are not available anymore to uninstall
+                // if they have been installed - remove resources with a different state
+                for(final String entityId : this.persistentList.getEntityIds()) {
+                    final Collection<RegisteredResource> group = this.persistentList.getResources(entityId);
+
+                    final List<RegisteredResource> toRemove = new ArrayList<RegisteredResource>();
+                    boolean first = true;
+                    for(final RegisteredResource r : group) {
+                        if ( r.getScheme().equals(entry.getKey()) ) {
+                            Logger.logDebug("Checking " + r);
+                            // search if we have a new entry with the same url
+                            boolean found = false;
+                            final Iterator<RegisteredResource> m = entry.getValue().iterator();
+                            while ( !found && m.hasNext() ) {
+                                final RegisteredResource testResource = m.next();
+                                found = testResource.getURL().equals(r.getURL());
+                            }
+                            if ( !found) {
+                                Logger.logDebug("Resource " + r + " seems to be removed.");
+                                if ( r.getState() == RegisteredResource.State.INSTALLED && first ) {
+                                     r.setState(RegisteredResource.State.UNINSTALL);
+                                } else {
+                                    toRemove.add(r);
+                                }
+                            }
                         }
+                        first = false;
+                    }
+                    for(final RegisteredResource rr : toRemove) {
+                        this.persistentList.remove(rr);
                     }
                 }
-        	}
-            for(SortedSet<RegisteredResource> s : newResourcesSets) {
-                newResources.addAll(s);
-                Logger.logDebug("Added set of " + s.size() + " new resources with scheme "
-                            + s.first().getScheme() + ": " + s);
+                Logger.logDebug("Added set of " + entry.getValue().size() + " new resources with scheme "
+                        + entry.getKey() + ": " + entry.getValue());
+                newResources.addAll(entry.getValue());
             }
-            newResourcesSets.clear();
+
             newResourcesSchemes.clear();
 
             for(RegisteredResource r : newResources) {
-                SortedSet<RegisteredResource> t = this.persistentList.getData().get(r.getEntityId());
-                if(t == null) {
-                    t = new TreeSet<RegisteredResource>();
-                    this.persistentList.getData().put(r.getEntityId(), t);
-                }
-
-                // If an object with same sort key is already present, replace with the
-                // new one which might have different attributes
-                if(t.contains(r)) {
-                	for(RegisteredResource rr : t) {
-                		if(rr.compareTo(r) == 0) {
-                		    Logger.logDebug("Cleanup obsolete " + rr);
-                			rr.cleanup();
-                		}
-                	}
-                    t.remove(r);
-                }
-                t.add(r);
+                this.persistentList.addOrUpdate(r);
             }
             newResources.clear();
 
             // Mark resources for removal according to urlsToRemove
-            if(!urlsToRemove.isEmpty()) {
-                for(SortedSet<RegisteredResource> group : this.persistentList.getData().values()) {
-                	for(RegisteredResource r : group) {
-                		if(urlsToRemove.contains(r.getURL())) {
-                		    Logger.logDebug("Marking " + r + " uninistallable, URL is included in urlsToRemove");
-                			r.setInstallable(false);
-                		}
-                	}
+            if (!urlsToRemove.isEmpty()) {
+                for(final String url : urlsToRemove ) {
+                    this.persistentList.remove(url);
                 }
             }
             urlsToRemove.clear();
+
+            // if we have changes we have to process the resources per entity to update states
+            if ( changed ) {
+                for(final String entityId : this.persistentList.getEntityIds()) {
+                    final Collection<RegisteredResource> group = this.persistentList.getResources(entityId);
+                    if ( !group.isEmpty() ) {
+
+                        // The first resource in each group defines what should be done within this group.
+                        // This is based on the state of the first resource:
+                        // INSTALL : Install this resource and ignore all others in the group
+                        // UNINSTALL : Uninstall this resource and set the next resource in the group to INSTALL
+                        //             if it has either state IGNORE or INSTALLED
+                        // INSTALLED : Nothing to do
+                        // IGNORED   : Nothing to do
+                        // UNINSTALLED : This can't happen - but we do nothing in this case anyway
+
+                        final Iterator<RegisteredResource> i = group.iterator();
+                        final RegisteredResource first = i.next();
+
+                        RegisteredResource toActivate = null;
+                        switch ( first.getState() ) {
+                            case UNINSTALL : toActivate = first;
+                                             break;
+                            case INSTALL   : toActivate = first;
+                                             break;
+                        }
+                        if ( toActivate != null ) {
+                            Logger.logDebug("Activating " + toActivate);
+                            if ( toActivate.getState() == RegisteredResource.State.UNINSTALL && i.hasNext() ) {
+                                final RegisteredResource r = i.next();;
+                                if (r.getState() == RegisteredResource.State.IGNORED || r.getState() == RegisteredResource.State.INSTALLED) {
+                                    Logger.logDebug("Reactivating for next cycle " + r);
+                                    r.setState(RegisteredResource.State.INSTALL);
+                                }
+                            }
+                        }
+                    }
+                }
+                // persist list
+                this.persistentList.save();
+            }
         }
     }
 
-
-    /** Compute OSGi tasks based on our resources, and add to supplied list of tasks */
-    SortedSet<OsgiInstallerTask> computeTasks() throws Exception {
+    /**
+     * Compute OSGi tasks based on our resources, and add to supplied list of tasks.
+     */
+    private SortedSet<OsgiInstallerTask> computeTasks() {
         final SortedSet<OsgiInstallerTask> tasks = new TreeSet<OsgiInstallerTask>();
+
         // Add tasks that were scheduled for next cycle
         synchronized (tasksForNextCycle) {
-            for(OsgiInstallerTask t : tasksForNextCycle) {
-                tasks.add(t);
-            }
+            tasks.addAll(tasksForNextCycle);
             tasksForNextCycle.clear();
         }
 
         // Walk the list of entities, and create appropriate OSGi tasks for each group
-        // TODO do nothing for a group that's "stable" - i.e. one where no tasks were
-        // created in the last cycle??
-        for(SortedSet<RegisteredResource> group : this.persistentList.getData().values()) {
-            if (group.isEmpty()) {
-                continue;
-            }
-            final String rt = group.first().getType();
-            if ( InstallableResource.TYPE_BUNDLE.equals(rt) ) {
-                bundleTaskCreator.createTasks(group, tasks);
-            } else if ( InstallableResource.TYPE_CONFIG.equals(rt) ) {
-                configTaskCreator.createTasks(group, tasks);
+        for(final String entityId : this.persistentList.getEntityIds()) {
+            final Collection<RegisteredResource> group = this.persistentList.getResources(entityId);
+            if ( !group.isEmpty() ) {
+
+                // Check the first resource in each group
+                final Iterator<RegisteredResource> i = group.iterator();
+                final RegisteredResource first = i.next();
+
+                RegisteredResource toActivate = null;
+                switch ( first.getState() ) {
+                    case UNINSTALL : toActivate = first;
+                                     break;
+                    case INSTALL   : toActivate = first;
+                                     break;
+                }
+                if ( toActivate != null ) {
+                    final String rt = toActivate.getType();
+                    final OsgiInstallerTask task;
+                    if ( InstallableResource.TYPE_BUNDLE.equals(rt) ) {
+                        task = bundleTaskCreator.createTask(toActivate);
+                    } else if ( InstallableResource.TYPE_CONFIG.equals(rt) ) {
+                        task = configTaskCreator.createTask(toActivate);
+                    } else {
+                        task = null;
+                    }
+                    if ( task != null ) {
+                        tasks.add(task);
+                    }
+                }
+
             }
         }
         return tasks;
     }
 
-    private int executeTasks(final SortedSet<OsgiInstallerTask> tasks) {
+    /**
+     * Execute all tasks
+     */
+    private void executeTasks(final SortedSet<OsgiInstallerTask> tasks) {
         final OsgiInstallerContext ctx = new OsgiInstallerContext() {
 
             public void addTaskToNextCycle(final OsgiInstallerTask t) {
@@ -333,63 +441,39 @@ public class OsgiInstallerImpl
                 }
             }
         };
-        int counter = 0;
-        while (!tasks.isEmpty()) {
+        while (this.active && !tasks.isEmpty()) {
             OsgiInstallerTask t = null;
             synchronized (tasks) {
                 t = tasks.first();
                 tasks.remove(t);
             }
+            Logger.logInfo("Executing task " + t);
             t.execute(ctx);
-            counter++;
         }
-        return counter;
     }
 
-    private void cleanupInstallableResources() throws IOException {
-        // Cleanup resources that are not marked installable,
-        // they have been processed by now
-        int resourceCount = 0;
-        final List<RegisteredResource> toDelete = new ArrayList<RegisteredResource>();
-        final List<String> groupKeysToRemove = new ArrayList<String>();
-        for(SortedSet<RegisteredResource> group : this.persistentList.getData().values()) {
-            toDelete.clear();
-            String key = null;
-            for(RegisteredResource r : group) {
-                key = r.getEntityId();
-                resourceCount++;
-                if(!r.isInstallable()) {
-                    toDelete.add(r);
-                }
-            }
-            for(RegisteredResource r : toDelete) {
-                group.remove(r);
-                r.cleanup();
-                Logger.logDebug("Removing RegisteredResource from list, not installable and has been processed: " + r);
-            }
-            if(group.isEmpty() && key != null) {
-                groupKeysToRemove.add(key);
-            }
-        }
-
-        for(String key : groupKeysToRemove) {
-            this.persistentList.getData().remove(key);
+    /**
+     * Clean up and compact
+     */
+    private void cleanupInstallableResources() {
+        if ( this.persistentList.compact() ) {
+            persistentList.save();
         }
-
-        // List of resources might have changed
-        persistentList.save();
     }
 
     /** If we have any tasks waiting to be retried, schedule their execution */
     private void scheduleRetries() {
-    	final int toRetry = tasksForNextCycle.size();
-    	if(toRetry > 0) {
-    	    Logger.logDebug(toRetry + " tasks scheduled for retrying");
+        final int toRetry;
+        synchronized ( tasksForNextCycle ) {
+            toRetry = tasksForNextCycle.size();
+        }
+        if (toRetry > 0) {
+            Logger.logDebug(toRetry + " tasks scheduled for retrying");
             synchronized (newResources) {
-                newResources.notify();
                 retriesScheduled = true;
+                newResources.notify();
             }
-    	}
+        }
     }
 
     /**
@@ -399,11 +483,11 @@ public class OsgiInstallerImpl
         synchronized (LOCK) {
             eventsCount++;
         }
-    	final int t = e.getType();
-    	if(t == BundleEvent.INSTALLED || t == BundleEvent.RESOLVED || t == BundleEvent.STARTED || t == BundleEvent.UPDATED) {
-    	    Logger.logDebug("Received BundleEvent that might allow installed bundles to start, scheduling retries if any");
-    		scheduleRetries();
-    	}
+        final int t = e.getType();
+        if(t == BundleEvent.INSTALLED || t == BundleEvent.RESOLVED || t == BundleEvent.STARTED || t == BundleEvent.UPDATED) {
+            Logger.logDebug("Received BundleEvent that might allow installed bundles to start, scheduling retries if any");
+            scheduleRetries();
+        }
     }
 
     private static volatile long eventsCount;

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerTask.java Fri Sep 10 07:41:55 2010
@@ -19,14 +19,25 @@
 package org.apache.sling.osgi.installer.impl;
 
 
-/** Base class for tasks that can be executed by the {@link OsgiInstallerImpl} */
+/**
+ * Base class for tasks that can be executed by the {@link OsgiInstallerImpl}
+ */
 public abstract class OsgiInstallerTask implements Comparable<OsgiInstallerTask> {
 
-    public abstract void execute(OsgiInstallerContext ctx);
+    private final RegisteredResource resource;
 
-	protected void logExecution() {
-	    Logger.logInfo("OsgiInstallerTask: executing  " + this);
-	}
+    public OsgiInstallerTask(final RegisteredResource r) {
+        this.resource = r;
+    }
+
+    /**
+     * Return the corresponding resource - depending on the task this might be null.
+     */
+    public RegisteredResource getResource() {
+        return this.resource;
+    }
+
+    public abstract void execute(OsgiInstallerContext ctx);
 
 	/** Tasks are sorted according to this key */
 	public abstract String getSortKey();
@@ -36,7 +47,12 @@ public abstract class OsgiInstallerTask 
 		return getSortKey().compareTo(o.getSortKey());
 	}
 
-	@Override
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ": " + resource;
+    }
+
+    @Override
 	public final boolean equals(Object o) {
 		if(o instanceof OsgiInstallerTask) {
 			return getSortKey().equals(((OsgiInstallerTask)o).getSortKey());

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/PersistentResourceList.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/PersistentResourceList.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/PersistentResourceList.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/PersistentResourceList.java Fri Sep 10 07:41:55 2010
@@ -24,19 +24,32 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.SortedSet;
+import java.util.TreeSet;
 
-/** Persistent list of RegisteredResource, used by installer to
- *  keep track of all registered resources
+/**
+ * Persistent list of RegisteredResource, used by installer to
+ * keep track of all registered resources
  */
-class PersistentResourceList {
+public class PersistentResourceList {
+
+    /**
+     * Map of registered resource sets.
+     * The key of the map is the entity id of the registered resource.
+     * The value is a set containing all registered resources for the
+     * same entity. Usually this is just one resource per entity.
+     */
     private final Map<String, SortedSet<RegisteredResource>> data;
     private final File dataFile;
 
     @SuppressWarnings("unchecked")
-    PersistentResourceList(final File dataFile) {
+    public PersistentResourceList(final File dataFile) {
         this.dataFile = dataFile;
 
         Map<String, SortedSet<RegisteredResource>> restoredData = null;
@@ -45,13 +58,14 @@ class PersistentResourceList {
             try {
                 ois = new ObjectInputStream(new FileInputStream(dataFile));
                 restoredData = (Map<String, SortedSet<RegisteredResource>>)ois.readObject();
-            } catch(Exception e) {
-                Logger.logInfo("Unable to restore data, starting with empty list (" + e.toString());
+                Logger.logDebug("Restored rsource list: " + restoredData);
+            } catch (final Exception e) {
+                Logger.logWarn("Unable to restore data, starting with empty list (" + e.getMessage() + ")", e);
             } finally {
-                if(ois != null) {
+                if (ois != null) {
                     try {
                         ois.close();
-                    } catch(IOException ignore) {
+                    } catch (final IOException ignore) {
                         // ignore
                     }
                 }
@@ -60,16 +74,114 @@ class PersistentResourceList {
         data = restoredData != null ? restoredData : new HashMap<String, SortedSet<RegisteredResource>>();
     }
 
+    /** This method is just for testing. */
     Map<String, SortedSet<RegisteredResource>>  getData() {
         return data;
     }
 
-    void save() throws IOException {
-        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(dataFile));
+    public void save() {
         try {
-            oos.writeObject(data);
-        } finally {
-            oos.close();
+            final ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(dataFile));
+            try {
+                oos.writeObject(data);
+                Logger.logDebug("Persisted resource list.");
+            } finally {
+                oos.close();
+            }
+        } catch (final Exception e) {
+            Logger.logWarn("Unable to save persistent list: " + e.getMessage(), e);
+        }
+    }
+
+    public Collection<String> getEntityIds() {
+        return this.data.keySet();
+    }
+
+    public void addOrUpdate(final RegisteredResource r) {
+        Logger.logDebug("Adding " + r);
+        SortedSet<RegisteredResource> t = this.data.get(r.getEntityId());
+        if (t == null) {
+            t = new TreeSet<RegisteredResource>();
+            this.data.put(r.getEntityId(), t);
+        }
+
+        // If an object with same sort key is already present, replace with the
+        // new one which might have different attributes
+        boolean first = true;
+        for(final RegisteredResource rr : t) {
+            if ( rr.getURL().equals(r.getURL()) ) {
+                Logger.logDebug("Cleanup obsolete resource " + rr);
+                rr.cleanup();
+                t.remove(rr);
+                if ( first && rr.equals(r) ) {
+                    r.setState(rr.getState());
+                }
+                break;
+            }
+            first = false;
+        }
+        t.add(r);
+    }
+
+    public void remove(final String url) {
+        for(final SortedSet<RegisteredResource> group : this.data.values()) {
+            final Iterator<RegisteredResource> i = group.iterator();
+            boolean first = true;
+            while ( i.hasNext() ) {
+                final RegisteredResource r = i.next();
+                if ( r.getURL().equals(url) ) {
+                    if ( first && r.getState() == RegisteredResource.State.INSTALLED ) {
+                        Logger.logDebug("Marking " + r + " for uninstalling");
+                        r.setState(RegisteredResource.State.UNINSTALL);
+                    } else {
+                        Logger.logDebug("Removing unused " + r);
+                        i.remove();
+                        r.cleanup();
+                    }
+                }
+                first = false;
+            }
+        }
+    }
+
+    public void remove(final RegisteredResource r) {
+        final SortedSet<RegisteredResource> group = this.data.get(r.getEntityId());
+        if ( group != null ) {
+            Logger.logDebug("Removing unused " + r);
+            group.remove(r);
+            r.cleanup();
         }
     }
+
+    public Collection<RegisteredResource> getResources(final String entityId) {
+        return this.data.get(entityId);
+    }
+
+    public boolean compact() {
+        boolean changed = false;
+        final Iterator<Map.Entry<String, SortedSet<RegisteredResource>>> i = this.data.entrySet().iterator();
+        while ( i.hasNext() ) {
+            final Map.Entry<String, SortedSet<RegisteredResource>> entry = i.next();
+
+            final List<RegisteredResource> toDelete = new ArrayList<RegisteredResource>();
+            for(final RegisteredResource r : entry.getValue()) {
+                if ( r.getState() == RegisteredResource.State.UNINSTALLED ) {
+                    toDelete.add(r);
+                }
+            }
+            for(final RegisteredResource r : toDelete) {
+                changed = true;
+                entry.getValue().remove(r);
+                r.cleanup();
+                Logger.logDebug("Removing from list, uninstalled: " + r);
+            }
+
+            if ( entry.getValue().isEmpty() ) {
+                changed = true;
+                i.remove();
+            }
+        }
+        return changed;
+    }
+
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java Fri Sep 10 07:41:55 2010
@@ -33,6 +33,14 @@ import org.apache.sling.osgi.installer.O
  */
 public interface RegisteredResource extends Serializable, Comparable<RegisteredResource> {
 
+    enum State {
+        INSTALL,
+        UNINSTALL,
+        INSTALLED,
+        UNINSTALLED,
+        IGNORED
+    }
+
     /** Attribute key: configuration pid */
     String CONFIG_PID_ATTRIBUTE = "config.pid";
 
@@ -83,9 +91,6 @@ public interface RegisteredResource exte
     void cleanup();
 	String getURL();
 
-	boolean isInstallable();
-	void setInstallable(boolean installable);
-
 	String getScheme();
 
 	/** Attributes include the bundle symbolic name, bundle version, etc. */
@@ -98,4 +103,8 @@ public interface RegisteredResource exte
     String getEntityId();
 
     long getSerialNumber();
+
+    State getState();
+
+    void setState(final State s);
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResourceImpl.java Fri Sep 10 07:41:55 2010
@@ -58,14 +58,20 @@ public class RegisteredResourceImpl
     private static final String ENTITY_BUNDLE_PREFIX = "bundle:";
     private static final String ENTITY_CONFIG_PREFIX = "config:";
 
-    private static final long serialVersionUID = 3L;
+    private static final long serialVersionUID = 4L;
+
+    /** The resource id. */
     private final String id;
+    /** The installer scheme. */
 	private final String urlScheme;
+	/** The digest for the resource. */
 	private final String digest;
+	/** The entity id. */
 	private final String entity;
+	/** The dictionary for configurations. */
 	private final Dictionary<String, Object> dictionary;
+	/** Additional attributes. */
 	private final Map<String, Object> attributes = new HashMap<String, Object>();
-	private boolean installable = true;
 	private final File dataFile;
 	private final int priority;
     private final long serialNumber;
@@ -73,6 +79,8 @@ public class RegisteredResourceImpl
 
     private final String resourceType;
 
+    private State state = State.INSTALL;
+
     /**
      * Try to create a registered resource.
      */
@@ -170,7 +178,10 @@ public class RegisteredResourceImpl
 
 	@Override
 	public String toString() {
-	    return getClass().getSimpleName() + " " + this.getURL() + ", digest=" + this.getDigest() + ", serialNumber=" + this.getSerialNumber();
+	    return getClass().getSimpleName() + " " + this.getURL() +
+	        ", entity=" + this.getEntityId() +
+	        ", state=" + this.state +
+	        ", digest=" + this.getDigest() + ", serialNumber=" + this.getSerialNumber();
 	}
 
 	protected File getDataFile(final BundleContext bundleContext) {
@@ -284,20 +295,6 @@ public class RegisteredResourceImpl
 		return attributes;
 	}
 
-	/**
-	 * @see org.apache.sling.osgi.installer.impl.RegisteredResource#isInstallable()
-	 */
-	public boolean isInstallable() {
-        return installable;
-	}
-
-    /**
-     * @see org.apache.sling.osgi.installer.impl.RegisteredResource#setInstallable(boolean)
-     */
-    public void setInstallable(boolean installable) {
-        this.installable = installable;
-    }
-
     /** Read the manifest from supplied input stream, which is closed before return */
     private Manifest getManifest(InputStream ins) throws IOException {
         Manifest result = null;
@@ -363,85 +360,101 @@ public class RegisteredResourceImpl
     }
 
     /**
+     * @see org.apache.sling.osgi.installer.impl.RegisteredResource#getState()
+     */
+    public State getState() {
+        return this.state;
+    }
+
+    /**
+     * @see org.apache.sling.osgi.installer.impl.RegisteredResource#setState(org.apache.sling.osgi.installer.impl.RegisteredResource.State)
+     */
+    public void setState(State s) {
+        this.state = s;
+    }
+
+    /**
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        if ( obj == this ) {
+            return true;
+        }
+        if ( ! (obj instanceof RegisteredResource) ) {
+            return false;
+        }
+        return compareTo((RegisteredResource)obj) == 0;
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return this.entity.hashCode();
+    }
+
+    /**
      * @see java.lang.Comparable#compareTo(java.lang.Object)
      */
     public int compareTo(final RegisteredResource b) {
         return compare(this, b);
     }
 
+    /**
+     * Compare resources.
+     * First we compare the entity id - the entity id contains the resource type
+     * together with an entity identifier for the to be installed resource like
+     * the symbolic name of a bundle, the pid for a configuration etc.
+     */
     public static int compare(final RegisteredResource a, final RegisteredResource b) {
-        final boolean aBundle = a.getType().equals(InstallableResource.TYPE_BUNDLE);
-        final boolean bBundle = b.getType().equals(InstallableResource.TYPE_BUNDLE);
+        // check entity id first
+        int result = a.getEntityId().compareTo(b.getEntityId());
+        if ( result == 0 ) {
+            if (a.getType().equals(InstallableResource.TYPE_BUNDLE)) {
+                // we need a special comparison for bundles
+                result = compareBundles(a, b);
+            } else {
+                // all other types: check prio and then digest
+                result = Integer.valueOf(b.getPriority()).compareTo(a.getPriority());
 
-        if (aBundle && bBundle) {
-            return compareBundles(a, b);
-        } else if (!aBundle && !bBundle){
-            return compareConfig(a, b);
-        } else if (aBundle) {
-            return 1;
-        } else {
-            return -1;
+                // check digest
+                if ( result == 0 ) {
+                    result = a.getDigest().compareTo(b.getDigest());
+                }
+            }
         }
+        return result;
     }
 
+    /**
+     * Bundles are compared differently than other resource types:
+     * - higher versions have always priority - regardless of the priority attribute!
+     * - priority matters only if version is same
+     * - if the version is a snapshot version, the serial number and the digest are used
+     *   in addition
+     */
     private static int compareBundles(final RegisteredResource a, final RegisteredResource b) {
         boolean isSnapshot = false;
         int result = 0;
 
-        // Order first by symbolic name
-        final String nameA = (String)a.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
-        final String nameB = (String)b.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
-        if(nameA != null && nameB != null) {
-            result = nameA.compareTo(nameB);
-        }
-
-        // Then by version
-        if(result == 0) {
-            final Version va = new Version((String)a.getAttributes().get(Constants.BUNDLE_VERSION));
-            final Version vb = new Version((String)b.getAttributes().get(Constants.BUNDLE_VERSION));
-            isSnapshot = va.toString().contains("SNAPSHOT");
-            // higher version has more priority, must come first so invert comparison
-            result = vb.compareTo(va);
-        }
+        // Order by version
+        final Version va = new Version((String)a.getAttributes().get(Constants.BUNDLE_VERSION));
+        final Version vb = new Version((String)b.getAttributes().get(Constants.BUNDLE_VERSION));
+        isSnapshot = va.toString().contains("SNAPSHOT");
+        // higher version has more priority, must come first so invert comparison
+        result = vb.compareTo(va);
 
         // Then by priority, higher values first
-        if(result == 0) {
-            if(a.getPriority() < b.getPriority()) {
-                result = 1;
-            } else if(a.getPriority() > b.getPriority()) {
-                result = -1;
-            }
+        if (result == 0) {
+            result = Integer.valueOf(b.getPriority()).compareTo(a.getPriority());
         }
 
-        if(result == 0 && isSnapshot) {
+        if (result == 0 && isSnapshot) {
+            result = a.getDigest().compareTo(b.getDigest());
             // For snapshots, compare serial numbers so that snapshots registered
             // later get priority
-            if(a.getSerialNumber() < b.getSerialNumber()) {
-                result = 1;
-            } else if(a.getSerialNumber() > b.getSerialNumber()) {
-                result = -1;
-            }
-        }
-
-        return result;
-    }
-
-    private static int compareConfig(final RegisteredResource a, final RegisteredResource b) {
-        int result = 0;
-
-        // First compare by pid
-        final ConfigurationPid pA = (ConfigurationPid)a.getAttributes().get(RegisteredResource.CONFIG_PID_ATTRIBUTE);
-        final ConfigurationPid pB = (ConfigurationPid)b.getAttributes().get(RegisteredResource.CONFIG_PID_ATTRIBUTE);
-        if(pA != null && pA.getCompositePid() != null && pB != null && pB.getCompositePid() != null) {
-            result = pA.getCompositePid().compareTo(pB.getCompositePid());
-        }
-
-        // Then by priority, higher values first
-        if(result == 0) {
-            if(a.getPriority() < b.getPriority()) {
-                result = 1;
-            } else if( a.getPriority() > b.getPriority()) {
-                result = -1;
+            if ( result != 0 ) {
+                result = Long.valueOf(b.getSerialNumber()).compareTo(a.getSerialNumber());
             }
         }
 

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/AbstractConfigTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/AbstractConfigTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/AbstractConfigTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/AbstractConfigTask.java Fri Sep 10 07:41:55 2010
@@ -31,22 +31,18 @@ import org.osgi.util.tracker.ServiceTrac
 abstract class AbstractConfigTask extends OsgiInstallerTask {
 
     protected final ConfigurationPid pid;
-    protected final RegisteredResource resource;
 
     /** Tracker for the configuration admin. */
     private final ServiceTracker configAdminServiceTracker;
 
     AbstractConfigTask(final RegisteredResource r, final ServiceTracker configAdminServiceTracker) {
+        super(r);
         this.configAdminServiceTracker = configAdminServiceTracker;
-        this.resource = r;
         this.pid = (ConfigurationPid)r.getAttributes().get(RegisteredResource.CONFIG_PID_ATTRIBUTE);
-        if (this.pid == null) {
-            throw new IllegalArgumentException("RegisteredResource does not have CONFIG_PID_ATTRIBUTE: " + r);
-        }
     }
 
     /**
-     * @see org.apache.sling.osgi.installer.impl.OsgiInstallerContext#getConfigurationAdmin()
+     * Get the configuration admin - if available
      */
     protected ConfigurationAdmin getConfigurationAdmin() {
         return (ConfigurationAdmin)this.configAdminServiceTracker.getService();
@@ -54,8 +50,8 @@ abstract class AbstractConfigTask extend
 
 
     protected Configuration getConfiguration(final ConfigurationAdmin ca,
-            final ConfigurationPid cp,
-            final boolean createIfNeeded)
+                                             final ConfigurationPid cp,
+                                             final boolean createIfNeeded)
     throws IOException, InvalidSyntaxException {
         Configuration result = null;
 
@@ -78,9 +74,4 @@ abstract class AbstractConfigTask extend
 
         return result;
     }
-
-    @Override
-    public String toString() {
-        return getClass().getName() + ": " + resource;
-    }
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigInstallTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigInstallTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigInstallTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigInstallTask.java Fri Sep 10 07:41:55 2010
@@ -66,14 +66,10 @@ public class ConfigInstallTask extends A
         }
 
         // Convert data to a configuration Dictionary
-        Dictionary<String, Object> dict = resource.getDictionary();
-
-        if (dict == null) {
-            throw new IllegalArgumentException("Null Dictionary for resource " + resource);
-        }
+        final Dictionary<String, Object> dict = getResource().getDictionary();
 
         // Add pseudo-properties
-        dict.put(CONFIG_PATH_KEY, resource.getURL());
+        dict.put(CONFIG_PATH_KEY, getResource().getURL());
 
         // Factory?
         if (pid.getFactoryPid() != null) {
@@ -89,29 +85,30 @@ public class ConfigInstallTask extends A
                 created = true;
                 config = getConfiguration(ca, pid, true);
             } else {
-    			if (isSameData(config.getProperties(), resource.getDictionary())) {
+    			if (isSameData(config.getProperties(), getResource().getDictionary())) {
     			    Logger.logDebug("Configuration " + config.getPid()
     	                        + " already installed with same data, update request ignored: "
-    	                        + resource);
+    	                        + getResource());
     				config = null;
     			}
             }
 
             if (config != null) {
-                logExecution();
                 if (config.getBundleLocation() != null) {
                     config.setBundleLocation(null);
                 }
                 config.update(dict);
+                this.getResource().setState(RegisteredResource.State.INSTALLED);
                 Logger.logInfo("Configuration " + config.getPid()
                             + " " + (created ? "created" : "updated")
-                            + " from " + resource);
-                return;
+                            + " from " + getResource());
+            } else {
+                this.getResource().setState(RegisteredResource.State.IGNORED);
             }
         } catch (Exception e) {
+            Logger.logDebug("Exception during installation of config " + this.getResource() + " : " + e.getMessage() + ". Retrying later.", e);
             ctx.addTaskToNextCycle(this);
         }
-        return;
     }
 
     private Set<String> collectKeys(final Dictionary<String, Object>a) {

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigRemoveTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigRemoveTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigRemoveTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigRemoveTask.java Fri Sep 10 07:41:55 2010
@@ -55,19 +55,19 @@ public class ConfigRemoveTask extends Ab
             return;
         }
 
-        logExecution();
         try {
             final Configuration cfg = getConfiguration(ca, pid, false);
             if (cfg == null) {
-                Logger.logDebug("Cannot delete config , pid=" + pid + " not found, ignored (" + resource + ")");
+                Logger.logDebug("Cannot delete config , pid=" + pid + " not found, ignored (" + getResource() + ")");
+                this.getResource().setState(RegisteredResource.State.IGNORED);
             } else {
-                Logger.logInfo("Deleting config " + pid + " (" + resource + ")");
+                Logger.logInfo("Deleting config " + pid + " (" + getResource() + ")");
                 cfg.delete();
-                return;
+                this.getResource().setState(RegisteredResource.State.UNINSTALLED);
             }
         } catch (Exception e) {
+            Logger.logDebug("Exception during removal of config " + this.getResource() + " : " + e.getMessage() + ". Retrying later.", e);
             ctx.addTaskToNextCycle(this);
         }
-        return;
     }
 }
\ No newline at end of file

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigTaskCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigTaskCreator.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigTaskCreator.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/config/ConfigTaskCreator.java Fri Sep 10 07:41:55 2010
@@ -18,75 +18,50 @@
  */
 package org.apache.sling.osgi.installer.impl.config;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-
-import org.apache.sling.osgi.installer.impl.Logger;
 import org.apache.sling.osgi.installer.impl.OsgiInstallerTask;
 import org.apache.sling.osgi.installer.impl.RegisteredResource;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 
-/** TaskCreator that processes a list of config RegisteredResources */
+/**
+ * Task creator for configurations.
+ */
 public class ConfigTaskCreator {
 
     /** Interface of the config admin */
     private static String CONFIG_ADMIN_SERVICE_NAME = ConfigurationAdmin.class.getName();
 
-    /** Store digests of the installed configs, keyed by config pid */
-    private final Map<String, String> digests = new HashMap<String, String>();
-
+    /** Service tracker for the configuration admin. */
     private final ServiceTracker configAdminServiceTracker;
 
+    /**
+     * Constructor
+     */
     public ConfigTaskCreator(final BundleContext bc) {
         this.configAdminServiceTracker = new ServiceTracker(bc, CONFIG_ADMIN_SERVICE_NAME, null);
         this.configAdminServiceTracker.open();
     }
 
+    /**
+     * Deactivate this creator.
+     */
     public void deactivate() {
         this.configAdminServiceTracker.close();
     }
 
-	/** Create tasks for a set of RegisteredResource that all represent the same config PID.
+	/**
+     * Create a task to install or uninstall a configuration.
 	 */
-	public void createTasks(SortedSet<RegisteredResource> resources, SortedSet<OsgiInstallerTask> tasks) {
-
-		// Find the config that must be active: the resources collection is ordered according
-		// to priorities, so we just need to find the first one that is installable
-		RegisteredResource toActivate = null;
-		for(RegisteredResource r : resources) {
-			if (r.isInstallable()) {
-				toActivate = r;
-				break;
-			}
-		}
-
-		if(toActivate == null) {
+	public OsgiInstallerTask createTask(final RegisteredResource toActivate) {
+	    final OsgiInstallerTask result;
+		if (toActivate.getState() == RegisteredResource.State.UNINSTALL) {
 		    // None of our resources are installable, remove corresponding config
 		    // (task simply does nothing if config does not exist)
-		    final RegisteredResource first = resources.first();
-		    tasks.add(new ConfigRemoveTask(first, this.configAdminServiceTracker));
-		    digests.remove(getDigestKey(first));
+		    result = new ConfigRemoveTask(toActivate, this.configAdminServiceTracker);
 		} else {
-		    final String key = getDigestKey(toActivate);
-		    final String previousDigest = digests.get(key);
-		    if(toActivate.getDigest().equals(previousDigest)) {
-		        Logger.logDebug("Configuration (" + key+ ") already installed, ignored: " + toActivate);
-		    } else {
-		        tasks.add(new ConfigInstallTask(toActivate, this.configAdminServiceTracker));
-		        digests.put(key, toActivate.getDigest());
-		        Logger.logDebug("Scheduling update/install of config " + toActivate + ", digest has changed or was absent");
-		    }
+	        result = new ConfigInstallTask(toActivate, this.configAdminServiceTracker);
 		}
-	}
-
-	private String getDigestKey(RegisteredResource r) {
-        final ConfigurationPid cp = (ConfigurationPid)r.getAttributes().get(RegisteredResource.CONFIG_PID_ATTRIBUTE);
-        if(cp == null) {
-            throw new IllegalArgumentException("Resource does not provide a CONFIG_PID_ATTRIBUTE: " + r);
-        }
-        return cp.getCompositePid();
+		return result;
 	}
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallTask.java Fri Sep 10 07:41:55 2010
@@ -34,19 +34,12 @@ public class BundleInstallTask extends O
 
     private static final String BUNDLE_INSTALL_ORDER = "50-";
 
-    private final RegisteredResource resource;
-
     private final BundleTaskCreator creator;
 
     public BundleInstallTask(final RegisteredResource r,
             final BundleTaskCreator creator) {
+        super(r);
         this.creator = creator;
-        this.resource = r;
-    }
-
-    @Override
-    public String toString() {
-    	return getClass().getSimpleName() + ": " + resource;
     }
 
     /**
@@ -54,8 +47,8 @@ public class BundleInstallTask extends O
      */
     public void execute(final OsgiInstallerContext ctx) {
         int startLevel = 0;
-        final Object providedLevel = (this.resource.getDictionary() != null
-            ? this.resource.getDictionary().get(InstallableResource.BUNDLE_START_LEVEL) : null);
+        final Object providedLevel = (this.getResource().getDictionary() != null
+            ? this.getResource().getDictionary().get(InstallableResource.BUNDLE_START_LEVEL) : null);
         if ( providedLevel != null ) {
             if ( providedLevel instanceof Number ) {
                 startLevel = ((Number)providedLevel).intValue();
@@ -66,7 +59,7 @@ public class BundleInstallTask extends O
         // get the start level service (if possible) so we can set the initial start level
         final StartLevel startLevelService = this.creator.getStartLevel();
         try {
-            final Bundle b = this.creator.getBundleContext().installBundle(resource.getURL(), resource.getInputStream());
+            final Bundle b = this.creator.getBundleContext().installBundle(getResource().getURL(), getResource().getInputStream());
             // optionally set the start level
             if ( startLevel > 0 ) {
                 if (startLevelService != null) {
@@ -75,21 +68,21 @@ public class BundleInstallTask extends O
                     Logger.logWarn("Ignoring start level " + startLevel + " for bundle " + b + " - start level service not available.");
                 }
             }
-            final Version newVersion = new Version((String)resource.getAttributes().get(Constants.BUNDLE_VERSION));
-            this.creator.saveInstalledBundleInfo(b.getSymbolicName(), resource.getDigest(), newVersion.toString());
-            logExecution();
-            ctx.addTaskToCurrentCycle(new BundleStartTask(b.getBundleId(), this.creator));
-            return;
+            final Version newVersion = new Version((String)getResource().getAttributes().get(Constants.BUNDLE_VERSION));
+            this.creator.getBundleDigestStorage().putInfo(b.getSymbolicName(), getResource().getDigest(), newVersion.toString());
+
+            // mark this resource as installed and to be started
+            this.getResource().getAttributes().put(BundleTaskCreator.ATTR_START, "true");
+            ctx.addTaskToCurrentCycle(new BundleStartTask(getResource(), b.getBundleId(), this.creator));
         } catch (Exception ex) {
             // if something goes wrong we simply try it again
+            Logger.logDebug("Exception during install of bundle " + this.getResource() + " : " + ex.getMessage() + ". Retrying later.", ex);
             ctx.addTaskToNextCycle(this);
-            return;
         }
     }
 
     @Override
     public String getSortKey() {
-        return BUNDLE_INSTALL_ORDER + resource.getURL();
+        return BUNDLE_INSTALL_ORDER + getResource().getURL();
     }
-
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleRemoveTask.java Fri Sep 10 07:41:55 2010
@@ -18,6 +18,7 @@
  */
 package org.apache.sling.osgi.installer.impl.tasks;
 
+import org.apache.sling.osgi.installer.impl.Logger;
 import org.apache.sling.osgi.installer.impl.OsgiInstallerContext;
 import org.apache.sling.osgi.installer.impl.OsgiInstallerTask;
 import org.apache.sling.osgi.installer.impl.RegisteredResource;
@@ -33,28 +34,24 @@ public class BundleRemoveTask extends Os
 
     private static final String BUNDLE_REMOVE_ORDER = "30-";
 
-    private final RegisteredResource resource;
-
     private final BundleTaskCreator creator;
 
     public BundleRemoveTask(final RegisteredResource r,
-            final BundleTaskCreator creator) {
+                            final BundleTaskCreator creator) {
+        super(r);
         this.creator = creator;
-        this.resource = r;
-    }
-
-    @Override
-    public String toString() {
-    	return getClass().getSimpleName() + ": " + resource;
     }
 
-    @Override
+    /**
+     * @see org.apache.sling.osgi.installer.impl.OsgiInstallerTask#execute(org.apache.sling.osgi.installer.impl.OsgiInstallerContext)
+     */
     public void execute(OsgiInstallerContext ctx) {
-        logExecution();
-        final String symbolicName = (String)resource.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
+        final String symbolicName = (String)getResource().getAttributes().get(Constants.BUNDLE_SYMBOLICNAME);
         final Bundle b = this.creator.getMatchingBundle(symbolicName);
-        if(b == null) {
-            throw new IllegalStateException("Bundle to remove (" + symbolicName + ") not found");
+        if (b == null) {
+            // nothing to do, so just stop
+            this.getResource().setState(RegisteredResource.State.IGNORED);
+            return;
         }
         final int state = b.getState();
         try {
@@ -62,17 +59,17 @@ public class BundleRemoveTask extends Os
             	b.stop();
             }
             b.uninstall();
-        } catch (BundleException be) {
+            this.getResource().setState(RegisteredResource.State.UNINSTALLED);
+            ctx.addTaskToCurrentCycle(new SynchronousRefreshPackagesTask(this.creator));
+        } catch (final BundleException be) {
+            Logger.logDebug("Exception during removal of bundle " + this.getResource() + " : " + be.getMessage() + ". Retrying later.", be);
             ctx.addTaskToNextCycle(this);
-            return;
         }
-        ctx.addTaskToCurrentCycle(new SynchronousRefreshPackagesTask(this.creator));
-        return;
     }
 
     @Override
     public String getSortKey() {
-        return BUNDLE_REMOVE_ORDER + resource.getURL();
+        return BUNDLE_REMOVE_ORDER + getResource().getURL();
     }
 
 }

Modified: sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java?rev=995689&r1=995688&r2=995689&view=diff
==============================================================================
--- sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java (original)
+++ sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java Fri Sep 10 07:41:55 2010
@@ -22,8 +22,9 @@ import java.text.DecimalFormat;
 
 import org.apache.sling.osgi.installer.impl.Logger;
 import org.apache.sling.osgi.installer.impl.OsgiInstallerContext;
-import org.apache.sling.osgi.installer.impl.OsgiInstallerTask;
 import org.apache.sling.osgi.installer.impl.OsgiInstallerImpl;
+import org.apache.sling.osgi.installer.impl.OsgiInstallerTask;
+import org.apache.sling.osgi.installer.impl.RegisteredResource;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 
@@ -43,7 +44,8 @@ public class BundleStartTask extends Osg
 
 	private final BundleTaskCreator creator;
 
-	public BundleStartTask(final long bundleId, final BundleTaskCreator btc) {
+	public BundleStartTask(final RegisteredResource r, final long bundleId, final BundleTaskCreator btc) {
+	    super(r);
 		this.bundleId = bundleId;
 		this.creator = btc;
 		this.sortKey = BUNDLE_START_ORDER + new DecimalFormat("00000").format(bundleId);
@@ -63,6 +65,9 @@ public class BundleStartTask extends Osg
 	 * @see org.apache.sling.osgi.installer.impl.OsgiInstallerTask#execute(org.apache.sling.osgi.installer.impl.OsgiInstallerContext)
 	 */
 	public void execute(final OsgiInstallerContext ctx) {
+	    if ( this.getResource() != null ) {
+	        this.getResource().setState(RegisteredResource.State.INSTALLED);
+	    }
 	    // this is just a sanity check which should never be reached
         if (bundleId == 0) {
             Logger.logDebug("Bundle 0 is the framework bundle, ignoring request to start it");
@@ -88,7 +93,6 @@ public class BundleStartTask extends Osg
             return;
         }
         // Try to start bundle, and if that doesn't work we'll need to retry
-        logExecution();
         try {
             b.start();
             Logger.logInfo("Bundle started (retry count=" + retryCount + ", bundle ID=" + bundleId + ") " + b.getSymbolicName());