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 2011/12/13 10:57:46 UTC

svn commit: r1213634 - in /sling/trunk/installer/core: ./ src/main/java/org/apache/sling/installer/api/tasks/ src/main/java/org/apache/sling/installer/core/impl/ src/main/java/org/apache/sling/installer/core/impl/tasks/ src/test/java/org/apache/sling/i...

Author: cziegeler
Date: Tue Dec 13 09:57:45 2011
New Revision: 1213634

URL: http://svn.apache.org/viewvc?rev=1213634&view=rev
Log:
SLING-2289 : Implement generic retry mechanism

Added:
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java   (with props)
Modified:
    sling/trunk/installer/core/pom.xml
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallTask.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallationContext.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/Activator.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/InternalService.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/SortingServiceTracker.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleStartTask.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleTaskCreator.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/InstallerBundleUpdateTask.java
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/SystemBundleUpdateTask.java
    sling/trunk/installer/core/src/test/java/org/apache/sling/installer/core/impl/tasks/MockBundleTaskCreator.java

Modified: sling/trunk/installer/core/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/pom.xml?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/pom.xml (original)
+++ sling/trunk/installer/core/pom.xml Tue Dec 13 09:57:45 2011
@@ -62,7 +62,7 @@
 						<Export-Package>
 							org.apache.sling.installer.api;version=3.1.0,
                             org.apache.sling.installer.api.event;version=1.0.0,
