You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2014/03/04 20:31:43 UTC
git commit: [KARAF-2797] Add support of verbose and noRefresh on
feature uninstall
Repository: karaf
Updated Branches:
refs/heads/master 7e4fe0d9e -> 3c1b01d4a
[KARAF-2797] Add support of verbose and noRefresh on feature uninstall
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/3c1b01d4
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/3c1b01d4
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/3c1b01d4
Branch: refs/heads/master
Commit: 3c1b01d4aa6646a3f3dfeb8d7e4a76c105c89b1f
Parents: 7e4fe0d
Author: Jean-Baptiste Onofré <jb...@apache.org>
Authored: Tue Mar 4 20:31:00 2014 +0100
Committer: Jean-Baptiste Onofré <jb...@apache.org>
Committed: Tue Mar 4 20:31:00 2014 +0100
----------------------------------------------------------------------
.../command/UninstallFeatureCommand.java | 19 +-
.../apache/karaf/features/FeaturesService.java | 4 +
.../karaf/features/internal/BundleManager.java | 878 +++++++++----------
.../features/internal/FeaturesServiceImpl.java | 22 +-
.../management/FeaturesServiceMBean.java | 4 +
.../internal/FeaturesServiceMBeanImpl.java | 22 +-
.../karaf/features/FeaturesServiceTest.java | 13 +-
.../karaf/tooling/features/InstallKarsMojo.java | 8 +
8 files changed, 504 insertions(+), 466 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
----------------------------------------------------------------------
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
index 51c427b..e22d2f3 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
@@ -21,8 +21,10 @@ import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.shell.commands.Completer;
+import org.apache.karaf.shell.commands.Option;
import org.apache.karaf.shell.inject.Service;
+import java.util.EnumSet;
import java.util.List;
@Command(scope = "feature", name = "uninstall", description = "Uninstalls a feature with the specified name and version.")
@@ -33,8 +35,21 @@ public class UninstallFeatureCommand extends FeaturesCommandSupport {
@Completer(InstalledFeatureCompleter.class)
List<String> features;
+ @Option(name = "-r", aliases = "--no-auto-refresh", description = "Do not automatically refresh bundles", required = false, multiValued = false)
+ boolean noRefresh;
+
+ @Option(name = "-v", aliases = "--verbose", description = "Explain what is being done", required = false, multiValued = false)
+ boolean verbose;
+
protected void doExecute(FeaturesService admin) throws Exception {
// iterate in the provided feature
+ EnumSet<FeaturesService.Option> options = EnumSet.noneOf(FeaturesService.Option.class);
+ if (noRefresh) {
+ options.add(FeaturesService.Option.NoAutoRefreshBundles);
+ }
+ if (verbose) {
+ options.add(FeaturesService.Option.Verbose);
+ }
for (String feature : features) {
String[] split = feature.split("/");
String name = split[0];
@@ -43,9 +58,9 @@ public class UninstallFeatureCommand extends FeaturesCommandSupport {
version = split[1];
}
if (version != null && version.length() > 0) {
- admin.uninstallFeature(name, version);
+ admin.uninstallFeature(name, version, options);
} else {
- admin.uninstallFeature(name );
+ admin.uninstallFeature(name, options);
}
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
index e09ffdd..9e9b08f 100644
--- a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
+++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
@@ -67,7 +67,11 @@ public interface FeaturesService {
void installFeatures(Set<Feature> features, EnumSet<Option> options) throws Exception;
+ void uninstallFeature(String name, EnumSet<Option> options) throws Exception;
+
void uninstallFeature(String name) throws Exception;
+
+ void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception;
void uninstallFeature(String name, String version) throws Exception;
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java b/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
index a0eab0c..cd744f0 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
@@ -62,458 +62,440 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BundleManager {
- private static final Logger LOGGER = LoggerFactory
- .getLogger(BundleManager.class);
- private final BundleContext bundleContext;
- private final RegionsPersistence regionsPersistence;
- private final long refreshTimeout;
-
- public BundleManager(BundleContext bundleContext) {
- this(bundleContext, null);
- }
-
- public BundleManager(BundleContext bundleContext,
- RegionsPersistence regionsPersistence) {
- this(bundleContext, regionsPersistence, 5000);
- }
-
- public BundleManager(BundleContext bundleContext,
- RegionsPersistence regionsPersistence, long refreshTimeout) {
- this.bundleContext = bundleContext;
- this.regionsPersistence = regionsPersistence;
- this.refreshTimeout = refreshTimeout;
- }
-
- public BundleInstallerResult installBundleIfNeeded(String bundleLocation,
- int startLevel, String regionName) throws IOException,
- BundleException {
- BundleInstallerResult result = doInstallBundleIfNeeded(bundleLocation,
- startLevel);
- installToRegion(regionName, result.bundle, result.isNew);
- return result;
- }
-
- private BundleInstallerResult doInstallBundleIfNeeded(
- String bundleLocation, int startLevel) throws IOException,
- BundleException {
- InputStream is = getInputStreamForBundle(bundleLocation);
- try {
- is.mark(256 * 1024);
- @SuppressWarnings("resource")
- JarInputStream jar = new JarInputStream(is);
- Manifest m = jar.getManifest();
- if (m == null) {
- throw new BundleException(
- "Manifest not present in the first entry of the zip "
- + bundleLocation);
- }
- String sn = m.getMainAttributes().getValue(
- Constants.BUNDLE_SYMBOLICNAME);
- if (sn == null) {
- throw new BundleException(
- "Jar is not a bundle, no Bundle-SymbolicName "
- + bundleLocation);
- }
- // remove attributes from the symbolic name (like
- // ;blueprint.graceperiod:=false suffix)
- int attributeIndexSep = sn.indexOf(';');
- if (attributeIndexSep != -1) {
- sn = sn.substring(0, attributeIndexSep);
- }
- String vStr = m.getMainAttributes().getValue(
- Constants.BUNDLE_VERSION);
- Version v = vStr == null ? Version.emptyVersion : Version
- .parseVersion(vStr);
- Bundle existingBundle = findInstalled(sn, v);
- if (existingBundle != null) {
- LOGGER.debug("Found installed bundle: " + existingBundle);
- return new BundleInstallerResult(existingBundle, false);
- }
- try {
- is.reset();
- } catch (IOException e) {
- is.close();
- is = new URL(bundleLocation).openStream();
- // is = new BufferedInputStream(new
- // URL(bundleLocation).openStream());
- }
- is = new BufferedInputStream(new FilterInputStream(is) {
- @Override
- public int read(byte b[], int off, int len) throws IOException {
- if (Thread.currentThread().isInterrupted()) {
- throw new InterruptedIOException();
- }
- return super.read(b, off, len);
- }
- });
-
- LOGGER.debug("Installing bundle " + bundleLocation);
- Bundle b = bundleContext.installBundle(bundleLocation, is);
-
- if (startLevel > 0) {
- b.adapt(BundleStartLevel.class).setStartLevel(startLevel);
- }
-
- return new BundleInstallerResult(b, true);
- } finally {
- is.close();
- }
- }
-
- private Bundle findInstalled(String symbolicName, Version version) {
- String vStr;
- for (Bundle b : bundleContext.getBundles()) {
- if (b.getSymbolicName() != null
- && b.getSymbolicName().equals(symbolicName)) {
- vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
- Version bv = vStr == null ? Version.emptyVersion : Version
- .parseVersion(vStr);
- if (version.equals(bv)) {
- return b;
- }
- }
- }
- return null;
- }
-
- private InputStream getInputStreamForBundle(String bundleLocation)
- throws MalformedURLException, IOException {
- InputStream is;
- LOGGER.debug("Checking " + bundleLocation);
- try {
- int protocolIndex = bundleLocation.indexOf(":");
- if (protocolIndex != -1) {
- String protocol = bundleLocation.substring(0, protocolIndex);
- waitForUrlHandler(protocol);
- }
- URL bundleUrl = new URL(bundleLocation);
- is = new BufferedInputStream(bundleUrl.openStream());
- } catch (RuntimeException e) {
- LOGGER.error(e.getMessage());
- throw e;
- }
- return is;
- }
-
- private void installToRegion(String region, Bundle b, boolean isNew)
- throws BundleException {
- if (region != null && isNew) {
- if (regionsPersistence != null) {
- regionsPersistence.install(b, region);
- } else {
- throw new RuntimeException(
- "Unable to find RegionsPersistence service, while installing "
- + region);
- }
- }
- }
-
- /**
- * Will wait for the {@link URLStreamHandlerService} service for the
- * specified protocol to be registered.
- *
- * @param protocol
- */
- private void waitForUrlHandler(String protocol) {
- try {
- Filter filter = bundleContext.createFilter("(&("
- + Constants.OBJECTCLASS + "="
- + URLStreamHandlerService.class.getName()
- + ")(url.handler.protocol=" + protocol + "))");
- if (filter == null) {
- return;
- }
- ServiceTracker<URLStreamHandlerService, URLStreamHandlerService> urlHandlerTracker = new ServiceTracker<URLStreamHandlerService, URLStreamHandlerService>(
- bundleContext, filter, null);
- try {
- urlHandlerTracker.open();
- urlHandlerTracker.waitForService(30000);
- } catch (InterruptedException e) {
- LOGGER.debug(
- "Interrupted while waiting for URL handler for protocol {}.",
- protocol);
- } finally {
- urlHandlerTracker.close();
- }
- } catch (Exception ex) {
- LOGGER.error("Error creating service tracker.", ex);
- }
- }
-
- protected Set<Bundle> findBundlesToRefresh(Set<Bundle> existing,
- Set<Bundle> installed) {
- Set<Bundle> bundles = new HashSet<Bundle>();
- bundles.addAll(findBundlesWithOptionalPackagesToRefresh(existing,
- installed));
- bundles.addAll(findBundlesWithFragmentsToRefresh(existing, installed));
- return bundles;
- }
-
- protected Set<Bundle> findBundlesWithFragmentsToRefresh(
- Set<Bundle> existing, Set<Bundle> installed) {
- Set<Bundle> bundles = new HashSet<Bundle>();
- Set<Bundle> oldBundles = new HashSet<Bundle>(existing);
- oldBundles.removeAll(installed);
- if (!oldBundles.isEmpty()) {
- for (Bundle b : installed) {
- String hostHeader = (String) b.getHeaders().get(
- Constants.FRAGMENT_HOST);
- if (hostHeader != null) {
- Clause[] clauses = Parser.parseHeader(hostHeader);
- if (clauses != null && clauses.length > 0) {
- Clause path = clauses[0];
- for (Bundle hostBundle : oldBundles) {
- if (hostBundle.getSymbolicName().equals(
- path.getName())) {
- String ver = path
- .getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE);
- if (ver != null) {
- VersionRange v = VersionRange
- .parseVersionRange(ver);
- if (v.contains(hostBundle.getVersion())) {
- bundles.add(hostBundle);
- }
- } else {
- bundles.add(hostBundle);
- }
- }
- }
- }
- }
- }
- }
- return bundles;
- }
-
- protected Set<Bundle> findBundlesWithOptionalPackagesToRefresh(
- Set<Bundle> existing, Set<Bundle> installed) {
- // First pass: include all bundles contained in these features
- Set<Bundle> bundles = new HashSet<Bundle>(existing);
- bundles.removeAll(installed);
- if (bundles.isEmpty()) {
- return bundles;
- }
- // Second pass: for each bundle, check if there is any unresolved
- // optional package that could be resolved
- Map<Bundle, List<Clause>> imports = new HashMap<Bundle, List<Clause>>();
- for (Iterator<Bundle> it = bundles.iterator(); it.hasNext();) {
- Bundle b = it.next();
- String importsStr = (String) b.getHeaders().get(
- Constants.IMPORT_PACKAGE);
- List<Clause> importsList = getOptionalImports(importsStr);
- if (importsList.isEmpty()) {
- it.remove();
- } else {
- imports.put(b, importsList);
- }
- }
- if (bundles.isEmpty()) {
- return bundles;
- }
- // Third pass: compute a list of packages that are exported by our
- // bundles and see if
- // some exported packages can be wired to the optional imports
- List<Clause> exports = new ArrayList<Clause>();
- for (Bundle b : installed) {
- String exportsStr = (String) b.getHeaders().get(
- Constants.EXPORT_PACKAGE);
- if (exportsStr != null) {
- Clause[] exportsList = Parser.parseHeader(exportsStr);
- exports.addAll(Arrays.asList(exportsList));
- }
- }
- for (Iterator<Bundle> it = bundles.iterator(); it.hasNext();) {
- Bundle b = it.next();
- List<Clause> importsList = imports.get(b);
- for (Iterator<Clause> itpi = importsList.iterator(); itpi.hasNext();) {
- Clause pi = itpi.next();
- boolean matching = false;
- for (Clause pe : exports) {
- if (pi.getName().equals(pe.getName())) {
- String evStr = pe
- .getAttribute(Constants.VERSION_ATTRIBUTE);
- String ivStr = pi
- .getAttribute(Constants.VERSION_ATTRIBUTE);
- Version exported = evStr != null ? Version
- .parseVersion(evStr) : Version.emptyVersion;
- VersionRange imported = ivStr != null ? VersionRange
- .parseVersionRange(ivStr)
- : VersionRange.ANY_VERSION;
- if (imported.contains(exported)) {
- matching = true;
- break;
- }
- }
- }
- if (!matching) {
- itpi.remove();
- }
- }
- if (importsList.isEmpty()) {
- it.remove();
- } else {
- LOGGER.debug(
- "Refeshing bundle {} ({}) to solve the following optional imports",
- b.getSymbolicName(), b.getBundleId());
- for (Clause p : importsList) {
- LOGGER.debug(" {}", p);
- }
-
- }
- }
- return bundles;
- }
-
- /*
- * Get the list of optional imports from an OSGi Import-Package string
- */
- protected List<Clause> getOptionalImports(String importsStr) {
- Clause[] imports = Parser.parseHeader(importsStr);
- List<Clause> result = new LinkedList<Clause>();
- for (Clause anImport : imports) {
- String resolution = anImport
- .getDirective(Constants.RESOLUTION_DIRECTIVE);
- if (Constants.RESOLUTION_OPTIONAL.equals(resolution)) {
- result.add(anImport);
- }
- }
- return result;
- }
-
- protected void refreshPackages(Collection<Bundle> bundles) {
- final Object refreshLock = new Object();
- FrameworkWiring wiring = bundleContext.getBundle().adapt(
- FrameworkWiring.class);
- if (wiring != null) {
- synchronized (refreshLock) {
- wiring.refreshBundles(bundles, new FrameworkListener() {
- public void frameworkEvent(FrameworkEvent event) {
- if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
- synchronized (refreshLock) {
- refreshLock.notifyAll();
- }
- }
- }
- });
- try {
- refreshLock.wait(refreshTimeout);
- } catch (InterruptedException e) {
- LOGGER.warn(e.getMessage(), e);
- }
- }
- }
- }
-
- public void uninstall(Set<Bundle> bundles) {
- for (Bundle b : bundles) {
- try {
- b.uninstall();
- } catch (Exception e2) {
- // Ignore
- }
- }
- refreshPackages(null);
- }
-
- public void uninstall(List<Bundle> bundles) {
- for (Bundle b : bundles) {
- try {
- b.uninstall();
- } catch (Exception e2) {
- // Ignore
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BundleManager.class);
+ private final BundleContext bundleContext;
+ private final RegionsPersistence regionsPersistence;
+ private final long refreshTimeout;
+
+ public BundleManager(BundleContext bundleContext) {
+ this(bundleContext, null);
+ }
+
+ public BundleManager(BundleContext bundleContext, RegionsPersistence regionsPersistence) {
+ this(bundleContext, regionsPersistence, 5000);
+ }
+
+ public BundleManager(BundleContext bundleContext, RegionsPersistence regionsPersistence, long refreshTimeout) {
+ this.bundleContext = bundleContext;
+ this.regionsPersistence = regionsPersistence;
+ this.refreshTimeout = refreshTimeout;
+ }
+
+ public BundleInstallerResult installBundleIfNeeded(String bundleLocation, int startLevel, String regionName) throws IOException, BundleException {
+ BundleInstallerResult result = doInstallBundleIfNeeded(bundleLocation, startLevel);
+ installToRegion(regionName, result.bundle, result.isNew);
+ return result;
+ }
+
+ private BundleInstallerResult doInstallBundleIfNeeded(String bundleLocation, int startLevel) throws IOException, BundleException {
+ InputStream is = getInputStreamForBundle(bundleLocation);
+ try {
+ is.mark(256 * 1024);
+ @SuppressWarnings("resource")
+ JarInputStream jar = new JarInputStream(is);
+ Manifest m = jar.getManifest();
+ if (m == null) {
+ throw new BundleException("Manifest not present in the first entry of the zip " + bundleLocation);
+ }
+ String sn = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ if (sn == null) {
+ throw new BundleException("Jar is not a bundle, no Bundle-SymbolicName " + bundleLocation);
+ }
+ // remove attributes from the symbolic name (like
+ // ;blueprint.graceperiod:=false suffix)
+ int attributeIndexSep = sn.indexOf(';');
+ if (attributeIndexSep != -1) {
+ sn = sn.substring(0, attributeIndexSep);
+ }
+ String vStr = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ Version v = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ Bundle existingBundle = findInstalled(sn, v);
+ if (existingBundle != null) {
+ LOGGER.debug("Found installed bundle: " + existingBundle);
+ return new BundleInstallerResult(existingBundle, false);
+ }
+ try {
+ is.reset();
+ } catch (IOException e) {
+ is.close();
+ is = new URL(bundleLocation).openStream();
+ // is = new BufferedInputStream(new
+ // URL(bundleLocation).openStream());
+ }
+ is = new BufferedInputStream(new FilterInputStream(is) {
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedIOException();
+ }
+ return super.read(b, off, len);
+ }
+ });
+
+ LOGGER.debug("Installing bundle " + bundleLocation);
+ Bundle b = bundleContext.installBundle(bundleLocation, is);
+
+ if (startLevel > 0) {
+ b.adapt(BundleStartLevel.class).setStartLevel(startLevel);
+ }
+
+ return new BundleInstallerResult(b, true);
+ } finally {
+ is.close();
+ }
+ }
+
+ private Bundle findInstalled(String symbolicName, Version version) {
+ String vStr;
+ for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName() != null && b.getSymbolicName().equals(symbolicName)) {
+ vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
+ Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ if (version.equals(bv)) {
+ return b;
+ }
+ }
+ }
+ return null;
+ }
+
+ private InputStream getInputStreamForBundle(String bundleLocation) throws MalformedURLException, IOException {
+ InputStream is;
+ LOGGER.debug("Checking " + bundleLocation);
+ try {
+ int protocolIndex = bundleLocation.indexOf(":");
+ if (protocolIndex != -1) {
+ String protocol = bundleLocation.substring(0, protocolIndex);
+ waitForUrlHandler(protocol);
+ }
+ URL bundleUrl = new URL(bundleLocation);
+ is = new BufferedInputStream(bundleUrl.openStream());
+ } catch (RuntimeException e) {
+ LOGGER.error(e.getMessage());
+ throw e;
+ }
+ return is;
+ }
+
+ private void installToRegion(String region, Bundle b, boolean isNew) throws BundleException {
+ if (region != null && isNew) {
+ if (regionsPersistence != null) {
+ regionsPersistence.install(b, region);
+ } else {
+ throw new RuntimeException("Unable to find RegionsPersistence service, while installing " + region);
+ }
+ }
+ }
+
+ /**
+ * Will wait for the {@link URLStreamHandlerService} service for the
+ * specified protocol to be registered.
+ *
+ * @param protocol
+ */
+ private void waitForUrlHandler(String protocol) {
+ try {
+ Filter filter = bundleContext.createFilter("(&(" + Constants.OBJECTCLASS + "=" + URLStreamHandlerService.class.getName() + ")(url.handler.protocol=" + protocol + "))");
+ if (filter == null) {
+ return;
+ }
+ ServiceTracker<URLStreamHandlerService, URLStreamHandlerService> urlHandlerTracker = new ServiceTracker<URLStreamHandlerService, URLStreamHandlerService>(bundleContext, filter, null);
+ try {
+ urlHandlerTracker.open();
+ urlHandlerTracker.waitForService(30000);
+ } catch (InterruptedException e) {
+ LOGGER.debug("Interrupted while waiting for URL handler for protocol {}.", protocol);
+ } finally {
+ urlHandlerTracker.close();
+ }
+ } catch (Exception ex) {
+ LOGGER.error("Error creating service tracker.", ex);
+ }
+ }
+
+ protected Set<Bundle> findBundlesToRefresh(Set<Bundle> existing, Set<Bundle> installed) {
+ Set<Bundle> bundles = new HashSet<Bundle>();
+ bundles.addAll(findBundlesWithOptionalPackagesToRefresh(existing, installed));
+ bundles.addAll(findBundlesWithFragmentsToRefresh(existing, installed));
+ return bundles;
+ }
+
+ protected Set<Bundle> findBundlesWithFragmentsToRefresh(Set<Bundle> existing, Set<Bundle> installed) {
+ Set<Bundle> bundles = new HashSet<Bundle>();
+ Set<Bundle> oldBundles = new HashSet<Bundle>(existing);
+ oldBundles.removeAll(installed);
+ if (!oldBundles.isEmpty()) {
+ for (Bundle b : installed) {
+ String hostHeader = (String) b.getHeaders().get(Constants.FRAGMENT_HOST);
+ if (hostHeader != null) {
+ Clause[] clauses = Parser.parseHeader(hostHeader);
+ if (clauses != null && clauses.length > 0) {
+ Clause path = clauses[0];
+ for (Bundle hostBundle : oldBundles) {
+ if (hostBundle.getSymbolicName().equals(path.getName())) {
+ String ver = path.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (ver != null) {
+ VersionRange v = VersionRange.parseVersionRange(ver);
+ if (v.contains(hostBundle.getVersion())) {
+ bundles.add(hostBundle);
+ }
+ } else {
+ bundles.add(hostBundle);
+ }
+ }
}
+ }
}
- refreshPackages(null);
+ }
+ }
+ return bundles;
+ }
+
+ protected Set<Bundle> findBundlesWithOptionalPackagesToRefresh(Set<Bundle> existing, Set<Bundle> installed) {
+ // First pass: include all bundles contained in these features
+ Set<Bundle> bundles = new HashSet<Bundle>(existing);
+ bundles.removeAll(installed);
+ if (bundles.isEmpty()) {
+ return bundles;
+ }
+ // Second pass: for each bundle, check if there is any unresolved
+ // optional package that could be resolved
+ Map<Bundle, List<Clause>> imports = new HashMap<Bundle, List<Clause>>();
+ for (Iterator<Bundle> it = bundles.iterator(); it.hasNext(); ) {
+ Bundle b = it.next();
+ String importsStr = (String) b.getHeaders().get(
+ Constants.IMPORT_PACKAGE);
+ List<Clause> importsList = getOptionalImports(importsStr);
+ if (importsList.isEmpty()) {
+ it.remove();
+ } else {
+ imports.put(b, importsList);
+ }
+ }
+ if (bundles.isEmpty()) {
+ return bundles;
+ }
+ // Third pass: compute a list of packages that are exported by our
+ // bundles and see if
+ // some exported packages can be wired to the optional imports
+ List<Clause> exports = new ArrayList<Clause>();
+ for (Bundle b : installed) {
+ String exportsStr = (String) b.getHeaders().get(
+ Constants.EXPORT_PACKAGE);
+ if (exportsStr != null) {
+ Clause[] exportsList = Parser.parseHeader(exportsStr);
+ exports.addAll(Arrays.asList(exportsList));
+ }
}
+ for (Iterator<Bundle> it = bundles.iterator(); it.hasNext(); ) {
+ Bundle b = it.next();
+ List<Clause> importsList = imports.get(b);
+ for (Iterator<Clause> itpi = importsList.iterator(); itpi.hasNext(); ) {
+ Clause pi = itpi.next();
+ boolean matching = false;
+ for (Clause pe : exports) {
+ if (pi.getName().equals(pe.getName())) {
+ String evStr = pe
+ .getAttribute(Constants.VERSION_ATTRIBUTE);
+ String ivStr = pi
+ .getAttribute(Constants.VERSION_ATTRIBUTE);
+ Version exported = evStr != null ? Version
+ .parseVersion(evStr) : Version.emptyVersion;
+ VersionRange imported = ivStr != null ? VersionRange
+ .parseVersionRange(ivStr)
+ : VersionRange.ANY_VERSION;
+ if (imported.contains(exported)) {
+ matching = true;
+ break;
+ }
+ }
+ }
+ if (!matching) {
+ itpi.remove();
+ }
+ }
+ if (importsList.isEmpty()) {
+ it.remove();
+ } else {
+ LOGGER.debug(
+ "Refeshing bundle {} ({}) to solve the following optional imports",
+ b.getSymbolicName(), b.getBundleId());
+ for (Clause p : importsList) {
+ LOGGER.debug(" {}", p);
+ }
+ }
+ }
+ return bundles;
+ }
+
+ /*
+ * Get the list of optional imports from an OSGi Import-Package string
+ */
+ protected List<Clause> getOptionalImports(String importsStr) {
+ Clause[] imports = Parser.parseHeader(importsStr);
+ List<Clause> result = new LinkedList<Clause>();
+ for (Clause anImport : imports) {
+ String resolution = anImport
+ .getDirective(Constants.RESOLUTION_DIRECTIVE);
+ if (Constants.RESOLUTION_OPTIONAL.equals(resolution)) {
+ result.add(anImport);
+ }
+ }
+ return result;
+ }
+
+ protected void refreshPackages(Collection<Bundle> bundles) {
+ final Object refreshLock = new Object();
+ FrameworkWiring wiring = bundleContext.getBundle().adapt(FrameworkWiring.class);
+ if (wiring != null) {
+ synchronized (refreshLock) {
+ wiring.refreshBundles(bundles, new FrameworkListener() {
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
+ synchronized (refreshLock) {
+ refreshLock.notifyAll();
+ }
+ }
+ }
+ });
+ try {
+ refreshLock.wait(refreshTimeout);
+ } catch (InterruptedException e) {
+ LOGGER.warn(e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ public void uninstall(Set<Bundle> bundles) {
+ uninstall(bundles, true);
+ }
+
+ public void uninstall(Set<Bundle> bundles, boolean refresh) {
+ for (Bundle b : bundles) {
+ try {
+ b.uninstall();
+ } catch (Exception e2) {
+ // Ignore
+ }
+ }
+ if (refresh) {
+ refreshPackages(null);
+ }
+ }
+
+ public void uninstall(List<Bundle> bundles) {
+ uninstall(bundles, true);
+ }
+
+ public void uninstall(List<Bundle> bundles, boolean refresh) {
+ for (Bundle b : bundles) {
+ try {
+ b.uninstall();
+ } catch (Exception e2) {
+ // Ignore
+ }
+ }
+ if (refresh) {
+ refreshPackages(null);
+ }
+ }
+
+ public void uninstallById(Set<Long> bundles) throws BundleException, InterruptedException {
+ uninstallById(bundles, true);
+ }
+
+ public void uninstallById(Set<Long> bundles, boolean refresh) throws BundleException,
+ InterruptedException {
+ for (long bundleId : bundles) {
+ Bundle b = bundleContext.getBundle(bundleId);
+ if (b != null) {
+ b.uninstall();
+ }
+ }
+ if (refresh) {
+ refreshPackages(null);
+ }
+ }
+
+ public File getDataFile(String fileName) {
+ return bundleContext.getDataFile(fileName);
+ }
+
+ EventAdminListener createAndRegisterEventAdminListener() {
+ EventAdminListener listener = null;
+ try {
+ getClass().getClassLoader().loadClass(
+ "org.bundles.service.event.EventAdmin");
+ listener = new EventAdminListener(bundleContext);
+ } catch (Throwable t) {
+ // Ignore, if the EventAdmin package is not available, just don't
+ // use it
+ LOGGER.debug("EventAdmin package is not available, just don't use it");
+ }
+ return listener;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public ServiceTracker createServiceTrackerForResolverName(String resolver)
+ throws InvalidSyntaxException {
+ String filter = "(&(" + Constants.OBJECTCLASS + "="
+ + Resolver.class.getName() + ")(name=" + resolver + "))";
+ return new ServiceTracker(bundleContext,
+ FrameworkUtil.createFilter(filter), null);
+ }
+
+ public void refreshBundles(Set<Bundle> existing, Set<Bundle> installed,
+ EnumSet<Option> options) {
+ boolean print = options.contains(Option.PrintBundlesToRefresh);
+ boolean refresh = !options.contains(Option.NoAutoRefreshBundles);
+ if (print || refresh) {
+ Set<Bundle> bundlesToRefresh = findBundlesToRefresh(existing,
+ installed);
+ StringBuilder sb = new StringBuilder();
+ for (Bundle b : bundlesToRefresh) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(b.getSymbolicName()).append(" (")
+ .append(b.getBundleId()).append(")");
+ }
+ LOGGER.debug("Bundles to refresh: {}", sb.toString());
+ if (!bundlesToRefresh.isEmpty()) {
+ if (print) {
+ if (refresh) {
+ System.out.println("Refreshing bundles "
+ + sb.toString());
+ } else {
+ System.out
+ .println("The following bundles may need to be refreshed: "
+ + sb.toString());
+ }
+ }
+ if (refresh) {
+ LOGGER.debug("Refreshing bundles: {}", sb.toString());
+ refreshPackages(bundlesToRefresh);
+ }
+ }
+ }
+ }
+
+ public BundleContext getBundleContext() {
+ return this.bundleContext;
+ }
+
+ public static class BundleInstallerResult {
+ Bundle bundle;
+ boolean isNew;
+
+ public BundleInstallerResult(Bundle bundle, boolean isNew) {
+ super();
+ this.bundle = bundle;
+ this.isNew = isNew;
+ }
- public void uninstallById(Set<Long> bundles) throws BundleException,
- InterruptedException {
- for (long bundleId : bundles) {
- Bundle b = bundleContext.getBundle(bundleId);
- if (b != null) {
- b.uninstall();
- }
- }
- refreshPackages(null);
- }
-
- public File getDataFile(String fileName) {
- return bundleContext.getDataFile(fileName);
- }
-
- EventAdminListener createAndRegisterEventAdminListener() {
- EventAdminListener listener = null;
- try {
- getClass().getClassLoader().loadClass(
- "org.bundles.service.event.EventAdmin");
- listener = new EventAdminListener(bundleContext);
- } catch (Throwable t) {
- // Ignore, if the EventAdmin package is not available, just don't
- // use it
- LOGGER.debug("EventAdmin package is not available, just don't use it");
- }
- return listener;
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- public ServiceTracker createServiceTrackerForResolverName(String resolver)
- throws InvalidSyntaxException {
- String filter = "(&(" + Constants.OBJECTCLASS + "="
- + Resolver.class.getName() + ")(name=" + resolver + "))";
- return new ServiceTracker(bundleContext,
- FrameworkUtil.createFilter(filter), null);
- }
-
- public void refreshBundles(Set<Bundle> existing, Set<Bundle> installed,
- EnumSet<Option> options) {
- boolean print = options.contains(Option.PrintBundlesToRefresh);
- boolean refresh = !options.contains(Option.NoAutoRefreshBundles);
- if (print || refresh) {
- Set<Bundle> bundlesToRefresh = findBundlesToRefresh(existing,
- installed);
- StringBuilder sb = new StringBuilder();
- for (Bundle b : bundlesToRefresh) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(b.getSymbolicName()).append(" (")
- .append(b.getBundleId()).append(")");
- }
- LOGGER.debug("Bundles to refresh: {}", sb.toString());
- if (!bundlesToRefresh.isEmpty()) {
- if (print) {
- if (refresh) {
- System.out.println("Refreshing bundles "
- + sb.toString());
- } else {
- System.out
- .println("The following bundles may need to be refreshed: "
- + sb.toString());
- }
- }
- if (refresh) {
- LOGGER.debug("Refreshing bundles: {}", sb.toString());
- refreshPackages(bundlesToRefresh);
- }
- }
- }
- }
-
- public BundleContext getBundleContext() {
- return this.bundleContext;
- }
-
- public static class BundleInstallerResult {
- Bundle bundle;
- boolean isNew;
-
- public BundleInstallerResult(Bundle bundle, boolean isNew) {
- super();
- this.bundle = bundle;
- this.isNew = isNew;
- }
-
- }
+ }
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
index 58b4ad0..be37db6 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
@@ -637,6 +637,10 @@ public class FeaturesServiceImpl implements FeaturesService {
}
public void uninstallFeature(String name) throws Exception {
+ uninstallFeature(name, EnumSet.noneOf(Option.class));
+ }
+
+ public void uninstallFeature(String name, EnumSet<Option> options) throws Exception {
List<String> versions = new ArrayList<String>();
for (Feature f : installed.keySet()) {
if (name.equals(f.getName())) {
@@ -657,14 +661,22 @@ public class FeaturesServiceImpl implements FeaturesService {
sb.append("). Please specify the version to uninstall.");
throw new Exception(sb.toString());
}
- uninstallFeature(name, versions.get(0));
+ uninstallFeature(name, versions.get(0), options);
}
-
+
public void uninstallFeature(String name, String version) throws Exception {
+ uninstallFeature(name, version, EnumSet.noneOf(Option.class));
+ }
+
+ public void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception {
Feature feature = getFeature(name, version);
if (feature == null || !installed.containsKey(feature)) {
- throw new Exception("Feature named '" + name
- + "' with version '" + version + "' is not installed");
+ throw new Exception("Feature named '" + name + "' with version '" + version + "' is not installed");
+ }
+ boolean verbose = options != null && options.contains(Option.Verbose);
+ boolean refresh = options == null || !options.contains(Option.NoAutoRefreshBundles);
+ if (verbose) {
+ System.out.println("Uninstalling feature " + feature.getName() + " " + feature.getVersion());
}
// Grab all the bundles installed by this feature
// and remove all those who will still be in use.
@@ -702,7 +714,7 @@ public class FeaturesServiceImpl implements FeaturesService {
}
});
}
- bundleManager.uninstall(bundlesDescendSortedByStartLvl);
+ bundleManager.uninstall(bundlesDescendSortedByStartLvl, refresh);
callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureUninstalled, false));
saveState();
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java b/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
index 4dc65a8..da4a36e 100644
--- a/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
+++ b/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
@@ -46,8 +46,12 @@ public interface FeaturesServiceMBean {
void uninstallFeature(String name) throws Exception;
+ void uninstallFeature(String name, boolean noRefresh) throws Exception;
+
void uninstallFeature(String name, String version) throws Exception;
+ void uninstallFeature(String name, String version, boolean noRefresh) throws Exception;
+
String FEATURE_NAME = "Name";
String FEATURE_VERSION = "Version";
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/core/src/main/java/org/apache/karaf/features/management/internal/FeaturesServiceMBeanImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/internal/FeaturesServiceMBeanImpl.java b/features/core/src/main/java/org/apache/karaf/features/management/internal/FeaturesServiceMBeanImpl.java
index c4fe307..700e835 100644
--- a/features/core/src/main/java/org/apache/karaf/features/management/internal/FeaturesServiceMBeanImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/management/internal/FeaturesServiceMBeanImpl.java
@@ -28,11 +28,7 @@ import javax.management.Notification;
import javax.management.ObjectName;
import javax.management.openmbean.TabularData;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeatureEvent;
-import org.apache.karaf.features.FeaturesListener;
-import org.apache.karaf.features.Repository;
-import org.apache.karaf.features.RepositoryEvent;
+import org.apache.karaf.features.*;
import org.apache.karaf.features.management.FeaturesServiceMBean;
import org.apache.karaf.features.management.codec.JmxFeature;
import org.apache.karaf.features.management.codec.JmxFeatureEvent;
@@ -206,10 +202,26 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
featuresService.uninstallFeature(name);
}
+ public void uninstallFeature(String name, boolean noRefresh) throws Exception {
+ EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
+ if (noRefresh) {
+ options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
+ }
+ featuresService.uninstallFeature(name, options);
+ }
+
public void uninstallFeature(String name, String version) throws Exception {
featuresService.uninstallFeature(name, version);
}
+ public void uninstallFeature(String name, String version, boolean noRefresh) throws Exception {
+ EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
+ if (noRefresh) {
+ options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
+ }
+ featuresService.uninstallFeature(name, version, options);
+ }
+
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java b/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
index a44e729..bce3735 100644
--- a/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
@@ -51,6 +51,7 @@ import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
+import org.osgi.service.packageadmin.PackageAdmin;
public class FeaturesServiceTest extends TestBase {
private static final String FEATURE_WITH_INVALID_BUNDLE = "<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
@@ -143,7 +144,7 @@ public class FeaturesServiceTest extends TestBase {
expect(bundleManager.installBundleIfNeeded("bundle-0.1", 0, null)).andReturn(new BundleInstallerResult(bundlef101, false));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
- bundleManager.uninstall(Collections.EMPTY_LIST);
+ bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().times(2);
@@ -215,7 +216,7 @@ public class FeaturesServiceTest extends TestBase {
expect(bundleManager.getBundleContext()).andReturn(bundleContext).anyTimes();
expect(bundleContext.getBundle(12345)).andReturn(bundlef101).anyTimes();
ignoreRefreshes(bundleManager);
- bundleManager.uninstall(Collections.EMPTY_LIST);
+ bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().anyTimes();
replay(bundleManager);
@@ -237,7 +238,7 @@ public class FeaturesServiceTest extends TestBase {
expect(bundleManager.installBundleIfNeeded(bundleUri, 0, null)).andReturn(new BundleInstallerResult(installedBundle, true));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
- bundleManager.uninstall(Collections.EMPTY_LIST);
+ bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().times(2);
return bundleManager;
}
@@ -296,9 +297,9 @@ public class FeaturesServiceTest extends TestBase {
expect(bundleManager.installBundleIfNeeded("bundle-0.2", 0, null)).andReturn(new BundleInstallerResult(bundleVer02, true));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
- bundleManager.uninstall(Collections.EMPTY_LIST);
+ bundleManager.uninstall(Collections.EMPTY_LIST, true);
+
EasyMock.expectLastCall().times(2);
-
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
@@ -329,7 +330,7 @@ public class FeaturesServiceTest extends TestBase {
expect(bundleManager.installBundleIfNeeded(bundleVer01Uri, 0, null)).andReturn(new BundleInstallerResult(bundleVer01, false));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
- bundleManager.uninstall(Collections.EMPTY_LIST);
+ bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().times(2);
http://git-wip-us.apache.org/repos/asf/karaf/blob/3c1b01d4/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
index a2ee978..94c153a 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/InstallKarsMojo.java
@@ -587,10 +587,18 @@ public class InstallKarsMojo extends MojoSupport {
}
@Override
+ public void uninstallFeature(String name, EnumSet<Option> options) {
+ }
+
+ @Override
public void uninstallFeature(String name, String version) throws Exception {
}
@Override
+ public void uninstallFeature(String name, String version, EnumSet<Option> options) {
+ }
+
+ @Override
public Feature[] listFeatures() throws Exception {
return new Feature[0];
}