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<Long, Bundle> pendingBundles;
+
+ /**
+ * All bundles which were active before [EMAIL PROTECTED]
#processResourceQueue()}
+ * refreshes the packages. Bundles which may not be started after
refreshing
+ * the packages remain in this set. In addition bundles from the
+ * [EMAIL PROTECTED] #installedBundles} bundles list will be added here to
try to start
+ * them in the next round.
+ */
+ private final Set<Long> 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 [EMAIL PROTECTED] #activeBundles} set for
them to be
+ * started in the next round.
+ */
+ private final List<Long> installedBundles;
+
+ /**
+ * Flag set by [EMAIL PROTECTED] #installOrUpdate(String, Map,
InputStream)} of a
+ * bundle has been updated and by [EMAIL PROTECTED] #uninstall(String,
Map)} if a
+ * bundle has been removed. This causes the [EMAIL PROTECTED]
#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<Long, Bundle>();
+ this.activeBundles = new HashSet<Long>();
+ this.installedBundles = new ArrayList<Long>();
+
+ // 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<String, Object> attributes,
InputStream data)
- throws BundleException, IOException {
+ public int installOrUpdate(String uri, Map<String, Object> 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<String, Object> attributes) throws
BundleException {
- final Long longId = (Long)attributes.get(KEY_BUNDLE_ID);
+ public void uninstall(String uri, Map<String, Object> 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<Long> toRemove = new LinkedList<Long>();
- final List<Long> idList = new LinkedList<Long>();
-
- 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
* <code>InputStream.markSupported</code>.
- *
+ *
* @param data The mark supporting <code>InputStream</code> 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
+ * <code>bundleCollection</code>. 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<Long> 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 {