-                            org.apache.sling.installer.api.tasks;version=1.1.0
+                            org.apache.sling.installer.api.tasks;version=1.2.0
                         </Export-Package>
 						<Private-Package>
 						    org.apache.sling.installer.core.impl.*

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallTask.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallTask.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallTask.java Tue Dec 13 09:57:45 2011
@@ -21,6 +21,15 @@ package org.apache.sling.installer.api.t
 /**
  * Base class for tasks that can be executed by the
  * {@link org.apache.sling.installer.api.OsgiInstaller}.
+ *
+ * The task is invoked by the installer through the {@link #execute(InstallationContext)}
+ * method. During execution the task should use the {@link #setFinishedState(ResourceState)}
+ * or {@link #setFinishedState(ResourceState, String)} method once the task is
+ * performed or the task decided that the task can never be performed.
+ *
+ * If the task needs to be retried, the implementation should just not alter the
+ * state at all. The installer will invoke the tasks at a later time again for
+ * retrying.
  */
 public abstract class InstallTask implements Comparable<InstallTask> {
 
@@ -95,7 +104,7 @@ public abstract class InstallTask implem
 
     @Override
 	public final boolean equals(Object o) {
-		if(o instanceof InstallTask) {
+		if (o instanceof InstallTask) {
 			return getSortKey().equals(((InstallTask)o).getSortKey());
 		}
 		return false;
@@ -109,7 +118,7 @@ public abstract class InstallTask implem
     /**
      * All comparisons are based on getSortKey().
      */
-    public final int compareTo(InstallTask o) {
+    public final int compareTo(final InstallTask o) {
         return getSortKey().compareTo(o.getSortKey());
     }
 }
\ No newline at end of file

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallationContext.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallationContext.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallationContext.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/InstallationContext.java Tue Dec 13 09:57:45 2011
@@ -42,12 +42,21 @@ public interface InstallationContext {
 	 * usually to indicate that a task must be retried
 	 * or the current task is finished and another task
 	 * has to be run.
+	 * @deprecated
 	 */
-	void addTaskToNextCycle(InstallTask t);
+	@Deprecated
+    void addTaskToNextCycle(InstallTask t);
 
 	/**
 	 * Make an entry into the audit log - this should be invoked
 	 * by the tasks whenever something has been installed/uninstalled etc.
 	 */
 	void log(String message, Object... args);
+
+    /**
+     * Add an async task.
+     * This adds a task for asynchronous execution.
+     * @since 1.2.0
+     */
+    void addAsyncTask(InstallTask t);
 }

Added: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java?rev=1213634&view=auto
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java (added)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java Tue Dec 13 09:57:45 2011
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.installer.api.tasks;
+
+/**
+ * The retry handler should be informed by external services
+ * whenever something in the system changed which might make
+ * it worth to retry a failed installed.
+ *
+ * The installer retries all unprocessed resources whenever this
+ * retry handler is notified to reschedule. For example a bundle
+ * listener listening for updates/installs/removals of bundles
+ * can inform the retry handler.
+ *
+ * @since 1.2
+ */
+public interface RetryHandler {
+
+    /**
+     * Schedule a retry.
+     */
+    void scheduleRetry();
+}

Propchange: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/api/tasks/RetryHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/Activator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/Activator.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/Activator.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/Activator.java Tue Dec 13 09:57:45 2011
@@ -168,7 +168,7 @@ public class Activator implements Bundle
             }
             if ( serviceInterfaces != null ) {
                 this.services.add(service);
-                service.init(context, this.osgiControllerService);
+                service.init(context, this.osgiControllerService, this.osgiControllerService);
                 this.registrations.add(context.registerService(
                         serviceInterfaces, service, props));
             }

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java Tue Dec 13 09:57:45 2011
@@ -25,6 +25,7 @@ import org.apache.sling.installer.api.In
 import org.apache.sling.installer.api.ResourceChangeListener;
 import org.apache.sling.installer.api.tasks.RegisteredResource;
 import org.apache.sling.installer.api.tasks.ResourceTransformer;
+import org.apache.sling.installer.api.tasks.RetryHandler;
 import org.apache.sling.installer.api.tasks.TransformationResult;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -43,9 +44,9 @@ public class DefaultTransformer
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
     /**
-     * @see org.apache.sling.installer.core.impl.InternalService#init(org.osgi.framework.BundleContext, org.apache.sling.installer.api.ResourceChangeListener)
+     * @see org.apache.sling.installer.core.impl.InternalService#init(org.osgi.framework.BundleContext, org.apache.sling.installer.api.ResourceChangeListener, RetryHandler)
      */
-    public void init(final BundleContext bctx, final ResourceChangeListener rcl) {
+    public void init(final BundleContext bctx, final ResourceChangeListener rcl, RetryHandler retryHandler) {
         // nothing to do
     }
 

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/InternalService.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/InternalService.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/InternalService.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/InternalService.java Tue Dec 13 09:57:45 2011
@@ -19,6 +19,7 @@
 package org.apache.sling.installer.core.impl;
 
 import org.apache.sling.installer.api.ResourceChangeListener;
+import org.apache.sling.installer.api.tasks.RetryHandler;
 import org.osgi.framework.BundleContext;
 
 /**
@@ -26,7 +27,7 @@ import org.osgi.framework.BundleContext;
  */
 public interface InternalService {
 
-    void init(BundleContext bctx, ResourceChangeListener listener);
+    void init(BundleContext bctx, ResourceChangeListener listener, RetryHandler retryHandler);
 
     void deactivate();
 

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java Tue Dec 13 09:57:45 2011
@@ -46,15 +46,12 @@ import org.apache.sling.installer.api.ta
 import org.apache.sling.installer.api.tasks.RegisteredResource;
 import org.apache.sling.installer.api.tasks.ResourceState;
 import org.apache.sling.installer.api.tasks.ResourceTransformer;
+import org.apache.sling.installer.api.tasks.RetryHandler;
 import org.apache.sling.installer.api.tasks.TaskResource;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
 import org.apache.sling.installer.api.tasks.TransformationResult;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleListener;
 import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -70,10 +67,7 @@ import org.slf4j.LoggerFactory;
  */
 public class OsgiInstallerImpl
     extends Thread
-    implements BundleListener, FrameworkListener, OsgiInstaller, ResourceChangeListener {
-
-    /** Internal key for tasks to be executed async. */
-    public static final String ASYNC_TASK_KEY = "EXECUTE TASK ASYNC";
+    implements OsgiInstaller, ResourceChangeListener, RetryHandler {
 
     /** The logger */
     private final Logger logger =  LoggerFactory.getLogger(this.getClass());
@@ -93,12 +87,15 @@ public class OsgiInstallerImpl
     /** Removed resources from clients. */
     private final Set<String> urlsToRemove = new HashSet<String>();
 
-    /** Tasks to be scheduled in the next iteration. */
-    private final SortedSet<InstallTask> tasksForNextCycle = new TreeSet<InstallTask>();
-
     /** Are we still activate? */
     private volatile boolean active = true;
 
+    /** Are we still running? */
+    private volatile boolean backgroundThreadIsRunning = false;
+
+    /** Flag indicating that a retry event occured during tasks executions. */
+    private volatile boolean retryDuringTaskExecution = false;
+
     /** The persistent resource list. */
     private PersistentResourceList persistentList;
 
@@ -116,7 +113,6 @@ public class OsgiInstallerImpl
 
     private final InstallListener listener;
 
-    private volatile boolean running = false;
 
     /** Constructor */
     public OsgiInstallerImpl(final BundleContext ctx) {
@@ -142,15 +138,11 @@ public class OsgiInstallerImpl
         this.factoryTracker.close();
         this.transformerTracker.close();
 
-        // remove as listener
-        ctx.removeBundleListener(this);
-        ctx.removeFrameworkListener(this);
-
         this.listener.dispose();
 
-        if ( this.running ) {
+        if ( this.backgroundThreadIsRunning ) {
             this.logger.debug("Waiting for installer thread to stop");
-            while ( this.running ) {
+            while ( this.backgroundThreadIsRunning ) {
                 try {
                     this.join(50L);
                 } catch (InterruptedException e) {
@@ -177,68 +169,62 @@ public class OsgiInstallerImpl
         this.updateHandlerTracker = new SortingServiceTracker<UpdateHandler>(ctx, UpdateHandler.class.getName(), null);
         this.updateHandlerTracker.open();
 
-        // listen to framework and bundle events
-        this.ctx.addFrameworkListener(this);
-        this.ctx.addBundleListener(this);
         setName(getClass().getSimpleName());
         this.logger.info("Apache Sling OSGi Installer Service started.");
     }
 
+    private static final InstallationEvent START_EVENT = new InstallationEvent() {
+
+        public TYPE getType() { return TYPE.STARTED; }
+
+        public Object getSource() { return null; }
+    };
+
+    private static final InstallationEvent SUSPENDED_EVENT = new InstallationEvent() {
+
+        public TYPE getType() { return TYPE.SUSPENDED; }
+
+        public Object getSource() { return null; }
+    };
+
     @Override
     public void run() {
-        this.running = true;
+        this.backgroundThreadIsRunning = true;
         try {
             this.init();
-            while (active) {
-                listener.onEvent(new InstallationEvent() {
-
-                    public TYPE getType() {
-                        return TYPE.STARTED;
-                    }
+            if ( this.active ) {
+                this.logger.debug("Starting installer");
+                this.listener.onEvent(START_EVENT);
+            }
 
-                    public Object getSource() {
-                        return null;
-                    }
-                });
-                logger.debug("Starting new installer cycle");
+            while (this.active) {
 
+                // merge potential new resources
                 this.mergeNewlyRegisteredResources();
 
                 // invoke transformers
                 this.transformResources();
 
-                // execute tasks
+                // Compute tasks
                 final SortedSet<InstallTask> tasks = this.computeTasks();
-                final boolean tasksCreated = !tasks.isEmpty();
-
                 // execute tasks and see if we have to stop processing
+                synchronized ( this.resourcesLock ) {
+                    this.retryDuringTaskExecution = false;
+                }
                 if ( this.executeTasks(tasks) ) {
-
-                    // clean up and save
-                    this.cleanupInstallableResources();
-
-                    // if we don't have any tasks, we go to sleep
-                    if (!tasksCreated) {
-                        synchronized ( this.resourcesLock ) {
-          		            // before we go to sleep, check if new resources arrived in the meantime
-                            if ( !this.hasNewResources() && this.active ) {
-                                // No tasks to execute - wait until new resources are
-                                // registered
-                                logger.debug("No tasks to process, going idle");
-                                listener.onEvent(new InstallationEvent() {
-
-                                    public TYPE getType() {
-                                        return TYPE.SUSPENDED;
-                                    }
-
-                                    public Object getSource() {
-                                        return null;
-                                    }
-                                });
-                                try {
-                                    this.resourcesLock.wait();
-                                } catch (InterruptedException ignore) {}
-                                logger.debug("Notified of new resources, back to work");
+                    synchronized ( this.resourcesLock ) {
+                        // before we go to sleep, check if new resources arrived in the meantime
+                        if ( !this.hasNewResources() && this.active && !this.retryDuringTaskExecution) {
+                            // No tasks to execute - wait until new resources are
+                            // registered
+                            logger.debug("No tasks to process, going idle");
+                            listener.onEvent(SUSPENDED_EVENT);
+                            try {
+                                this.resourcesLock.wait();
+                            } catch (final InterruptedException ignore) {}
+                            if ( active ) {
+                                listener.onEvent(START_EVENT);
+                                this.logger.debug("Running new installer cycle");
                             }
                         }
                     }
@@ -246,9 +232,19 @@ public class OsgiInstallerImpl
                     // stop processing
                     this.active = false;
                 }
+
             }
         } finally {
-            this.running = false;
+            this.backgroundThreadIsRunning = false;
+        }
+    }
+
+    /**
+     * Wake up the run cycle.
+     */
+    private void wakeUp() {
+        synchronized (this.resourcesLock) {
+            this.resourcesLock.notify();
         }
     }
 
@@ -430,6 +426,7 @@ public class OsgiInstallerImpl
             }
             this.newResourcesSchemes.clear();
             this.mergeNewResources();
+
             printResources("Merged");
             // persist list
             this.persistentList.save();
@@ -494,12 +491,6 @@ public class OsgiInstallerImpl
     private SortedSet<InstallTask> computeTasks() {
         final SortedSet<InstallTask> tasks = new TreeSet<InstallTask>();
 
-        // Add tasks that were scheduled for next cycle
-        synchronized (tasksForNextCycle) {
-            tasks.addAll(tasksForNextCycle);
-            tasksForNextCycle.clear();
-        }
-
         // Walk the list of entities, and create appropriate OSGi tasks for each group
         final List<InstallTaskFactory> services = this.factoryTracker.getSortedServices();
         if ( services.size() > 0 ) {
@@ -547,26 +538,26 @@ public class OsgiInstallerImpl
             final InstallationContext ctx = new InstallationContext() {
 
                 public void addTaskToNextCycle(final InstallTask t) {
-                    if ( t.getSortKey().equals(ASYNC_TASK_KEY) ) {
-                        logger.debug("Adding async task: {}", t);
-                        synchronized (asyncTasks) {
-                            asyncTasks.add(t);
-                        }
-                    } else {
-                        logger.debug("Adding task to next cycle: {}", t);
-                        synchronized (tasksForNextCycle) {
-                            tasksForNextCycle.add(t);
-                        }
+                    logger.warn("Deprecated method addTaskToNextCycle is called. Task is executed in this cycle(!): {}", t);
+                    synchronized ( tasks ) {
+                        tasks.add(t);
                     }
                 }
 
                 public void addTaskToCurrentCycle(final InstallTask t) {
-                    logger.debug("adding task to current cycle: {}", t);
+                    logger.debug("Adding task to current cycle: {}", t);
                     synchronized ( tasks ) {
                         tasks.add(t);
                     }
                 }
 
+                public void addAsyncTask(InstallTask t) {
+                    logger.debug("Adding async task: {}", t);
+                    synchronized (asyncTasks) {
+                        asyncTasks.add(t);
+                    }
+                }
+
                 public void log(String message, Object... args) {
                     auditLogger.info(message, args);
                 }
@@ -585,33 +576,26 @@ public class OsgiInstallerImpl
                 }
             }
             // save new state
-            persistentList.save();
+            this.cleanupInstallableResources();
 
             // let's check if we have async tasks and no other tasks
             if ( this.active && !asyncTasks.isEmpty() ) {
-                final boolean isEmpty;
-                synchronized ( tasksForNextCycle ) {
-                    isEmpty = tasksForNextCycle.isEmpty();
-                }
-                if ( isEmpty ) {
-                    // compact list and save if required
-                    this.cleanupInstallableResources();
-                    final InstallTask task = asyncTasks.get(0);
-                    final Thread t = new Thread() {
+                final InstallTask task = asyncTasks.get(0);
+                logger.debug("Executing async task: {}", task);
+                final Thread t = new Thread() {
 
-                        @Override
-                        public void run() {
-                            try {
-                                Thread.sleep(2000L);
-                            } catch (final InterruptedException ie) {
-                                // ignore
-                            }
-                            task.execute(ctx);
+                    @Override
+                    public void run() {
+                        try {
+                            Thread.sleep(2000L);
+                        } catch (final InterruptedException ie) {
+                            // ignore
                         }
-                    };
-                    t.start();
-                    return false;
-                }
+                        task.execute(ctx);
+                    }
+                };
+                t.start();
+                return false;
             }
 
         }
@@ -622,9 +606,8 @@ public class OsgiInstallerImpl
      * Clean up and compact
      */
     private void cleanupInstallableResources() {
-        if ( this.persistentList.compact() ) {
-            persistentList.save();
-        }
+        this.persistentList.compact();
+        this.persistentList.save();
         printResources("Compacted");
     }
 
@@ -680,64 +663,14 @@ public class OsgiInstallerImpl
         }
     }
 
-    /** If we have any tasks waiting to be retried, schedule their execution
-    private void scheduleRetries() {
-        final int toRetry;
-        synchronized ( tasksForNextCycle ) {
-            toRetry = tasksForNextCycle.size();
-        }
-        if (toRetry > 0) {
-            logger.debug("{} tasks scheduled for retrying", toRetry);
-            synchronized (newResources) {
-                newResources.notify();
-            }
-        }
-    }
-    */
-
     /**
-     * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
+     * @see org.apache.sling.installer.api.tasks.RetryHandler#scheduleRetry()
      */
-    public void bundleChanged(BundleEvent e) {
-        synchronized (LOCK) {
-            eventsCount++;
-        }
-        final int t = e.getType();
-        if (t == BundleEvent.INSTALLED || t == BundleEvent.RESOLVED || t == BundleEvent.STARTED || t == BundleEvent.UPDATED) {
-            logger.debug("Received BundleEvent that might allow installed bundles to start, scheduling retries if any");
-            // TODO - for now we always reschedule regardless if we have retries
-            // If the config task factory is only registered when config admin is available we can relax this again.
-            this.wakeUp();
-        }
-    }
-
-    private static volatile long eventsCount;
-
-    private static final Object LOCK = new Object();
-
-    /** Used for tasks that wait for a framework or bundle event before retrying their operations */
-    public static long getTotalEventsCount() {
-        synchronized (LOCK) {
-            return eventsCount;
-        }
-    }
-
-    /**
-     * @see org.osgi.framework.FrameworkListener#frameworkEvent(org.osgi.framework.FrameworkEvent)
-     */
-    public void frameworkEvent(final FrameworkEvent event) {
-        synchronized (LOCK) {
-            eventsCount++;
-        }
-    }
-
-    /**
-     * Wake up the run cycle.
-     */
-    public void wakeUp() {
-        synchronized (this.resourcesLock) {
-            this.resourcesLock.notify();
+    public void scheduleRetry() {
+        synchronized ( this.resourcesLock ) {
+            this.retryDuringTaskExecution = true;
         }
+        this.wakeUp();
     }
 
     /**
@@ -880,7 +813,7 @@ public class OsgiInstallerImpl
                 }
                 if ( updated || created ) {
                     this.persistentList.save();
-                    this.wakeUp();
+                    this.scheduleRetry();
                 }
 
             }
@@ -924,7 +857,7 @@ public class OsgiInstallerImpl
                                 erl.setFinishState(ResourceState.UNINSTALLED);
                                 erl.compact();
                                 this.persistentList.save();
-                                this.wakeUp();
+                                this.scheduleRetry();
                             } else {
                                 logger.debug("No handler found to handle remove of resource with scheme {}", tr.getScheme());
                             }
@@ -933,7 +866,7 @@ public class OsgiInstallerImpl
                         // if it has been ignored before, we activate it now again!
                         ((RegisteredResourceImpl)tr).setState(ResourceState.INSTALL);
                         this.persistentList.save();
-                        this.wakeUp();
+                        this.scheduleRetry();
                     }
                 }
             }

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/SortingServiceTracker.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/SortingServiceTracker.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/SortingServiceTracker.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/SortingServiceTracker.java Tue Dec 13 09:57:45 2011
@@ -23,6 +23,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.sling.installer.api.tasks.RetryHandler;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.osgi.util.tracker.ServiceTracker;
@@ -34,7 +35,7 @@ import org.osgi.util.tracker.ServiceTrac
 public class SortingServiceTracker<T>
     extends ServiceTracker  {
 
-    private final OsgiInstallerImpl listener;
+    private final RetryHandler listener;
 
     private int lastCount = -1;
 
@@ -49,7 +50,7 @@ public class SortingServiceTracker<T>
      */
     public SortingServiceTracker(final BundleContext context,
             final String clazz,
-            final OsgiInstallerImpl listener) {
+            final RetryHandler listener) {
         super(context, clazz, null);
         this.listener = listener;
     }
@@ -82,7 +83,7 @@ public class SortingServiceTracker<T>
         this.sortedReferences = null;
         if ( listener != null ) {
             // new factory or resource transformer has been added, wake up main thread
-            this.listener.wakeUp();
+            this.listener.scheduleRetry();
         }
         return context.getService(reference);
     }

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleStartTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleStartTask.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleStartTask.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleStartTask.java Tue Dec 13 09:57:45 2011
@@ -22,55 +22,44 @@ import java.text.DecimalFormat;
 
 import org.apache.sling.installer.api.tasks.InstallationContext;
 import org.apache.sling.installer.api.tasks.ResourceState;
-import org.apache.sling.installer.api.tasks.TaskResource;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
-import org.apache.sling.installer.core.impl.OsgiInstallerImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 
-/** Start a bundle given its bundle ID
- *  Restarts if the bundle does not start on the first try,
- *  but only after receiving a bundle or framework event,
- *  indicating that it's worth retrying
+/**
+ * Start a bundle given its bundle ID
+ * Restarts if the bundle does not start on the first try,
+ * but only after receiving a bundle or framework event,
+ * indicating that it's worth retrying
  */
 public class BundleStartTask extends AbstractBundleTask {
 
     private static final String BUNDLE_START_ORDER = "70-";
 
-    private static final String ATTR_RC = "bst:retryCount";
-    private static final String ATTR_EC = "bst:eventsCount";
-
     private final long bundleId;
-	private final String sortKey;
-	private long eventsCountForRetrying;
-	private int retryCount = 0;
+    private final String sortKey;
 
-		public BundleStartTask(final TaskResourceGroup r, final long bundleId, final BundleTaskCreator btc) {
-	    super(r, btc);
+    public BundleStartTask(final TaskResourceGroup r, final long bundleId, final BundleTaskCreator btc) {
+        super(r, btc);
         this.bundleId = bundleId;
         this.sortKey = BUNDLE_START_ORDER + new DecimalFormat("00000").format(bundleId);
-        final TaskResource rr = this.getResource();
-	    if ( rr != null && rr.getTemporaryAttribute(ATTR_RC) != null ) {
-	        this.retryCount = (Integer)rr.getTemporaryAttribute(ATTR_RC);
-            this.eventsCountForRetrying = (Long)rr.getTemporaryAttribute(ATTR_EC);
-	    }
-	}
-
-	@Override
-	public String getSortKey() {
-		return sortKey;
-	}
-
-	@Override
-	public String toString() {
-		return getClass().getSimpleName() + ": bundle " + bundleId;
-	}
-
-	/**
-	 * @see org.apache.sling.installer.api.tasks.InstallTask#execute(org.apache.sling.installer.api.tasks.InstallationContext)
-	 */
-	public void execute(final InstallationContext ctx) {
-	    // this is just a sanity check which should never be reached
+    }
+
+    @Override
+    public String getSortKey() {
+        return sortKey;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ": bundle " + bundleId;
+    }
+
+    /**
+     * @see org.apache.sling.installer.api.tasks.InstallTask#execute(org.apache.sling.installer.api.tasks.InstallationContext)
+     */
+    public void execute(final InstallationContext ctx) {
+        // this is just a sanity check which should never be reached
         if (bundleId == 0) {
             this.getLogger().debug("Bundle 0 is the framework bundle, ignoring request to start it");
             if ( this.getResource() != null ) {
@@ -79,23 +68,12 @@ public class BundleStartTask extends Abs
             return;
         }
 
-        // Do not execute this task if waiting for events
-        final long eventsCount = OsgiInstallerImpl.getTotalEventsCount();
-        if (eventsCount < eventsCountForRetrying) {
-            this.getLogger().debug("Task is not executable at this time, counters={}/{}",
-                    eventsCountForRetrying, eventsCount);
-            if ( this.getResource() == null ) {
-                ctx.addTaskToNextCycle(this);
-            }
+        final Bundle b = this.getBundleContext().getBundle(bundleId);
+        if (b == null) {
+            this.getLogger().info("Cannot start bundle, id not found: {}", bundleId);
             return;
         }
 
-        final Bundle b = this.getBundleContext().getBundle(bundleId);
-		if (b == null) {
-		    this.getLogger().info("Cannot start bundle, id not found: {}", bundleId);
-			return;
-		}
-
         final String fragmentHostHeader = getFragmentHostHeader(b);
         if (fragmentHostHeader != null) {
             this.getLogger().debug("Need to do a refresh of the bundle's host");
@@ -118,28 +96,12 @@ public class BundleStartTask extends Abs
             try {
                 b.start();
                 this.setFinishedState(ResourceState.INSTALLED);
-                this.getLogger().info("Bundle started (retry count={}, bundle ID={}) : {}",
-                        new Object[] {retryCount, bundleId, b.getSymbolicName()});
+                this.getLogger().info("Bundle started (bundle ID={}) : {}",
+                        new Object[] {bundleId, b.getSymbolicName()});
             } catch (final BundleException e) {
-                this.getLogger().info("Could not start bundle (retry count={}, bundle ID={}) : {}. Reason: {}. Will retry.",
-                        new Object[] {retryCount, bundleId, b.getSymbolicName(), e});
-
-                // Do the first retry immediately (in case "something" happenened right now
-                // that warrants a retry), but for the next ones wait for at least one bundle
-                // event or framework event
-                if (this.retryCount == 0) {
-                    this.eventsCountForRetrying = OsgiInstallerImpl.getTotalEventsCount();
-                } else {
-                    this.eventsCountForRetrying = OsgiInstallerImpl.getTotalEventsCount() + 1;
-                }
-                this.retryCount++;
-                if ( this.getResource() == null ) {
-                    ctx.addTaskToNextCycle(this);
-                } else {
-                    this.getResource().setTemporaryAttribute(ATTR_RC, this.retryCount);
-                    this.getResource().setTemporaryAttribute(ATTR_EC, this.eventsCountForRetrying);
-                }
+                this.getLogger().info("Could not start bundle (bundle ID={}) : {}. Reason: {}. Will retry.",
+                        new Object[] {bundleId, b.getSymbolicName(), e});
             }
-    	}
+        }
     }
 }

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleTaskCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleTaskCreator.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleTaskCreator.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleTaskCreator.java Tue Dec 13 09:57:45 2011
@@ -23,12 +23,17 @@ import org.apache.sling.installer.api.Re
 import org.apache.sling.installer.api.tasks.InstallTask;
 import org.apache.sling.installer.api.tasks.InstallTaskFactory;
 import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.api.tasks.RetryHandler;
 import org.apache.sling.installer.api.tasks.TaskResource;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
 import org.apache.sling.installer.core.impl.InternalService;
 import org.apache.sling.installer.core.impl.Util;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
 import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.Version;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.startlevel.StartLevel;
@@ -39,7 +44,8 @@ import org.slf4j.LoggerFactory;
 /**
  * Task creator for bundles
  */
-public class BundleTaskCreator implements InternalService, InstallTaskFactory {
+public class BundleTaskCreator
+    implements InternalService, InstallTaskFactory, FrameworkListener, BundleListener {
 
     /** If this property is set, the bundle is installed if the currently installed version
      * is the version specified by the property.
@@ -68,11 +74,19 @@ public class BundleTaskCreator implement
     /** The bundle context. */
     private BundleContext bundleContext;
 
+    /** The retry handler. */
+    private RetryHandler retryHandler;
+
     /**
-     * @see org.apache.sling.installer.core.impl.InternalService#init(org.osgi.framework.BundleContext, org.apache.sling.installer.api.ResourceChangeListener)
+     * @see org.apache.sling.installer.core.impl.InternalService#init(org.osgi.framework.BundleContext, org.apache.sling.installer.api.ResourceChangeListener, RetryHandler)
      */
-    public void init(final BundleContext bc, final ResourceChangeListener listener) {
+    public void init(final BundleContext bc, final ResourceChangeListener listener, final RetryHandler retryHandler) {
         this.bundleContext = bc;
+        this.retryHandler = retryHandler;
+
+        this.bundleContext.addBundleListener(this);
+        this.bundleContext.addFrameworkListener(this);
+
         // create and start tracker
         this.packageAdminTracker = new ServiceTracker(bc, PACKAGE_ADMIN_NAME, null);
         this.packageAdminTracker.open();
@@ -84,6 +98,10 @@ public class BundleTaskCreator implement
      * @see org.apache.sling.installer.core.impl.InternalService#deactivate()
      */
     public void deactivate() {
+        if ( this.bundleContext != null ) {
+            this.bundleContext.removeBundleListener(this);
+            this.bundleContext.removeFrameworkListener(this);
+        }
         if ( this.packageAdminTracker != null ) {
             this.packageAdminTracker.close();
             this.packageAdminTracker = null;
@@ -95,6 +113,27 @@ public class BundleTaskCreator implement
     }
 
     /**
+     * @see org.osgi.framework.FrameworkListener#frameworkEvent(org.osgi.framework.FrameworkEvent)
+     */
+    public void frameworkEvent(final FrameworkEvent event) {
+        if ( event.getType() == FrameworkEvent.PACKAGES_REFRESHED ) {
+            logger.debug("Received FrameworkEvent triggering a retry of the installer: {}", event);
+            this.retryHandler.scheduleRetry();
+        }
+    }
+
+    /**
+     * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
+     */
+    public void bundleChanged(final BundleEvent event) {
+        final int t = event.getType();
+        if (t == BundleEvent.INSTALLED || t == BundleEvent.RESOLVED || t == BundleEvent.STARTED || t == BundleEvent.UPDATED) {
+            logger.debug("Received BundleEvent triggering a retry of the installer: {}", event);
+            this.retryHandler.scheduleRetry();
+        }
+    }
+
+    /**
      * @see org.apache.sling.installer.core.impl.InternalService#getDescription()
      */
     public String getDescription() {

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/InstallerBundleUpdateTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/InstallerBundleUpdateTask.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/InstallerBundleUpdateTask.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/InstallerBundleUpdateTask.java Tue Dec 13 09:57:45 2011
@@ -22,11 +22,8 @@ import org.apache.sling.installer.api.ta
 import org.apache.sling.installer.api.tasks.InstallationContext;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
 import org.apache.sling.installer.core.impl.AbstractInstallTask;
-import org.apache.sling.installer.core.impl.OsgiInstallerImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.service.packageadmin.PackageAdmin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Update the installer itself
@@ -38,7 +35,7 @@ public class InstallerBundleUpdateTask e
     private final BundleTaskCreator creator;
 
     public InstallerBundleUpdateTask(final TaskResourceGroup r,
-                            final BundleTaskCreator creator) {
+                                     final BundleTaskCreator creator) {
         super(r);
         this.creator = creator;
     }
@@ -50,13 +47,11 @@ public class InstallerBundleUpdateTask e
         final Bundle b = this.creator.getBundleContext().getBundle();
         final PackageAdmin pa = this.creator.getPackageAdmin();
 
-        ctx.addTaskToNextCycle(new InstallTask(this.getResourceGroup()) {
-
-            private final Logger logger = LoggerFactory.getLogger(this.getClass());
+        ctx.addAsyncTask(new InstallTask(this.getResourceGroup()) {
 
             @Override
             public String getSortKey() {
-                return OsgiInstallerImpl.ASYNC_TASK_KEY;
+                return BUNDLE_UPDATE_ORDER + getResource().getEntityId();
             }
 
             @Override
@@ -75,9 +70,9 @@ public class InstallerBundleUpdateTask e
 
                         pa.refreshPackages( new Bundle[] { b } );
                     }
-                    logger.debug("Bundle updated: {}/{}", b.getBundleId(), b.getSymbolicName());
+                    getLogger().debug("Bundle updated: {}/{}", b.getBundleId(), b.getSymbolicName());
                 } catch (final Exception e) {
-                    logger.warn("Removing failing tasks - unable to retry: " + this, e);
+                    getLogger().warn("Removing failing tasks - unable to retry: " + this, e);
                 }
             }
         });

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/SystemBundleUpdateTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/SystemBundleUpdateTask.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/SystemBundleUpdateTask.java (original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/SystemBundleUpdateTask.java Tue Dec 13 09:57:45 2011
@@ -26,11 +26,8 @@ import org.apache.sling.installer.api.ta
 import org.apache.sling.installer.api.tasks.ResourceState;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
 import org.apache.sling.installer.core.impl.AbstractInstallTask;
-import org.apache.sling.installer.core.impl.OsgiInstallerImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Update the system bundle from a RegisteredResource.
@@ -58,13 +55,11 @@ public class SystemBundleUpdateTask exte
         // restart system bundle
         if ( this.getResource() == null ) {
             // do an async update
-            ctx.addTaskToNextCycle(new InstallTask(this.getResourceGroup()) {
-
-                private final Logger logger = LoggerFactory.getLogger(this.getClass());
+            ctx.addAsyncTask(new InstallTask(this.getResourceGroup()) {
 
                 @Override
                 public String getSortKey() {
-                    return OsgiInstallerImpl.ASYNC_TASK_KEY;
+                    return BUNDLE_UPDATE_ORDER + getResource().getURL();
                 }
 
                 @Override
@@ -72,7 +67,7 @@ public class SystemBundleUpdateTask exte
                     try {
                         systemBundle.update();
                     } catch (final BundleException e) {
-                        logger.warn("Updating system bundle failed - unable to retry: " + this, e);
+                        getLogger().warn("Updating system bundle failed - unable to retry: " + this, e);
                     }
                 }
             });
@@ -88,13 +83,11 @@ public class SystemBundleUpdateTask exte
                 // delayed system bundle update
                 final InputStream backgroundIS = is;
                 is = null;
-                ctx.addTaskToNextCycle(new InstallTask(this.getResourceGroup()) {
-
-                    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+                ctx.addAsyncTask(new InstallTask(this.getResourceGroup()) {
 
                     @Override
                     public String getSortKey() {
-                        return OsgiInstallerImpl.ASYNC_TASK_KEY;
+                        return BUNDLE_UPDATE_ORDER + getResource().getURL();
                     }
 
                     @Override
@@ -102,11 +95,11 @@ public class SystemBundleUpdateTask exte
                         try {
                             systemBundle.update(backgroundIS);
                         } catch (final BundleException e) {
-                            logger.warn("Updating system bundle failed - unable to retry: " + this, e);
+                            getLogger().warn("Updating system bundle failed - unable to retry: " + this, e);
                         } finally {
                             try {
                                 backgroundIS.close();
-                            } catch (IOException ignore) {}
+                            } catch (final IOException ignore) {}
                         }
                     }
                 });
@@ -117,7 +110,7 @@ public class SystemBundleUpdateTask exte
                 if ( is != null ) {
                     try {
                         is.close();
-                    } catch (IOException ignore) {}
+                    } catch (final IOException ignore) {}
                 }
             }
         }

Modified: sling/trunk/installer/core/src/test/java/org/apache/sling/installer/core/impl/tasks/MockBundleTaskCreator.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/test/java/org/apache/sling/installer/core/impl/tasks/MockBundleTaskCreator.java?rev=1213634&r1=1213633&r2=1213634&view=diff
==============================================================================
--- sling/trunk/installer/core/src/test/java/org/apache/sling/installer/core/impl/tasks/MockBundleTaskCreator.java (original)
+++ sling/trunk/installer/core/src/test/java/org/apache/sling/installer/core/impl/tasks/MockBundleTaskCreator.java Tue Dec 13 09:57:45 2011
@@ -31,7 +31,7 @@ class MockBundleTaskCreator extends Bund
     private final Map<String, BundleInfo> fakeBundleInfo = new HashMap<String, BundleInfo>();
 
     public MockBundleTaskCreator() throws IOException {
-        this.init(new MockBundleContext(), null);
+        this.init(new MockBundleContext(), null, null);
     }
 
     void addBundleInfo(String symbolicName, String version, int state) {