Return-Path: Delivered-To: apmail-incubator-sling-commits-archive@locus.apache.org Received: (qmail 45396 invoked from network); 28 Oct 2008 14:57:03 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 28 Oct 2008 14:57:03 -0000 Received: (qmail 46688 invoked by uid 500); 28 Oct 2008 14:57:08 -0000 Delivered-To: apmail-incubator-sling-commits-archive@incubator.apache.org Received: (qmail 46662 invoked by uid 500); 28 Oct 2008 14:57:08 -0000 Mailing-List: contact sling-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sling-dev@incubator.apache.org Delivered-To: mailing list sling-commits@incubator.apache.org Received: (qmail 46653 invoked by uid 99); 28 Oct 2008 14:57:08 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 28 Oct 2008 07:57:08 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 28 Oct 2008 14:55:55 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 54FEB2388988; Tue, 28 Oct 2008 07:56:05 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r708590 - in /incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi: OsgiResourceProcessor.java impl/BundleResourceProcessor.java impl/ConfigResourceProcessor.java impl/OsgiControllerImpl.java Date: Tue, 28 Oct 2008 14:56:05 -0000 To: sling-commits@incubator.apache.org From: fmeschbe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20081028145605.54FEB2388988@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: fmeschbe Date: Tue Oct 28 07:56:04 2008 New Revision: 708590 URL: http://svn.apache.org/viewvc?rev=708590&view=rev Log: SLING-709 simplify BundleResourceProcessor tasks: * stop bundles before update (start them later again) * only refreshPackages if bundles have been updated or removed * record active bundles before refreshPackages * start stopped/installed/updated bundles after refreshPackages * if refreshPackages is not called, directly start installed/updated bundles Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/ConfigResourceProcessor.java incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java?rev=708590&r1=708589&r2=708590&view=diff ============================================================================== --- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java (original) +++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/OsgiResourceProcessor.java Tue Oct 28 07:56:04 2008 @@ -50,4 +50,9 @@ * to start outstanding bundles. */ void processResourceQueue() throws Exception; + + /** + * Called to cleanup the resource processor when it is not needed anymore. + */ + void dispose(); } Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java?rev=708590&r1=708589&r2=708590&view=diff ============================================================================== --- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java (original) +++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/BundleResourceProcessor.java Tue Oct 28 07:56:04 2008 @@ -26,10 +26,14 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.jar.JarInputStream; import java.util.jar.Manifest; @@ -38,13 +42,16 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; 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.slf4j.Logger; import org.slf4j.LoggerFactory; /** OsgiResourceProcessor for Bundles */ -public class BundleResourceProcessor implements OsgiResourceProcessor { +public class BundleResourceProcessor implements OsgiResourceProcessor, + FrameworkListener { public static final String BUNDLE_EXTENSION = ".jar"; @@ -52,95 +59,155 @@ public static final String KEY_BUNDLE_ID = "bundle.id"; private final BundleContext ctx; + private final PackageAdmin packageAdmin; - private final Map pendingBundles; + + /** + * All bundles which were active before {@link #processResourceQueue()} + * refreshes the packages. Bundles which may not be started after refreshing + * the packages remain in this set. In addition bundles from the + * {@link #installedBundles} bundles list will be added here to try to start + * them in the next round. + */ + private final Set activeBundles; + + /** + * The list of bundles which have been updated or installed and which need + * to be started in the next round. Bundles from this list, which fail + * to start, are added to the {@link #activeBundles} set for them to be + * started in the next round. + */ + private final List installedBundles; + + /** + * Flag set by {@link #installOrUpdate(String, Map, InputStream)} of a + * bundle has been updated and by {@link #uninstall(String, Map)} if a + * bundle has been removed. This causes the {@link #processResourceQueue()} + * method to refresh the packages. + * @see #processResourceQueue() + */ + private boolean needsRefresh; + private final Logger log = LoggerFactory.getLogger(this.getClass()); BundleResourceProcessor(BundleContext ctx, PackageAdmin packageAdmin) { this.ctx = ctx; this.packageAdmin = packageAdmin; - pendingBundles = new HashMap(); + this.activeBundles = new HashSet(); + this.installedBundles = new ArrayList(); + + // register as a framework listener + ctx.addFrameworkListener(this); + } + + public void dispose() { + // unregister as a framework listener + ctx.removeFrameworkListener(this); } + // ---------- FrameworkListener + + /** + * Handles the PACKAGES_REFRESHED framework event which is sent after + * the PackageAdmin.refreshPackages has finished its work of refreshing + * the packages. When packages have been refreshed all bundles which are + * expected to be active (those active before refreshing the packages and + * newly installed or updated bundles) are started. + */ + public void frameworkEvent(FrameworkEvent event) { + if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) { + startBundles(); + } + } + + // ---------- OsgiResourceProcessor + /** * @throws BundleException - * @see org.apache.sling.jcr.jcrinstall.osgi.OsgiResourceProcessor#installOrUpdate(java.lang.String, java.util.Map, java.io.InputStream) + * @see org.apache.sling.jcr.jcrinstall.osgi.OsgiResourceProcessor#installOrUpdate(java.lang.String, + * java.util.Map, java.io.InputStream) */ - public int installOrUpdate(String uri, Map attributes, InputStream data) - throws BundleException, IOException { + public int installOrUpdate(String uri, Map attributes, + InputStream data) throws BundleException, IOException { // Update if we already have a bundle id, else install Bundle b = null; boolean updated = false; - synchronized (pendingBundles) { - // check whether we know the bundle and it exists - final Long longId = (Long)attributes.get(KEY_BUNDLE_ID); - if (longId != null) { - b = ctx.getBundle(longId); - } + // check whether we know the bundle and it exists + final Long longId = (Long) attributes.get(KEY_BUNDLE_ID); + if (longId != null) { + b = ctx.getBundle(longId); + } - // either we don't know the bundle yet or it does not exist, - // so check whether the bundle can be found by its symbolic name - if (b == null) { - // ensure we can mark and reset to read the manifest - if (!data.markSupported()) { - data = new BufferedInputStream(data); - } - final BundleInfo info = getMatchingBundle(data); - if ( info != null ) { - final Version availableVersion = new Version((String)info.bundle.getHeaders().get(Constants.BUNDLE_VERSION)); - final Version newVersion = new Version(info.newVersion); - if ( newVersion.compareTo(availableVersion) > 0 ) { - b = info.bundle; - } else { - log.debug("Ignore update of bundle {} from {} as the installed version is equal or higher.", info.bundle.getSymbolicName(), uri); - return IGNORED; - } - } + // either we don't know the bundle yet or it does not exist, + // so check whether the bundle can be found by its symbolic name + if (b == null) { + // ensure we can mark and reset to read the manifest + if (!data.markSupported()) { + data = new BufferedInputStream(data); } - - if (b != null) { - b.update(data); - updated = true; - // wait a little bit after an update - try { - Thread.sleep(1500); - } catch (InterruptedException e) { - // ignore + final BundleInfo info = getMatchingBundle(data); + if (info != null) { + final Version availableVersion = new Version( + (String) info.bundle.getHeaders().get( + Constants.BUNDLE_VERSION)); + final Version newVersion = new Version(info.newVersion); + if (newVersion.compareTo(availableVersion) > 0) { + b = info.bundle; + } else { + log.debug( + "Ignore update of bundle {} from {} as the installed version is equal or higher.", + info.bundle.getSymbolicName(), uri); + return IGNORED; } - } else { - uri = OsgiControllerImpl.getResourceLocation(uri); - log.debug("No matching Bundle for uri {}, installing", uri); - b = ctx.installBundle(uri, data); } + } + + if (b != null) { + b.stop(); + b.update(data); + updated = true; + needsRefresh = true; + } else { + uri = OsgiControllerImpl.getResourceLocation(uri); + log.debug("No matching Bundle for uri {}, installing", uri); + b = ctx.installBundle(uri, data); + } - // ensure the bundle id in the attributes, this may be overkill - // in simple update situations, but is required for installations - // and updates where there are no attributes yet - attributes.put(KEY_BUNDLE_ID, b.getBundleId()); + // ensure the bundle id in the attributes, this may be overkill + // in simple update situations, but is required for installations + // and updates where there are no attributes yet + attributes.put(KEY_BUNDLE_ID, b.getBundleId()); - pendingBundles.put(b.getBundleId(), b); + synchronized (activeBundles) { + installedBundles.add(b.getBundleId()); } return updated ? UPDATED : INSTALLED; } /** - * @see org.apache.sling.jcr.jcrinstall.osgi.OsgiResourceProcessor#uninstall(java.lang.String, java.util.Map) + * @see org.apache.sling.jcr.jcrinstall.osgi.OsgiResourceProcessor#uninstall(java.lang.String, + * java.util.Map) */ - public void uninstall(String uri, Map attributes) throws BundleException { - final Long longId = (Long)attributes.get(KEY_BUNDLE_ID); + public void uninstall(String uri, Map attributes) + throws BundleException { + final Long longId = (Long) attributes.get(KEY_BUNDLE_ID); if (longId == null) { - log.debug("No bundle id in metadata for {}, bundle cannot be uninstalled.", uri); + log.debug( + "No bundle id in metadata for {}, bundle cannot be uninstalled.", + uri); } else { final Bundle b = ctx.getBundle(longId); if (b == null) { - log.debug("Bundle having id {} not found, cannot uninstall", longId); + log.debug("Bundle having id {} not found, cannot uninstall", + longId); } else { - synchronized(pendingBundles) { - pendingBundles.remove(b.getBundleId()); + synchronized (installedBundles) { + installedBundles.remove(b.getBundleId()); } b.uninstall(); + needsRefresh = true; } } } @@ -153,69 +220,34 @@ } /** - * @see org.apache.sling.jcr.jcrinstall.osgi.OsgiResourceProcessor#processResourceQueue() + * Refreshes packages with subsequence bundle start or directly starts + * installed bundles. If since the last call to this method a bundle has + * been updated or removed, the packages will be refreshed. If no update + * or uninstallation has taken place, we still try to start all bundles + * which we expect to be started (mostly bundles which have recently been + * installed) but without refreshing the packages first. */ public void processResourceQueue() { - synchronized(pendingBundles) { - if (pendingBundles.isEmpty()) { - return; - } - final List toRemove = new LinkedList(); - final List idList = new LinkedList(); - - idList.addAll(pendingBundles.keySet()); - - boolean doRefresh = false; - - for(Long id : idList) { - final Bundle bundle = ctx.getBundle(id); - if(bundle == null) { - log.debug("Bundle id {} disappeared (bundle removed from framework?), removed from pending bundles queue", id); - toRemove.add(id); - continue; - } - final int state = bundle.getState(); - - switch ( state ) { - case Bundle.ACTIVE : - log.info("Bundle {} is active, removed from pending bundles queue", bundle.getLocation()); - toRemove.add(id); - break; - case Bundle.STARTING : - log.info("Bundle {} is starting.", bundle.getLocation()); - break; - case Bundle.STOPPING : - log.info("Bundle {} is stopping.", bundle.getLocation()); - break; - case Bundle.UNINSTALLED : - log.info("Bundle {} is uninstalled, removed from pending bundles queue", bundle.getLocation()); - toRemove.add(id); - doRefresh = true; - break; - case Bundle.INSTALLED : - log.debug("Bundle {} is installed but not resolved.", bundle.getLocation()); - if ( !packageAdmin.resolveBundles(new Bundle[] {bundle}) ) { - log.debug("Bundle {} is installed, failed to resolve.", bundle.getLocation()); - break; - } - // fall through to RESOLVED to start the bundle - case Bundle.RESOLVED : - log.info("Bundle {} is resolved, trying to start it.", bundle.getLocation()); - try { - bundle.start(); - } catch (BundleException e) { - log.error("Exception during bundle start of Bundle " + bundle.getLocation(), e); - } - doRefresh = true; - break; + if (needsRefresh) { + + // reset the flag + needsRefresh = false; + + // gather bundles currently active + for (Bundle bundle : ctx.getBundles()) { + if (bundle.getState() == Bundle.ACTIVE) { + synchronized (activeBundles) { + activeBundles.add(bundle.getBundleId()); + } } } - if ( doRefresh ) { - packageAdmin.refreshPackages(null); - } - - pendingBundles.keySet().removeAll(toRemove); + // refresh now + packageAdmin.refreshPackages(null); + + } else { + + startBundles(); } } @@ -232,7 +264,7 @@ * methods to reset the stream to where it started reading. The caller must * make sure, the input stream supports the marking as reported by * InputStream.markSupported. - * + * * @param data The mark supporting InputStream providing the * bundle whose symbolic name is to be matched against installed * bundles. @@ -271,7 +303,8 @@ if (symbolicName.equals(bundle.getSymbolicName())) { final BundleInfo info = new BundleInfo(); info.bundle = bundle; - info.newVersion = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION); + info.newVersion = manifest.getMainAttributes().getValue( + Constants.BUNDLE_VERSION); return info; } } @@ -298,6 +331,54 @@ protected static final class BundleInfo { public Bundle bundle; + public String newVersion; } + + /** + * Starts all bundles which have been stopped during package refresh and + * all bundles, which have been freshly installed. + */ + private void startBundles() { + startBundles(activeBundles); + startBundles(installedBundles); + } + + /** + * Starts all bundles whose bundle ID is contained in the + * bundleCollection. If a bundle fails to start, its bundle + * ID is added to the list of active bundles again for the bundle to + * bestarted next time the packages are refreshed or the resource queue is + * processed. + * + * @param bundleIdCollection The IDs of bundles to be started. + */ + private void startBundles(Collection bundleIdCollection) { + // get the bundle ids and remove them from the collection + Long[] bundleIds; + synchronized (bundleIdCollection) { + bundleIds = bundleIdCollection.toArray(new Long[bundleIdCollection.size()]); + bundleIdCollection.clear(); + } + + for (Long bundleId : bundleIds) { + Bundle bundle = ctx.getBundle(bundleId); + if (bundle.getState() != Bundle.ACTIVE) { + log.info("Starting Bundle {}/{} ", bundle.getSymbolicName(), + bundle.getBundleId()); + try { + bundle.start(); + } catch (BundleException be) { + log.error("Failed starting Bundle {}/{}", + bundle.getSymbolicName(), bundle.getBundleId()); + + // add the failed bundle to the activeBundles list + // to start them after the next refresh + synchronized (activeBundles) { + activeBundles.add(bundleId); + } + } + } + } + } } \ No newline at end of file Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/ConfigResourceProcessor.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/ConfigResourceProcessor.java?rev=708590&r1=708589&r2=708590&view=diff ============================================================================== --- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/ConfigResourceProcessor.java (original) +++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/ConfigResourceProcessor.java Tue Oct 28 07:56:04 2008 @@ -46,6 +46,10 @@ configurationAdmin = ca; } + public void dispose() { + // nothing to do + } + public boolean canProcess(String uri) { return uri.endsWith(CONFIG_EXTENSION); } Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java?rev=708590&r1=708589&r2=708590&view=diff ============================================================================== --- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java (original) +++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/osgi/impl/OsgiControllerImpl.java Tue Oct 28 07:56:04 2008 @@ -30,7 +30,12 @@ import org.apache.sling.jcr.jcrinstall.osgi.JcrInstallException; import org.apache.sling.jcr.jcrinstall.osgi.OsgiController; import org.apache.sling.jcr.jcrinstall.osgi.OsgiResourceProcessor; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleException; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; import org.osgi.framework.SynchronousBundleListener; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; @@ -79,7 +84,7 @@ processors.add(new ConfigResourceProcessor(configAdmin)); storage = new Storage(context.getBundleContext().getDataFile(STORAGE_FILENAME)); - + // start queue processing running = true; final Thread t = new Thread(this, getClass().getSimpleName() + "_" + System.currentTimeMillis()); @@ -97,10 +102,17 @@ log.warn("IOException in Storage.saveToFile()", ioe); } } + + if (processors != null) { + for (OsgiResourceProcessor processor : processors) { + processor.dispose(); + } + } + storage = null; processors = null; } - + public int installOrUpdate(String uri, long lastModified, InputStream data) throws IOException, JcrInstallException { int result = IGNORED; final OsgiResourceProcessor p = getProcessor(uri); @@ -198,7 +210,6 @@ for(OsgiResourceProcessor p : processors) { p.processResourceQueue(); } - } catch (Exception e) { log.warn("Exception in run()", e); } finally {