Author: bdelacretaz Date: Fri Jul 10 16:21:25 2009 New Revision: 793010 URL: http://svn.apache.org/viewvc?rev=793010&view=rev Log: SLING-1042 - Optimize jcrinstall retry cycles
Added: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java (with props) sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java (with props) sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java (with props) Modified: sling/trunk/contrib/extensions/jcrinstall/it/src/test/java/org/apache/sling/jcr/jcrinstall/it/OsgiControllerTest.java sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerTask.java sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java Modified: sling/trunk/contrib/extensions/jcrinstall/it/src/test/java/org/apache/sling/jcr/jcrinstall/it/OsgiControllerTest.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/it/src/test/java/org/apache/sling/jcr/jcrinstall/it/OsgiControllerTest.java?rev=793010&r1=793009&r2=793010&view=diff ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/it/src/test/java/org/apache/sling/jcr/jcrinstall/it/OsgiControllerTest.java (original) +++ sling/trunk/contrib/extensions/jcrinstall/it/src/test/java/org/apache/sling/jcr/jcrinstall/it/OsgiControllerTest.java Fri Jul 10 16:21:25 2009 @@ -30,12 +30,15 @@ import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.vmOption; import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; import java.util.Dictionary; import java.util.Hashtable; import org.apache.sling.osgi.installer.DictionaryInstallableData; import org.apache.sling.osgi.installer.OsgiController; import org.apache.sling.osgi.installer.OsgiControllerServices; +import org.apache.sling.osgi.installer.OsgiControllerStatistics; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Inject; @@ -43,9 +46,12 @@ import org.ops4j.pax.exam.junit.JUnit4TestRunner; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; import org.osgi.framework.ServiceReference; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.packageadmin.PackageAdmin; /** Test the OsgiController running in the OSGi framework * @@ -55,9 +61,10 @@ * */ @RunWith(JUnit4TestRunner.class) -public class OsgiControllerTest { +public class OsgiControllerTest implements FrameworkListener { public final static String POM_VERSION = System.getProperty("jcrinstall.pom.version"); public final static String JAR_EXT = ".jar"; + private int packageRefreshEventsCount; @Inject protected BundleContext bundleContext; @@ -71,6 +78,61 @@ return result; } + protected void generateBundleEvent() throws Exception { + // install a bundle manually to generate a bundle event + final File f = getTestBundle("org.apache.sling.jcr.jcrinstall.it-" + POM_VERSION + "-testbundle-1.0.jar"); + final InputStream is = new FileInputStream(f); + Bundle b = null; + try { + b = bundleContext.installBundle(getClass().getName(), is); + b.start(); + final long timeout = System.currentTimeMillis() + 2000L; + while(b.getState() != Bundle.ACTIVE && System.currentTimeMillis() < timeout) { + Thread.sleep(10L); + } + } finally { + if(is != null) { + is.close(); + } + if(b != null) { + b.uninstall(); + } + } + } + + public void frameworkEvent(FrameworkEvent event) { + if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) { + packageRefreshEventsCount++; + } + } + + protected void refreshPackages() { + bundleContext.addFrameworkListener(this); + final int MAX_REFRESH_PACKAGES_WAIT_SECONDS = 5; + final int targetEventCount = packageRefreshEventsCount + 1; + final long timeout = System.currentTimeMillis() + MAX_REFRESH_PACKAGES_WAIT_SECONDS * 1000L; + + final PackageAdmin pa = getService(PackageAdmin.class); + pa.refreshPackages(null); + + try { + while(true) { + if(System.currentTimeMillis() > timeout) { + break; + } + if(packageRefreshEventsCount >= targetEventCount) { + break; + } + try { + Thread.sleep(250L); + } catch(InterruptedException ignore) { + } + } + } finally { + bundleContext.removeFrameworkListener(this); + } + } + protected Configuration findConfiguration(String pid) throws Exception { final ConfigurationAdmin ca = getService(ConfigurationAdmin.class); if(ca != null) { @@ -311,6 +373,50 @@ assertFalse(needsB + " must not be started, testB not present", b.getState() == Bundle.ACTIVE); } + // Check SLING-1042 retry rules + assertTrue("OsgiController must implement OsgiControllerStatistics", c instanceof OsgiControllerStatistics); + final OsgiControllerStatistics stats = (OsgiControllerStatistics)c; + + { + long n = stats.getExecutedTasksCount(); + c.executeScheduledOperations(); + assertTrue("First retry must not wait for an event", stats.getExecutedTasksCount() > n); + n = stats.getExecutedTasksCount(); + c.executeScheduledOperations(); + assertEquals("Retrying before a bundle event happens must not execute any OsgiControllerTask", n, stats.getExecutedTasksCount()); + + n = stats.getExecutedTasksCount(); + generateBundleEvent(); + c.executeScheduledOperations(); + assertTrue("Retrying after a bundle event must execute at least one OsgiControllerTask", stats.getExecutedTasksCount() > n); + } + + { + // wait until no more events are received + final long timeout = System.currentTimeMillis() + 2000L; + while(System.currentTimeMillis() < timeout) { + final long n = stats.getExecutedTasksCount(); + c.executeScheduledOperations(); + if(n == stats.getExecutedTasksCount()) { + break; + } + Thread.sleep(10L); + } + + if(System.currentTimeMillis() >= timeout) { + fail("Retries did not stop within specified time"); + } + } + + { + long n = stats.getExecutedTasksCount(); + c.executeScheduledOperations(); + assertEquals("Retrying before a framework event happens must not execute any OsgiControllerTask", n, stats.getExecutedTasksCount()); + refreshPackages(); + c.executeScheduledOperations(); + assertTrue("Retrying after framework event must execute at least one OsgiControllerTask", stats.getExecutedTasksCount() > n); + } + // now install testB -> needsB must start { c.scheduleInstallOrUpdate(testB + JAR_EXT, Added: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java?rev=793010&view=auto ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java (added) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java Fri Jul 10 16:21:25 2009 @@ -0,0 +1,25 @@ +/* + * 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.osgi.installer; + +/** Statistics for the OsgiController */ +public interface OsgiControllerStatistics { + /** How many OsgiControllerTask the controller tried to execute */ + long getExecutedTasksCount(); +} Propchange: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/OsgiControllerStatistics.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Modified: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java?rev=793010&r1=793009&r2=793010&view=diff ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java (original) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java Fri Jul 10 16:21:25 2009 @@ -39,14 +39,12 @@ private static String LOG_SERVICE_NAME = LogService.class.getName(); private ServiceTracker startLevelTracker; - private ServiceTracker packageAdminTracker; - private ServiceTracker logServiceTracker; - - private OsgiControllerImpl service; - - private ServiceRegistration serviceReg; + private OsgiControllerImpl osgiControllerService; + private ServiceRegistration osgiControllerServiceReg; + private EventsCounterImpl eventsCounter; + private ServiceRegistration eventsCounterServiceReg; /** * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) @@ -59,21 +57,36 @@ this.packageAdminTracker.open(); this.logServiceTracker.open(); - // register service - final Hashtable<String, String> props = new Hashtable<String, String>(); - props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Install Controller Service"); - props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + // register OsgiController service + { + final Hashtable<String, String> props = new Hashtable<String, String>(); + props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Install Controller Service"); + props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + + // Assume PackageAdmin is available before this bundle is started. + // That's the case when using Felix OSGi, not sure about other frameworks. + this.osgiControllerService = new OsgiControllerImpl(context, + (PackageAdmin)checkNotNull(this.packageAdminTracker.getService(), "PackageAdmin"), + logServiceTracker); + final String [] serviceInterfaces = { + OsgiController.class.getName(), + OsgiControllerServices.class.getName() + }; + osgiControllerServiceReg = context.registerService(serviceInterfaces, osgiControllerService, props); + } - // Assume PackageAdmin is available before this bundle is started. - // That's the case when using Felix OSGi, not sure about other frameworks. - this.service = new OsgiControllerImpl(context, - (PackageAdmin)checkNotNull(this.packageAdminTracker.getService(), "PackageAdmin"), - logServiceTracker); - final String [] serviceInterfaces = { - OsgiController.class.getName(), - OsgiControllerServices.class.getName() - }; - serviceReg = context.registerService(serviceInterfaces, service, props); + // register EventsCounter service + { + final Hashtable<String, String> props = new Hashtable<String, String>(); + props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling EventsCounter Service"); + props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation"); + + this.eventsCounter = new EventsCounterImpl(context); + final String [] serviceInterfaces = { + EventsCounter.class.getName() + }; + eventsCounterServiceReg = context.registerService(serviceInterfaces, eventsCounter, props); + } } /** Complain if value is null */ @@ -88,13 +101,21 @@ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { - if ( this.serviceReg != null ) { - this.serviceReg.unregister(); - this.serviceReg = null; - } - if ( this.service != null ) { - this.service.deactivate(); - this.service = null; + if( this.eventsCounterServiceReg != null) { + this.eventsCounterServiceReg.unregister(); + this.eventsCounterServiceReg = null; + } + if( this.eventsCounter != null) { + this.eventsCounter.deactivate(); + this.eventsCounter = null; + } + if ( this.osgiControllerServiceReg != null ) { + this.osgiControllerServiceReg.unregister(); + this.osgiControllerServiceReg = null; + } + if ( this.osgiControllerService != null ) { + this.osgiControllerService.deactivate(); + this.osgiControllerService = null; } if ( this.startLevelTracker != null ) { this.startLevelTracker.close(); Added: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java?rev=793010&view=auto ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java (added) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java Fri Jul 10 16:21:25 2009 @@ -0,0 +1,24 @@ +/* + * 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.osgi.installer.impl; + +/** Count framework + bundle events - used for SLING-1042 retries optimization */ +public interface EventsCounter { + long getTotalEventsCount(); +} Propchange: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounter.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Added: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java?rev=793010&view=auto ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java (added) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java Fri Jul 10 16:21:25 2009 @@ -0,0 +1,59 @@ +/* + * 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.osgi.installer.impl; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; + +/** EventsCounter implementation - simply counts events, + * to avoid having to make each BundleStartTask that's + * waiting for retries a FrameworkListener and BundleListener + */ +class EventsCounterImpl implements EventsCounter, FrameworkListener, BundleListener { + private long eventsCount; + private final BundleContext bundleContext; + + EventsCounterImpl(BundleContext bc) { + this.bundleContext = bc; + bundleContext.addBundleListener(this); + bundleContext.addFrameworkListener(this); + } + + void deactivate() { + bundleContext.removeBundleListener(this); + bundleContext.removeFrameworkListener(this); + } + + public long getTotalEventsCount() { + return eventsCount; + } + + public void frameworkEvent(FrameworkEvent arg0) { + // we'll retry as soon as any FrameworkEvent or BundleEvent happens + eventsCount++; + } + + public void bundleChanged(BundleEvent arg0) { + // we'll retry as soon as any FrameworkEvent or BundleEvent happens + eventsCount++; + } +} Propchange: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/EventsCounterImpl.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Modified: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java?rev=793010&r1=793009&r2=793010&view=diff ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java (original) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java Fri Jul 10 16:21:25 2009 @@ -29,6 +29,7 @@ import org.apache.sling.osgi.installer.JcrInstallException; import org.apache.sling.osgi.installer.OsgiController; import org.apache.sling.osgi.installer.OsgiControllerServices; +import org.apache.sling.osgi.installer.OsgiControllerStatistics; import org.apache.sling.osgi.installer.ResourceOverrideRules; import org.apache.sling.osgi.installer.impl.tasks.BundleInstallRemoveTask; import org.apache.sling.osgi.installer.impl.tasks.ConfigInstallRemoveTask; @@ -46,7 +47,8 @@ public class OsgiControllerImpl implements OsgiController, OsgiControllerServices, - OsgiControllerTaskContext { + OsgiControllerTaskContext, + OsgiControllerStatistics { private final BundleContext bundleContext; private final Storage storage; @@ -59,6 +61,7 @@ private final ServiceTracker logServiceTracker; private int threadCounter; private final PackageAdmin packageAdmin; + private int executedTasksCount; public static final String BUNDLE_EXTENSION = ".jar"; public static final String STORAGE_FILENAME = "controller.storage"; @@ -144,9 +147,28 @@ if(tasks.isEmpty()) { return; } - + + // No executable tasks? + boolean exec = false; + synchronized (tasks) { + for(OsgiControllerTask t : tasks) { + if(t.isExecutable(this)) { + exec = true; + break; + } + } + } + if(getLogService() != null) { - getLogService().log(LogService.LOG_INFO, "executeScheduledOperations() starts"); + if(exec) { + getLogService().log(LogService.LOG_INFO, "executeScheduledOperations() starts"); + } else { + getLogService().log(LogService.LOG_DEBUG, "No executable tasks, nothing to do"); + } + } + + if(!exec) { + return; } synchronized (tasks) { @@ -199,6 +221,7 @@ final List<OsgiControllerTask> toRemove = new LinkedList<OsgiControllerTask>(); for(OsgiControllerTask t : tasks) { toRemove.add(t); + executedTasksCount++; executeTask(t); if(!tasksForThisCycle.isEmpty()) { break; @@ -272,4 +295,7 @@ return this; } + public long getExecutedTasksCount() { + return executedTasksCount; + } } \ No newline at end of file Modified: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerTask.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerTask.java?rev=793010&r1=793009&r2=793010&view=diff ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerTask.java (original) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerTask.java Fri Jul 10 16:21:25 2009 @@ -26,9 +26,15 @@ /** Tasks are sorted according to this key */ public abstract String getSortKey(); + /** All comparisons are based on getSortKey() */ public final int compareTo(OsgiControllerTask o) { return getSortKey().compareTo(o.getSortKey()); } + + /** Is it worth executing this task now? */ + public boolean isExecutable(OsgiControllerTaskContext ctx) throws Exception { + return true; + } @Override public final boolean equals(Object o) { Modified: sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java?rev=793010&r1=793009&r2=793010&view=diff ============================================================================== --- sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java (original) +++ sling/trunk/contrib/extensions/jcrinstall/osgi/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleStartTask.java Fri Jul 10 16:21:25 2009 @@ -20,10 +20,14 @@ import java.text.DecimalFormat; +import org.apache.sling.osgi.installer.JcrInstallException; +import org.apache.sling.osgi.installer.impl.EventsCounter; import org.apache.sling.osgi.installer.impl.OsgiControllerTask; import org.apache.sling.osgi.installer.impl.OsgiControllerTaskContext; import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; +import org.osgi.framework.ServiceReference; import org.osgi.service.log.LogService; /** Task that starts a bundle */ @@ -31,6 +35,8 @@ private final long bundleId; private final String sortKey; + private long eventsCountForRetrying; + private int retryCount = 0; public BundleStartTask(long bundleId) { this.bundleId = bundleId; @@ -50,6 +56,7 @@ public void execute(OsgiControllerTaskContext tctx) throws Exception { final Bundle b = tctx.getBundleContext().getBundle(bundleId); final LogService log = tctx.getOsgiControllerServices().getLogService(); + boolean needToRetry = false; if(b == null) { if(log != null) { @@ -58,24 +65,70 @@ return; } - if(b.getState() == Bundle.ACTIVE) { - if(log != null) { - log.log(LogService.LOG_DEBUG, "Bundle already started, no action taken:" + bundleId + "/" + b.getSymbolicName()); - } - } else { - try { - b.start(); - if(log != null) { - log.log(LogService.LOG_INFO, "Bundle started:" + bundleId + "/" + b.getSymbolicName()); - } - } catch(BundleException e) { - if(log != null) { - log.log(LogService.LOG_INFO, - "Could not start bundle (" + e + "), will retry: " + bundleId + "/" + b.getSymbolicName()); - } - tctx.addTaskToNextCycle(this); - } - + try { + if(b.getState() == Bundle.ACTIVE) { + if(log != null) { + log.log(LogService.LOG_DEBUG, "Bundle already started, no action taken:" + bundleId + "/" + b.getSymbolicName()); + } + } else { + // Try to start bundle, and if that doesn't work we'll need to retry + try { + b.start(); + if(log != null) { + log.log(LogService.LOG_INFO, + "Bundle started (retry count=" + retryCount + ", bundle ID=" + bundleId + ") " + b.getSymbolicName()); + } + } catch(BundleException e) { + if(log != null) { + log.log(LogService.LOG_INFO, + "Could not start bundle (retry count=" + retryCount + ", " + e + + "), will retry: " + bundleId + "/" + b.getSymbolicName()); + } + needToRetry = true; + } + + } + } finally { + if(needToRetry) { + + // 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(retryCount == 0) { + eventsCountForRetrying = getEventsCount(tctx.getBundleContext()); + } else { + eventsCountForRetrying = getEventsCount(tctx.getBundleContext()) + 1; + } + + tctx.addTaskToNextCycle(this); + } } + retryCount++; } + + /** Do not execute this task if waiting for events */ + public boolean isExecutable(OsgiControllerTaskContext tctx) throws JcrInstallException { + final long eventsCount = getEventsCount(tctx.getBundleContext()); + final boolean result = eventsCount >= eventsCountForRetrying; + if(!result) { + if(tctx.getOsgiControllerServices().getLogService() != null) { + tctx.getOsgiControllerServices().getLogService().log(LogService.LOG_DEBUG, + this + " is not executable at this time, counters=" + eventsCountForRetrying + "/" + eventsCount); + } + } + return result; + } + + /** Return current events count */ + protected long getEventsCount(BundleContext bc) throws JcrInstallException { + final ServiceReference sr = bc.getServiceReference(EventsCounter.class.getName()); + if(sr == null) { + throw new JcrInstallException("EventsCounter service not found"); + } + final EventsCounter ec = (EventsCounter)bc.getService(sr); + if(ec == null) { + throw new JcrInstallException("EventsCounter service not found, although its ServiceReference was found"); + } + return ec.getTotalEventsCount(); + } }