You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/04/07 10:53:03 UTC
[12/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/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
deleted file mode 100644
index 909a963..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
+++ /dev/null
@@ -1,1192 +0,0 @@
-/*
- * 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.karaf.features.internal;
-
-import org.apache.felix.utils.version.VersionRange;
-import org.apache.felix.utils.version.VersionTable;
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.Conditional;
-import org.apache.karaf.features.Dependency;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeatureEvent;
-import org.apache.karaf.features.FeaturesListener;
-import org.apache.karaf.features.FeaturesService;
-import org.apache.karaf.features.Repository;
-import org.apache.karaf.features.RepositoryEvent;
-import org.apache.karaf.features.Resolver;
-import org.apache.karaf.features.internal.BundleManager.BundleInstallerResult;
-import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.osgi.framework.startlevel.BundleStartLevel;
-import org.osgi.util.tracker.ServiceTracker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Dictionary;
-import java.util.EnumSet;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import static java.lang.String.format;
-
-/**
- * The Features service implementation.
- * Adding a repository url will load the features contained in this repository and
- * create dummy sub shells. When invoked, these commands will prompt the user for
- * installing the needed bundles.
- */
-public class FeaturesServiceImpl implements FeaturesService {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
-
- private static final int KARAF_BUNDLE_START_LEVEL =
- Integer.parseInt(System.getProperty("karaf.startlevel.bundle", "80"));
-
- private final BundleManager bundleManager;
- private final FeatureConfigInstaller configManager;
- private final AtomicBoolean stopped = new AtomicBoolean();
-
- private boolean respectStartLvlDuringFeatureStartup;
- private boolean respectStartLvlDuringFeatureUninstall;
- private long resolverTimeout = 5000;
- private Set<URI> uris;
- private Map<URI, Repository> repositories = new HashMap<URI, Repository>();
- private Map<String, Map<String, Feature>> features;
- private Map<Feature, Set<Long>> installed = new HashMap<Feature, Set<Long>>();
- private List<FeaturesListener> listeners = new CopyOnWriteArrayIdentityList<FeaturesListener>();
- private ThreadLocal<Repository> repo = new ThreadLocal<Repository>();
- private EventAdminListener eventAdminListener;
- private String overrides;
- private FeatureFinder featureFinder;
-
- public FeaturesServiceImpl(BundleManager bundleManager) {
- this(bundleManager, null);
- }
-
- public FeaturesServiceImpl(BundleManager bundleManager, FeatureConfigInstaller configManager) {
- this.bundleManager = bundleManager;
- this.configManager = configManager;
- }
-
- public long getResolverTimeout() {
- return resolverTimeout;
- }
-
- public void setResolverTimeout(long resolverTimeout) {
- this.resolverTimeout = resolverTimeout;
- }
-
- public void setRespectStartLvlDuringFeatureStartup(boolean respectStartLvlDuringFeatureStartup) {
- this.respectStartLvlDuringFeatureStartup = respectStartLvlDuringFeatureStartup;
- }
-
- public String getOverrides() {
- return overrides;
- }
-
- public void setOverrides(String overrides) {
- this.overrides = overrides;
- }
-
- public FeatureFinder getFeatureFinder() {
- return featureFinder;
- }
-
- public void setFeatureFinder(FeatureFinder featureFinder) {
- this.featureFinder = featureFinder;
- }
-
- public void registerListener(FeaturesListener listener) {
- listeners.add(listener);
- try {
- for (Repository repository : listRepositories()) {
- listener.repositoryEvent(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, true));
- }
- for (Feature feature : listInstalledFeatures()) {
- listener.featureEvent(new FeatureEvent(feature, FeatureEvent.EventType.FeatureInstalled, true));
- }
- } catch (Exception e) {
- LOGGER.error("Error notifying listener about the current state", e);
- }
- }
-
- public void unregisterListener(FeaturesListener listener) {
- listeners.remove(listener);
- }
-
- public void setUrls(String uris) {
- String[] s = uris.split(",");
- this.uris = new HashSet<URI>();
- for (String value : s) {
- value = value.trim();
- if (!value.isEmpty()) {
- try {
- this.uris.add(new URI(value));
- } catch (URISyntaxException e) {
- LOGGER.warn("Invalid features repository URI: " + value);
- }
- }
- }
- }
-
- /**
- * Validate a features repository XML.
- *
- * @param uri the features repository URI.
- */
- public void validateRepository(URI uri) throws Exception {
-
- FeatureValidationUtil.validate(uri);
- }
-
- /**
- * Add a features repository.
- *
- * @param uri the features repository URI.
- * @throws Exception in case of adding failure.
- */
- public void addRepository(URI uri) throws Exception {
- this.addRepository(uri, false);
- }
-
- /**
- * Add a features repository.
- *
- * @param uri the features repository URI.
- * @param install if true, install all features contained in the features repository.
- * @throws Exception in case of adding failure.
- */
- public void addRepository(URI uri, boolean install) throws Exception {
- if (!repositories.containsKey(uri)) {
- Repository repositoryImpl = this.internalAddRepository(uri);
- saveState();
- if (install) {
- for (Feature feature : repositoryImpl.getFeatures()) {
- installFeature(feature, EnumSet.noneOf(Option.class));
- }
- }
- } else {
- refreshRepository(uri, install);
- }
- }
-
- /**
- * Refresh a features repository.
- *
- * @param uri the features repository URI.
- * @throws Exception in case of refresh failure.
- */
- @Override
- public void refreshRepository(URI uri) throws Exception {
- this.refreshRepository(uri, false);
- }
-
- /**
- * Refresh a features repository.
- *
- * @param uri the features repository URI.
- * @param install if true, install all features in the features repository.
- * @throws Exception in case of refresh failure.
- */
- protected void refreshRepository(URI uri, boolean install) throws Exception {
- try {
- removeRepository(uri, install);
- addRepository(uri, install);
- } catch (Exception e) {
- //get chance to restore previous, fix for KARAF-4
- restoreRepository(uri);
- throw new Exception("Unable to refresh features repository " + uri, e);
- }
- }
-
- /**
- * Add a features repository into the internal container.
- *
- * @param uri the features repository URI.
- * @return the internal <code>RepositoryImpl</code> representation.
- * @throws Exception in case of adding failure.
- */
- protected Repository internalAddRepository(URI uri) throws Exception {
- validateRepository(uri);
- RepositoryImpl repo = new RepositoryImpl(uri);
- repositories.put(uri, repo);
- repo.load();
- callListeners(new RepositoryEvent(repo, RepositoryEvent.EventType.RepositoryAdded, false));
- features = null;
- return repo;
-
- }
-
- /**
- * Remove a features repository.
- *
- * @param uri the features repository URI.
- * @throws Exception in case of remove failure.
- */
- public void removeRepository(URI uri) throws Exception {
- this.removeRepository(uri, false);
- }
-
- /**
- * Remove a features repository.
- *
- * @param uri the features repository URI.
- * @param uninstall if true, uninstall all features from the features repository.
- * @throws Exception in case of remove failure.
- */
- public void removeRepository(URI uri, boolean uninstall) throws Exception {
- if (repositories.containsKey(uri)) {
- if (uninstall) {
- Repository repositoryImpl = repositories.get(uri);
- for (Feature feature : repositoryImpl.getFeatures()) {
- this.uninstallFeature(feature.getName(), feature.getVersion());
- }
- }
- internalRemoveRepository(uri);
- saveState();
- }
- }
-
- /**
- * Remove a features repository from the internal container.
- *
- * @param uri the features repository URI.
- */
- protected void internalRemoveRepository(URI uri) {
- Repository repo = repositories.remove(uri);
- this.repo.set(repo);
- callListeners(new RepositoryEvent(repo, RepositoryEvent.EventType.RepositoryRemoved, false));
- features = null;
- }
-
- /**
- * Restore a features repository.
- *
- * @param uri the features repository URI.
- * @throws Exception in case of restore failure.
- */
- public void restoreRepository(URI uri) throws Exception {
- repositories.put(uri, repo.get());
- callListeners(new RepositoryEvent(repo.get(), RepositoryEvent.EventType.RepositoryAdded, false));
- features = null;
- }
-
- /**
- * Get the list of features repository.
- *
- * @return the list of features repository.
- */
- public Repository[] listRepositories() {
- Collection<Repository> repos = repositories.values();
- return repos.toArray(new Repository[repos.size()]);
- }
-
- @Override
- public Repository getRepository(String repoName) {
- for (Repository repo : this.repositories.values()) {
- if (repoName.equals(repo.getName())) {
- return repo;
- }
- }
- return null;
- }
-
- /**
- * Install a feature identified by a name.
- *
- * @param name the name of the feature.
- * @throws Exception in case of install failure.
- */
- public void installFeature(String name) throws Exception {
- installFeature(name, org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION);
- }
-
- /**
- * Install a feature identified by a name, including a set of options.
- *
- * @param name the name of the feature.
- * @param options the installation options.
- * @throws Exception in case of install failure.
- */
- public void installFeature(String name, EnumSet<Option> options) throws Exception {
- installFeature(name, org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, options);
- }
-
- /**
- * Install a feature identified by a name and a version.
- *
- * @param name the name of the feature.
- * @param version the version of the feature.
- * @throws Exception in case of install failure.
- */
- public void installFeature(String name, String version) throws Exception {
- installFeature(name, version, EnumSet.noneOf(Option.class));
- }
-
- /**
- * Install a feature identified by a name and a version, including a set of options.
- *
- * @param name the name of the feature.
- * @param version the version of the feature.
- * @param options the installation options.
- * @throws Exception in case of install failure.
- */
- public void installFeature(String name, String version, EnumSet<Option> options) throws Exception {
- Feature f = getFeature(name, version);
- if (f == null) {
- throw new Exception("No feature named '" + name
- + "' with version '" + version + "' available");
- }
- installFeature(f, options);
- }
-
- /**
- * Install a feature including a set of options.
- *
- * @param feature the <code>Feature</code> to install.
- * @param options the installation options set.
- * @throws Exception in case of install failure.
- */
- public void installFeature(Feature feature, EnumSet<Option> options) throws Exception {
- installFeatures(Collections.singleton(feature), options);
- }
-
- /**
- * Install a set of features, including a set of options.
- *
- * @param features a set of <code>Feature</code>.
- * @param options the installation options set.
- * @throws Exception in case of install failure.
- */
- public void installFeatures(Set<Feature> features, EnumSet<Option> options) throws Exception {
-
- final InstallationState state = new InstallationState();
- final InstallationState failure = new InstallationState();
- boolean verbose = options.contains(FeaturesService.Option.Verbose);
- try {
- // Install everything
- for (Feature f : features) {
- InstallationState s = new InstallationState();
- try {
- doInstallFeature(s, f, verbose);
- doInstallFeatureConditionals(s, f, verbose);
- //Check if current feature satisfies the conditionals of existing features
- for (Feature installedFeature : listInstalledFeatures()) {
- doInstallFeatureConditionals(s, installedFeature, verbose);
- }
- for (Feature installedFeature : state.features.keySet()) {
- doInstallFeatureConditionals(s, installedFeature, verbose);
- }
-
- state.bundleInfos.putAll(s.bundleInfos);
- state.bundles.addAll(s.bundles);
- state.features.putAll(s.features);
- state.installed.addAll(s.installed);
- state.bundleStartLevels.putAll(s.bundleStartLevels);
- } catch (Exception e) {
- failure.bundles.addAll(s.bundles);
- failure.features.putAll(s.features);
- failure.installed.addAll(s.installed);
- if (options.contains(Option.ContinueBatchOnFailure)) {
- LOGGER.warn("Error when installing feature {}: {}", f.getName(), e);
- } else {
- throw e;
- }
- }
- }
- bundleManager.refreshBundles(state.bundles, state.installed, options);
- // start all bundles sorted by startlvl if wished for
- List<Bundle> bundlesSortedByStartLvl = new ArrayList<Bundle>(state.bundles);
- if (respectStartLvlDuringFeatureStartup) {
- Collections.sort(bundlesSortedByStartLvl, new Comparator<Bundle>() {
- @Override
- public int compare(Bundle bundle, Bundle bundle1) {
- return state.bundleStartLevels.get(bundle) - state.bundleStartLevels.get(bundle1);
- }
- });
- }
- for (Bundle b : bundlesSortedByStartLvl) {
- LOGGER.debug("Starting bundle: {}", b.getSymbolicName());
- startBundle(state, b);
- }
- // Clean up for batch
- if (!options.contains(Option.NoCleanIfFailure)) {
- failure.installed.removeAll(state.bundles);
- if (failure.installed.size()>0) {
- bundleManager.uninstall(failure.installed);
- }
- }
- for (Feature f : features) {
- callListeners(new FeatureEvent(f, FeatureEvent.EventType.FeatureInstalled, false));
- }
- for (Map.Entry<Feature, Set<Long>> e : state.features.entrySet()) {
- installed.put(e.getKey(), e.getValue());
- }
- saveState();
- } catch (Exception e) {
- boolean noCleanIfFailure = options.contains(Option.NoCleanIfFailure);
- cleanUpOnFailure(state, failure, noCleanIfFailure);
- throw e;
- }
- }
-
- /**
- * Start a bundle.
- *
- * @param state the current bundle installation state.
- * @param bundle the bundle to start.
- * @throws Exception in case of start failure.
- */
- private void startBundle(InstallationState state, Bundle bundle) throws Exception {
- if (!isFragment(bundle)) {
- // do not start bundles that are persistently stopped
- if (state.installed.contains(bundle)
- || (bundle.getState() != Bundle.STARTING && bundle.getState() != Bundle.ACTIVE
- && bundle.adapt(BundleStartLevel.class).isPersistentlyStarted())) {
- // do no start bundles when user request it
- Long bundleId = bundle.getBundleId();
- BundleInfo bundleInfo = state.bundleInfos.get(bundleId);
- if (bundleInfo == null || bundleInfo.isStart()) {
- try {
- bundle.start();
- } catch (BundleException be) {
- String msg = format("Could not start bundle %s in feature(s) %s: %s", bundle.getLocation(), getFeaturesContainingBundleList(bundle), be.getMessage());
- throw new Exception(msg, be);
- }
- }
- }
- }
- }
-
- private boolean isFragment(Bundle b) {
- @SuppressWarnings("rawtypes")
- Dictionary d = b.getHeaders();
- String fragmentHostHeader = (String) d.get(Constants.FRAGMENT_HOST);
- return fragmentHostHeader != null && fragmentHostHeader.trim().length() > 0;
- }
-
- private void cleanUpOnFailure(InstallationState state, InstallationState failure, boolean noCleanIfFailure) {
- // cleanup on error
- if (!noCleanIfFailure) {
- HashSet<Bundle> uninstall = new HashSet<Bundle>();
- uninstall.addAll(state.installed);
- uninstall.addAll(failure.installed);
- if (uninstall.size() > 0) {
- bundleManager.uninstall(uninstall);
- }
- } else {
- // Force start of bundles so that they are flagged as persistently started
- for (Bundle b : state.installed) {
- try {
- b.start();
- } catch (Exception e2) {
- // Ignore
- }
- }
- }
- }
-
- protected void doInstallFeature(InstallationState state, Feature feature, boolean verbose) throws Exception {
- String msg = "Installing feature " + feature.getName() + " " + feature.getVersion();
- LOGGER.info(msg);
- if (verbose) {
- System.out.println(msg);
- }
- for (Dependency dependency : feature.getDependencies()) {
- installFeatureDependency(dependency, state, verbose);
- }
- if (configManager != null) {
- configManager.installFeatureConfigs(feature, verbose);
- }
- Set<Long> bundles = new TreeSet<Long>();
-
- for (BundleInfo bInfo : Overrides.override(resolve(feature), this.overrides)) {
- int startLevel = getBundleStartLevel(bInfo.getStartLevel(),feature.getStartLevel());
- BundleInstallerResult result = bundleManager.installBundleIfNeeded(bInfo.getLocation(), startLevel, feature.getRegion());
- state.bundles.add(result.bundle);
- state.bundleStartLevels.put(result.bundle, getBundleStartLevelForOrdering(startLevel));
- if (result.isNew) {
- state.installed.add(result.bundle);
- }
- String msg2 = (result.isNew) ? "Found installed bundle: " + result.bundle : "Installing bundle " + bInfo.getLocation();
- LOGGER.debug(msg2);
- if (verbose) {
- System.out.println(msg2);
- }
-
- bundles.add(result.bundle.getBundleId());
- state.bundleInfos.put(result.bundle.getBundleId(), bInfo);
-
- }
- state.features.put(feature, bundles);
- }
-
- private int getBundleStartLevel(int bundleStartLevel, int featureStartLevel) {
- return (bundleStartLevel > 0) ? bundleStartLevel : featureStartLevel;
- }
-
- private int getBundleStartLevelForOrdering(int startLevel){
- return startLevel == 0 ? KARAF_BUNDLE_START_LEVEL : startLevel;
- }
-
- protected void doInstallFeatureConditionals(InstallationState state, Feature feature, boolean verbose) throws Exception {
- //Check conditions of the current feature.
- feature = getFeature(feature.getName(), feature.getVersion());
- if (feature != null) {
- for (Conditional conditional : feature.getConditional()) {
-
- if (dependenciesSatisfied(conditional.getCondition(), state)) {
- InstallationState s = new InstallationState();
- doInstallFeature(s, conditional.asFeature(feature.getName(), feature.getVersion()), verbose);
- state.bundleInfos.putAll(s.bundleInfos);
- state.bundles.addAll(s.bundles);
- state.features.putAll(s.features);
- state.installed.addAll(s.installed);
- state.bundleStartLevels.putAll(s.bundleStartLevels);
- }
- }
- }
- }
-
- private void installFeatureDependency(Dependency dependency, InstallationState state, boolean verbose)
- throws Exception {
- Feature fi = getFeatureForDependency(dependency);
- if (fi == null) {
- throw new Exception("No feature named '" + dependency.getName()
- + "' with version '" + dependency.getVersion() + "' available");
- }
- if (state.features.containsKey(fi)) {
- LOGGER.debug("Feature {} with version {} is already being installed", fi.getName(), fi.getVersion());
- } else {
- doInstallFeature(state, fi, verbose);
- }
- }
-
- protected List<BundleInfo> resolve(Feature feature) throws Exception {
- String resolver = feature.getResolver();
- // If no resolver is specified, we expect a list of uris
- if (resolver == null || resolver.length() == 0) {
- return feature.getBundles();
- }
- boolean optional = false;
- if (resolver.startsWith("(") && resolver.endsWith(")")) {
- resolver = resolver.substring(1, resolver.length() - 1);
- optional = true;
- }
-
-
- @SuppressWarnings("unchecked")
- ServiceTracker<Resolver, Resolver> tracker = bundleManager.createServiceTrackerForResolverName(resolver);
- if (tracker == null) {
- return feature.getBundles();
- }
- tracker.open();
- try {
- if (optional) {
- Resolver r = (Resolver) tracker.getService();
- if (r != null) {
- return r.resolve(feature);
- } else {
- LOGGER.debug("Optional resolver '" + resolver + "' not found, using the default resolver");
- return feature.getBundles();
- }
- } else {
- Resolver r = (Resolver) tracker.waitForService(resolverTimeout);
- if (r == null) {
- throw new Exception("Unable to find required resolver '" + resolver + "'");
- }
- return r.resolve(feature);
- }
- } finally {
- tracker.close();
- }
- }
-
- 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())) {
- versions.add(f.getVersion());
- }
- }
- if (versions.size() == 0) {
- throw new Exception("Feature named '" + name + "' is not installed");
- } else if (versions.size() > 1) {
- StringBuilder sb = new StringBuilder();
- sb.append("Feature named '").append(name).append("' has multiple versions installed (");
- for (int i = 0; i < versions.size(); i++) {
- if (i > 0) {
- sb.append(", ");
- }
- sb.append(versions.get(i));
- }
- sb.append("). Please specify the version to uninstall.");
- throw new Exception(sb.toString());
- }
- 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");
- }
- boolean verbose = options != null && options.contains(Option.Verbose);
- boolean refresh = options == null || !options.contains(Option.NoAutoRefreshBundles);
- String msg = "Uninstalling feature " + feature.getName() + " " + feature.getVersion();
- LOGGER.info(msg);
- if (verbose) {
- System.out.println(msg);
- }
- // Grab all the bundles installed by this feature
- // and remove all those who will still be in use.
- // This gives this list of bundles to uninstall.
- Set<Long> bundles = installed.remove(feature);
-
- //Also remove bundles installed as conditionals
- for (Conditional conditional : feature.getConditional()) {
- Feature conditionalFeature = conditional.asFeature(feature.getName(),feature.getVersion());
- if (installed.containsKey(conditionalFeature)) {
- msg = "Uninstalling feature " + conditionalFeature.getName() + " " + conditionalFeature.getVersion();
- LOGGER.info(msg);
- if (verbose) {
- System.out.println(msg);
- }
- bundles.addAll(installed.remove(conditionalFeature));
- } else {
- LOGGER.info("Conditional feature {}, hasn't been installed!");
- }
- }
- for (Feature f : new HashSet<Feature>(installed.keySet())) {
- f = getFeature(f.getName(), f.getVersion());
- if (f != null) {
- for (Conditional conditional : f.getConditional()) {
- boolean satisfied = true;
- for (Dependency dep : conditional.getCondition()) {
- Feature df = getFeatureForDependency(dep);
- satisfied &= installed.containsKey(df);
- }
- if (!satisfied) {
- Feature conditionalFeature = conditional.asFeature(f.getName(), f.getVersion());
- if (installed.containsKey(conditionalFeature)) {
- msg = "Uninstalling feature " + conditionalFeature.getName() + " " + conditionalFeature.getVersion();
- LOGGER.info(msg);
- if (verbose) {
- System.out.println(msg);
- }
- bundles.addAll(installed.remove(conditionalFeature));
- }
- }
- }
- }
- }
-
- for (Set<Long> b : installed.values()) {
- bundles.removeAll(b);
- }
-
- List<Bundle> bundlesDescendSortedByStartLvl = new ArrayList<Bundle>();
- for (long bundleId : bundles) {
- Bundle b = bundleManager.getBundleContext().getBundle(bundleId);
- if (b != null) {
- bundlesDescendSortedByStartLvl.add(b);
- }
- }
-
- if (isRespectStartLvlDuringFeatureUninstall()) {
- Collections.sort(bundlesDescendSortedByStartLvl, new Comparator<Bundle>() {
- @Override
- public int compare(Bundle bundle, Bundle bundle1) {
- return bundle1.adapt(BundleStartLevel.class).getStartLevel() -
- bundle.adapt(BundleStartLevel.class).getStartLevel();
- }
- });
- }
- bundleManager.uninstall(bundlesDescendSortedByStartLvl, refresh);
- callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureUninstalled, false));
- saveState();
- }
-
- public Feature[] listFeatures() throws Exception {
- Set<Feature> features = new HashSet<Feature>();
- for (Map<String, Feature> featureWithDifferentVersion : getFeatures().values()) {
- for (Feature f : featureWithDifferentVersion.values()) {
- features.add(f);
- }
- }
- return features.toArray(new Feature[features.size()]);
- }
-
- public Feature[] listInstalledFeatures() throws Exception {
- Set<Feature> features = new HashSet<Feature>();
- for (Map<String, Feature> featureWithDifferentVersion : getFeatures().values()) {
- for (Feature f : featureWithDifferentVersion.values()) {
- if (installed.containsKey(f)) {
- features.add(f);
- }
- }
- }
- return features.toArray(new Feature[features.size()]);
- }
-
- public boolean isInstalled(Feature f) {
- return installed.containsKey(f);
- }
-
- public Feature getFeature(String name) throws Exception {
- return getFeature(name, org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION);
- }
-
- public Feature getFeature(String name, String version) throws Exception {
- if (version != null) {
- version = version.trim();
- }
- Map<String, Feature> versions = getFeatures().get(name);
- if (versions == null || versions.isEmpty()) {
- return null;
- } else {
- Feature feature = versions.get(version);
- if (feature == null) {
- if (org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(version)) {
- Version latest = new Version(cleanupVersion(version));
- for (String available : versions.keySet()) {
- Version availableVersion = new Version(cleanupVersion(available));
- if (availableVersion.compareTo(latest) > 0) {
- feature = versions.get(available);
- latest = availableVersion;
- }
- }
- } else {
- Version latest = new Version(cleanupVersion(org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
- VersionRange versionRange = new VersionRange(version, true, true);
- for (String available : versions.keySet()) {
- Version availableVersion = new Version(cleanupVersion(available));
- if (availableVersion.compareTo(latest) > 0 && versionRange.contains(availableVersion)) {
- feature = versions.get(available);
- latest = availableVersion;
- }
- }
- }
- }
- return feature;
- }
- }
-
- @Override
- public URI getRepositoryUriFor(String name, String version) {
- return featureFinder.getUriFor(name, version);
- }
-
- @Override
- public String[] getRepositoryNames() {
- return featureFinder.getNames();
- }
-
- protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
- if (features == null) {
- //the outer map's key is feature name, the inner map's key is feature version
- Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
- // Two phase load:
- // * first load dependent repositories
- for (;;) {
- boolean newRepo = false;
- for (Repository repo : listRepositories()) {
- for (URI uri : repo.getRepositories()) {
- if (!repositories.containsKey(uri)) {
- internalAddRepository(uri);
- newRepo = true;
- }
- }
- }
- if (!newRepo) {
- break;
- }
- }
- // * then load all features
- for (Repository repo : repositories.values()) {
- for (Feature f : repo.getFeatures()) {
- if (map.get(f.getName()) == null) {
- Map<String, Feature> versionMap = new HashMap<String, Feature>();
- versionMap.put(f.getVersion(), f);
- map.put(f.getName(), versionMap);
- } else {
- map.get(f.getName()).put(f.getVersion(), f);
- }
- }
- }
- features = map;
- }
- return features;
- }
-
- private void initState() {
- if (!loadState()) {
- if (uris != null) {
- for (URI uri : uris) {
- try {
- internalAddRepository(uri);
- } catch (Exception e) {
- LOGGER.warn(format("Unable to add features repository %s at startup", uri), e);
- }
- }
- }
- saveState();
- }
- }
-
- public void start() {
- this.eventAdminListener = bundleManager.createAndRegisterEventAdminListener();
- initState();
- }
-
- public void stop() {
- stopped.set(true);
- uris = new HashSet<URI>(repositories.keySet());
- while (!repositories.isEmpty()) {
- internalRemoveRepository(repositories.keySet().iterator().next());
- }
- }
-
- protected void saveState() {
- // Never save the state after the service has been stopped
- if (stopped.get()) {
- return;
- }
- OutputStream os = null;
- try {
- File file = bundleManager.getDataFile("FeaturesServiceState.properties");
- Properties props = new Properties();
- saveSet(props, "repositories.", repositories.keySet());
- saveMap(props, "features.", installed);
- os = new FileOutputStream(file);
- props.store(new FileOutputStream(file), "FeaturesService State");
- } catch (Exception e) {
- LOGGER.error("Error persisting FeaturesService state", e);
- } finally {
- close(os);
- }
- }
-
- protected boolean loadState() {
- try {
- File file = bundleManager.getDataFile("FeaturesServiceState.properties");
- if (!file.exists()) {
- return false;
- }
- Properties props = new Properties();
- InputStream is = new FileInputStream(file);
- try {
- props.load(is);
- } finally {
- close(is);
- }
- Set<URI> repositories = loadSet(props, "repositories.");
- for (URI repo : repositories) {
- try {
- internalAddRepository(repo);
- } catch (Exception e) {
- LOGGER.warn(format("Unable to add features repository %s at startup", repo), e);
- }
- }
- installed = loadMap(props, "features.");
- for (Feature f : installed.keySet()) {
- callListeners(new FeatureEvent(f, FeatureEvent.EventType.FeatureInstalled, true));
- }
- return true;
- } catch (Exception e) {
- LOGGER.error("Error loading FeaturesService state", e);
- }
- return false;
- }
-
- private void close(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }
-
- protected void saveSet(Properties props, String prefix, Set<URI> set) {
- List<URI> l = new ArrayList<URI>(set);
- props.clear();
- props.put(prefix + "count", Integer.toString(l.size()));
- for (int i = 0; i < l.size(); i++) {
- props.put(prefix + "item." + i, l.get(i).toString());
- }
- }
-
- protected Set<URI> loadSet(Properties props, String prefix) {
- Set<URI> l = new HashSet<URI>();
- String countStr = (String) props.get(prefix + "count");
- if (countStr != null) {
- int count = Integer.parseInt(countStr);
- for (int i = 0; i < count; i++) {
- l.add(URI.create((String) props.get(prefix + "item." + i)));
- }
- }
- return l;
- }
-
- protected void saveMap(Properties props, String prefix, Map<Feature, Set<Long>> map) {
- for (Map.Entry<Feature, Set<Long>> entry : map.entrySet()) {
- Feature key = entry.getKey();
- String val = createValue(entry.getValue());
- props.put(prefix + key.toString(), val);
- }
- }
-
- protected Map<Feature, Set<Long>> loadMap(Properties props, String prefix) {
- Map<Feature, Set<Long>> map = new HashMap<Feature, Set<Long>>();
- for (@SuppressWarnings("rawtypes") Enumeration e = props.propertyNames(); e.hasMoreElements();) {
- String key = (String) e.nextElement();
- if (key.startsWith(prefix)) {
- String val = (String) props.get(key);
- Set<Long> set = readValue(val);
- map.put(org.apache.karaf.features.internal.model.Feature.valueOf(key.substring(prefix.length())), set);
- }
- }
- return map;
- }
-
- protected String createValue(Set<Long> set) {
- StringBuilder sb = new StringBuilder();
- for (long i : set) {
- if (sb.length() > 0) {
- sb.append(",");
- }
- sb.append(i);
- }
- return sb.toString();
- }
-
- protected Set<Long> readValue(String val) {
- Set<Long> set = new HashSet<Long>();
- if (val != null && val.length() != 0) {
- for (String str : val.split(",")) {
- set.add(Long.parseLong(str));
- }
- }
- return set;
- }
-
- protected void callListeners(FeatureEvent event) {
- if (eventAdminListener != null) {
- eventAdminListener.featureEvent(event);
- }
- for (FeaturesListener listener : listeners) {
- listener.featureEvent(event);
- }
- }
-
- protected void callListeners(RepositoryEvent event) {
- if (eventAdminListener != null) {
- eventAdminListener.repositoryEvent(event);
- }
- for (FeaturesListener listener : listeners) {
- listener.repositoryEvent(event);
- }
- }
-
- static Pattern fuzzyVersion = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
- Pattern.DOTALL);
- static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
- Pattern.DOTALL);
-
- /**
- * Clean up version parameters. Other builders use more fuzzy definitions of
- * the version syntax. This method cleans up such a version to match an OSGi
- * version.
- *
- * @param version possibly bundles-non-compliant version
- * @return osgi compliant version
- */
- static public String cleanupVersion(String version) {
- Matcher m = fuzzyVersion.matcher(version);
- if (m.matches()) {
- StringBuffer result = new StringBuffer();
- String d1 = m.group(1);
- String d2 = m.group(3);
- String d3 = m.group(5);
- String qualifier = m.group(7);
-
- if (d1 != null) {
- result.append(d1);
- if (d2 != null) {
- result.append(".");
- result.append(d2);
- if (d3 != null) {
- result.append(".");
- result.append(d3);
- if (qualifier != null) {
- result.append(".");
- cleanupModifier(result, qualifier);
- }
- } else if (qualifier != null) {
- result.append(".0.");
- cleanupModifier(result, qualifier);
- }
- } else if (qualifier != null) {
- result.append(".0.0.");
- cleanupModifier(result, qualifier);
- }
- return result.toString();
- }
- }
- return version;
- }
-
- static void cleanupModifier(StringBuffer result, String modifier) {
- Matcher m = fuzzyModifier.matcher(modifier);
- if (m.matches())
- modifier = m.group(2);
-
- for (int i = 0; i < modifier.length(); i++) {
- char c = modifier.charAt(i);
- if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
- result.append(c);
- }
- }
-
- public Set<Feature> getFeaturesContainingBundle (Bundle bundle) throws Exception {
- Set<Feature> features = new HashSet<Feature>();
- for (Map<String, Feature> featureMap : this.getFeatures().values()) {
- for (Feature f : featureMap.values()) {
- for (BundleInfo bi : f.getBundles()) {
- if (bi.getLocation().equals(bundle.getLocation())) {
- features.add(f);
- break;
- }
- }
- }
- }
- return features;
- }
-
- private String getFeaturesContainingBundleList(Bundle bundle) throws Exception {
- Set<Feature> features = getFeaturesContainingBundle(bundle);
- StringBuilder buffer = new StringBuilder();
- Iterator<Feature> iter = features.iterator();
- while (iter.hasNext()) {
- Feature feature= iter.next();
- buffer.append(feature.getId());
- if (iter.hasNext()) {
- buffer.append(", ");
- }
- }
- return buffer.toString();
- }
-
- /**
- * Returns the {@link Feature} that matches the {@link Dependency}.
- * @param dependency
- * @return
- * @throws Exception
- */
- private Feature getFeatureForDependency(Dependency dependency) throws Exception {
- VersionRange range = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION.equals(dependency.getVersion())
- ? VersionRange.ANY_VERSION : new VersionRange(dependency.getVersion(), true, true);
- Feature fi = null;
- for (Feature f : installed.keySet()) {
- if (f.getName().equals(dependency.getName())) {
- Version v = VersionTable.getVersion(f.getVersion());
- if (range.contains(v)) {
- if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
- fi = f;
- }
- }
- }
- }
- if (fi == null) {
- Map<String, Feature> avail = getFeatures().get(dependency.getName());
- if (avail != null) {
- for (Feature f : avail.values()) {
- Version v = VersionTable.getVersion(f.getVersion());
- if (range.contains(v)) {
- if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
- fi = f;
- }
- }
- }
- }
- }
- return fi;
- }
-
- /**
- * Estimates if the {@link List} of {@link Dependency} is satisfied.
- * The method will look into {@link Feature}s that are already installed or now being installed (if {@link InstallationState} is provided (not null)).
- * @param dependencies
- * @param state
- * @return
- */
- private boolean dependenciesSatisfied(List<? extends Dependency> dependencies, InstallationState state) throws Exception {
- boolean satisfied = true;
- for (Dependency dep : dependencies) {
- Feature f = getFeatureForDependency(dep);
- if (f != null && !isInstalled(f) && (state != null && !state.features.keySet().contains(f))) {
- satisfied = false;
- }
- }
- return satisfied;
- }
-
- public boolean isRespectStartLvlDuringFeatureUninstall() {
- return respectStartLvlDuringFeatureUninstall;
- }
-
- public void setRespectStartLvlDuringFeatureUninstall(boolean respectStartLvlDuringFeatureUninstall) {
- this.respectStartLvlDuringFeatureUninstall = respectStartLvlDuringFeatureUninstall;
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java b/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java
deleted file mode 100644
index 0594497..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/InstallationState.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.karaf.features.internal;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.Feature;
-import org.osgi.framework.Bundle;
-
-public class InstallationState {
- final Set<Bundle> installed = new HashSet<Bundle>();
- final Set<Bundle> bundles = new TreeSet<Bundle>();
- final Map<Long, BundleInfo> bundleInfos = new HashMap<Long, BundleInfo>();
- final Map<Bundle,Integer> bundleStartLevels = new HashMap<Bundle, Integer>();
- final Map<Feature, Set<Long>> features = new HashMap<Feature, Set<Long>>();
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java b/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java
deleted file mode 100644
index dab988a..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/Overrides.java
+++ /dev/null
@@ -1,230 +0,0 @@
-
-/*
- * 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.karaf.features.internal;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import org.apache.felix.utils.manifest.Clause;
-import org.apache.felix.utils.manifest.Parser;
-import org.apache.felix.utils.version.VersionRange;
-import org.apache.felix.utils.version.VersionTable;
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.internal.model.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Helper class to deal with overriden bundles at feature installation time.
- \*/
-public class Overrides {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(Overrides.class);
-
- protected static final String OVERRIDE_RANGE = "range";
- private static final String VENDOR_WARNING = "Bundle Vendor has changed, please check if this is intentional. Bundle: ";
-
- /**
- * Compute a list of bundles to install, taking into account overrides.
- *
- * The file containing the overrides will be loaded from the given url.
- * Blank lines and lines starting with a '#' will be ignored, all other lines
- * are considered as urls to override bundles.
- *
- * The list of bundles to install will be scanned and for each bundle,
- * if a bundle override matches that bundle, it will be used instead.
- *
- * Matching is done on bundle symbolic name (they have to be the same)
- * and version (the bundle override version needs to be greater than the
- * bundle to be installed, and less than the next minor version. A range
- * directive can be added to the override url in which case, the matching
- * will succeed if the bundle to be installed is within the given range.
- *
- * @param infos the list of bundles to install
- * @param overridesUrl url pointing to the file containing the list of override bundles
- * @return a new list of bundles to install
- */
- public static List<BundleInfo> override(List<BundleInfo> infos, String overridesUrl) {
- List<Clause> overrides = loadOverrides(overridesUrl);
- if (overrides.isEmpty()) {
- return infos;
- }
- try {
- Map<String, Manifest> manifests = new HashMap<String, Manifest>();
- for (Clause override : overrides) {
- Manifest manifest = getManifest(override.getName());
- manifests.put(override.getName(), manifest);
- }
- List<BundleInfo> newInfos = new ArrayList<BundleInfo>();
- for (BundleInfo info : infos) {
- Manifest manifest = getManifest(info.getLocation());
- if (manifest != null) {
- String bsn = getBundleSymbolicName(manifest);
- Version ver = getBundleVersion(manifest);
- String ven = getBundleVendor(manifest);
- String url = info.getLocation();
- for (Clause override : overrides) {
- Manifest overMan = manifests.get(override.getName());
- if (overMan == null) {
- continue;
- }
- String oBsn = getBundleSymbolicName(overMan);
- if (!bsn.equals(oBsn)) {
- continue;
- }
-
- Version oVer = getBundleVersion(overMan);
- VersionRange range;
- String vr = extractVersionRange(override);
- if (vr == null) {
- // default to micro version compatibility
- Version v2 = new Version(oVer.getMajor(), oVer.getMinor(), 0);
- if (v2.equals(oVer)) {
- continue;
- }
- range = new VersionRange(false, v2, oVer, true);
- } else {
- range = VersionRange.parseVersionRange(vr);
- }
-
- String vendor = getBundleVendor(overMan);
-
- // Before we do a replace, lets check if vendors change
- if (ven == null) {
- if (vendor != null) {
- LOGGER.warn(VENDOR_WARNING + bsn);
- }
- } else {
- if (vendor == null) {
- LOGGER.warn(VENDOR_WARNING + bsn);
- } else {
- if (!vendor.equals(ven)) {
- LOGGER.warn(VENDOR_WARNING + bsn);
- }
- }
- }
- // The resource matches, so replace it with the overridden resource
- // if the override is actually a newer version than what we currently have
- if (range.contains(ver) && ver.compareTo(oVer) < 0) {
- LOGGER.warn("Overriding original bundle " + url + " to " + override.getName());
- ver = oVer;
- url = override.getName();
- }
- }
- if (!info.getLocation().equals(url)) {
- Bundle b = new Bundle();
- b.setLocation(url);
- b.setStartLevel(info.getStartLevel());
- b.setStart(info.isStart());
- b.setDependency(info.isDependency());
- newInfos.add(b);
- } else {
- newInfos.add(info);
- }
- } else {
- newInfos.add(info);
- }
- }
- return newInfos;
- } catch (Exception e) {
- LOGGER.info("Unable to process bundle overrides", e);
- return infos;
- }
- }
-
- public static List<Clause> loadOverrides(String overridesUrl) {
- List<Clause> overrides = new ArrayList<Clause>();
- try {
- if (overridesUrl != null) {
- InputStream is = new URL(overridesUrl).openStream();
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(is));
- String line;
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (!line.isEmpty() && !line.startsWith("#")) {
- Clause[] cs = Parser.parseClauses(new String[]{line});
- Collections.addAll(overrides, cs);
- }
- }
- } finally {
- is.close();
- }
- }
- } catch (Exception e) {
- LOGGER.debug("Unable to load overrides bundles list", e);
- }
- return overrides;
- }
-
- private static Version getBundleVersion(Manifest manifest) {
- String ver = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
- return VersionTable.getVersion(ver);
- }
-
- private static String getBundleSymbolicName(Manifest manifest) {
- String bsn = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- bsn = stripSymbolicName(bsn);
- return bsn;
- }
-
- private static String getBundleVendor(Manifest manifest) {
- String ven = manifest.getMainAttributes().getValue(Constants.BUNDLE_VENDOR);
- return ven;
- }
-
- private static Manifest getManifest(String url) throws IOException {
- InputStream is = new URL(url).openStream();
- try {
- ZipInputStream zis = new ZipInputStream(is);
- ZipEntry entry = null;
- while ( (entry = zis.getNextEntry()) != null ) {
- if ("META-INF/MANIFEST.MF".equals(entry.getName())) {
- return new Manifest(zis);
- }
- }
- return null;
- } finally {
- is.close();
- }
- }
-
- private static String extractVersionRange(Clause override) {
- return override.getAttribute(OVERRIDE_RANGE);
- }
-
- private static String stripSymbolicName(String symbolicName) {
- Clause[] cs = Parser.parseHeader(symbolicName);
- if (cs == null || cs.length != 1) {
- throw new IllegalArgumentException("Bad Bundle-SymbolicName header: " + symbolicName);
- }
- return cs[0].getName();
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
deleted file mode 100644
index 435ba36..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/RepositoryImpl.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.karaf.features.internal;
-
-import java.io.BufferedInputStream;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.net.URI;
-import org.apache.karaf.features.Repository;
-import org.apache.karaf.features.internal.model.Features;
-import org.apache.karaf.features.internal.model.JaxbUtil;
-
-/**
- * The repository implementation.
- */
-public class RepositoryImpl implements Repository {
-
- private URI uri;
- private boolean valid;
- private Features features;
-
- public RepositoryImpl(URI uri) {
- this.uri = uri;
- }
-
- public String getName() {
- return features.getName();
- }
-
- public URI getURI() {
- return uri;
- }
-
- public URI[] getRepositories() throws Exception {
- load();
- URI[] result = new URI[features.getRepository().size()];
- for (int i = 0; i < features.getRepository().size(); i++) {
- String uri = features.getRepository().get(i);
- uri = uri.trim();
- result[i] = URI.create(uri);
- }
- return result;
- }
-
- public org.apache.karaf.features.Feature[] getFeatures() throws Exception {
- load();
- return features.getFeature().toArray(new org.apache.karaf.features.Feature[0]);
- }
-
-
- public void load() throws IOException {
- if (features == null) {
- try {
- InputStream inputStream = uri.toURL().openStream();
- inputStream = new FilterInputStream(inputStream) {
- @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);
- }
- };
- try {
- features = JaxbUtil.unmarshal(inputStream, false);
- } finally {
- inputStream.close();
- }
- valid = true;
- } catch (IllegalArgumentException e) {
- throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
- } catch (Exception e) {
- throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
- }
- }
- }
-
- public boolean isValid() {
- return this.valid;
- }
-
-}
-
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
new file mode 100644
index 0000000..7226471
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/DeploymentBuilder.java
@@ -0,0 +1,330 @@
+/*
+ * 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.karaf.features.internal.deployment;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.felix.resolver.ResolverImpl;
+import org.apache.felix.utils.version.VersionRange;
+import org.apache.felix.utils.version.VersionTable;
+import org.apache.karaf.features.BundleInfo;
+import org.apache.karaf.features.Conditional;
+import org.apache.karaf.features.Dependency;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.Repository;
+import org.apache.karaf.features.internal.repository.AggregateRepository;
+import org.apache.karaf.features.internal.repository.StaticRepository;
+import org.apache.karaf.features.internal.resolver.FeatureNamespace;
+import org.apache.karaf.features.internal.resolver.FeatureResource;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.ResolveContextImpl;
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
+import org.apache.karaf.features.internal.service.Overrides;
+import org.apache.karaf.features.internal.util.Macro;
+import org.apache.karaf.features.internal.util.MultiException;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.service.resolver.ResolutionException;
+import org.osgi.service.resolver.ResolveContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ */
+public class DeploymentBuilder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeploymentBuilder.class);
+
+ public static final String REQ_PROTOCOL = "req:";
+
+ private final Collection<Repository> repositories;
+
+ private final List<org.osgi.service.repository.Repository> resourceRepos;
+
+ String featureRange = "${range;[====,====]}";
+
+ Downloader downloader;
+ ResourceImpl requirements;
+ Map<String, Resource> resources;
+ Map<String, StreamProvider> providers;
+
+ Set<Feature> featuresToRegister = new HashSet<Feature>();
+
+ public DeploymentBuilder(Downloader downloader,
+ Collection<Repository> repositories) {
+ this.downloader = downloader;
+ this.repositories = repositories;
+ this.resourceRepos = new ArrayList<org.osgi.service.repository.Repository>();
+ }
+
+ public void addResourceRepository(org.osgi.service.repository.Repository repository) {
+ resourceRepos.add(repository);
+ }
+
+ public Map<String, StreamProvider> getProviders() {
+ return providers;
+ }
+
+ public void setFeatureRange(String featureRange) {
+ this.featureRange = featureRange;
+ }
+
+ public Map<String, Resource> download(
+ Set<String> features,
+ Set<String> bundles,
+ Set<String> reqs,
+ Set<String> overrides,
+ Set<String> optionals)
+ throws IOException, MultiException, InterruptedException, ResolutionException, BundleException {
+ this.resources = new ConcurrentHashMap<String, Resource>();
+ this.providers = new ConcurrentHashMap<String, StreamProvider>();
+ this.requirements = new ResourceImpl("dummy", "dummy", Version.emptyVersion);
+ // First, gather all bundle resources
+ for (String feature : features) {
+ registerMatchingFeatures(feature);
+ }
+ for (String bundle : bundles) {
+ downloadAndBuildResource(bundle);
+ }
+ for (String req : reqs) {
+ buildRequirement(req);
+ }
+ for (String override : overrides) {
+ // TODO: ignore download failures for overrides
+ downloadAndBuildResource(Overrides.extractUrl(override));
+ }
+ for (String optional : optionals) {
+ downloadAndBuildResource(optional);
+ }
+ // Wait for all resources to be created
+ downloader.await();
+ // Do override replacement
+ Overrides.override(resources, overrides);
+ // Build features resources
+ for (Feature feature : featuresToRegister) {
+ Resource resource = FeatureResource.build(feature, featureRange, resources);
+ resources.put("feature:" + feature.getName() + "/" + feature.getVersion(), resource);
+ }
+ // Build requirements
+ for (String feature : features) {
+ requireFeature(feature, requirements);
+ }
+ for (String bundle : bundles) {
+ requireResource(bundle);
+ }
+ for (String req : reqs) {
+ requireResource(REQ_PROTOCOL + req);
+ }
+ return resources;
+ }
+
+ public Collection<Resource> resolve(List<Resource> systemBundles,
+ boolean resolveOptionalImports) throws ResolutionException {
+ // Resolve
+ for (int i = 0; i < systemBundles.size(); i++) {
+ resources.put("system-bundle-" + i, systemBundles.get(i));
+ }
+
+ List<org.osgi.service.repository.Repository> repos = new ArrayList<org.osgi.service.repository.Repository>();
+ repos.add(new StaticRepository(resources.values()));
+ repos.addAll(resourceRepos);
+
+ ResolverImpl resolver = new ResolverImpl(new Slf4jResolverLog(LOGGER));
+ ResolveContext context = new ResolveContextImpl(
+ Collections.<Resource>singleton(requirements),
+ Collections.<Resource>emptySet(),
+ new AggregateRepository(repos),
+ resolveOptionalImports);
+
+ Map<Resource, List<Wire>> resolution = resolver.resolve(context);
+ return resolution.keySet();
+ }
+
+ public void requireFeature(String feature, ResourceImpl resource) throws IOException {
+ // Find name and version range
+ String[] split = feature.split("/");
+ String name = split[0].trim();
+ String version = (split.length > 1) ? split[1].trim() : null;
+ if (version != null && !version.equals("0.0.0") && !version.startsWith("[") && !version.startsWith("(")) {
+ version = Macro.transform(featureRange, version);
+ }
+ VersionRange range = version != null ? new VersionRange(version) : VersionRange.ANY_VERSION;
+ // Add requirement
+ Map<String, Object> attrs = new HashMap<String, Object>();
+ attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, name);
+ attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, FeatureNamespace.TYPE_FEATURE);
+ attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, range);
+ resource.addRequirement(
+ new RequirementImpl(resource, IdentityNamespace.IDENTITY_NAMESPACE,
+ Collections.<String, String>emptyMap(), attrs)
+ );
+ }
+
+ public void requireResource(String location) {
+ Resource res = resources.get(location);
+ if (res == null) {
+ throw new IllegalStateException("Could not find resource for " + location);
+ }
+ List<Capability> caps = res.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+ if (caps.size() != 1) {
+ throw new IllegalStateException("Resource does not have a single " + IdentityNamespace.IDENTITY_NAMESPACE + " capability");
+ }
+ Capability cap = caps.get(0);
+ // Add requirement
+ Map<String, Object> attrs = new HashMap<String, Object>();
+ attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, cap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+ attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, cap.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+ attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, new VersionRange((Version) cap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE), true));
+ requirements.addRequirement(
+ new RequirementImpl(requirements, IdentityNamespace.IDENTITY_NAMESPACE,
+ Collections.<String, String>emptyMap(), attrs));
+
+ }
+
+ public void registerMatchingFeatures(String feature) throws IOException {
+ // Find name and version range
+ String[] split = feature.split("/");
+ String name = split[0].trim();
+ String version = (split.length > 1)
+ ? split[1].trim() : Version.emptyVersion.toString();
+ // Register matching features
+ registerMatchingFeatures(name, new VersionRange(version));
+ }
+
+ public void registerMatchingFeatures(String name, String version) throws IOException {
+ if (version != null && !version.equals("0.0.0") && !version.startsWith("[") && !version.startsWith("(")) {
+ version = Macro.transform(featureRange, version);
+ }
+ registerMatchingFeatures(name, version != null ? new VersionRange(version) : VersionRange.ANY_VERSION);
+ }
+
+ public void registerMatchingFeatures(String name, VersionRange range) throws IOException {
+ for (Repository repo : repositories) {
+ Feature[] features;
+ try {
+ features = repo.getFeatures();
+ } catch (Exception e) {
+ // This should not happen as the repository has been loaded already
+ throw new IllegalStateException(e);
+ }
+ for (Feature f : features) {
+ if (name.equals(f.getName())) {
+ Version v = VersionTable.getVersion(f.getVersion());
+ if (range.contains(v)) {
+ featuresToRegister.add(f);
+ for (Dependency dep : f.getDependencies()) {
+ registerMatchingFeatures(dep.getName(), dep.getVersion());
+ }
+ for (BundleInfo bundle : f.getBundles()) {
+ downloadAndBuildResource(bundle.getLocation());
+ }
+ for (Conditional cond : f.getConditional()) {
+ Feature c = cond.asFeature(f.getName(), f.getVersion());
+ featuresToRegister.add(c);
+ for (BundleInfo bundle : c.getBundles()) {
+ downloadAndBuildResource(bundle.getLocation());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void buildRequirement(String requirement) {
+ try {
+ String location = REQ_PROTOCOL + requirement;
+ ResourceImpl resource = new ResourceImpl(location, "dummy", Version.emptyVersion);
+ for (Requirement req : ResourceBuilder.parseRequirement(resource, requirement)) {
+ resource.addRequirement(req);
+ }
+ resources.put(location, resource);
+ } catch (BundleException e) {
+ throw new IllegalArgumentException("Error parsing requirement: " + requirement, e);
+ }
+ }
+
+ public void downloadAndBuildResource(final String location) throws IOException {
+ if (!resources.containsKey(location)) {
+ downloader.download(location, new Downloader.DownloadCallback() {
+ @Override
+ public void downloaded(StreamProvider provider) throws Exception {
+ manageResource(location, provider);
+ }
+ });
+ }
+ }
+
+ private void manageResource(String location, StreamProvider provider) throws Exception {
+ if (!resources.containsKey(location)) {
+ Attributes attributes = getAttributes(location, provider);
+ Resource resource = createResource(location, attributes);
+ resources.put(location, resource);
+ providers.put(location, provider);
+ }
+ }
+
+ private Resource createResource(String uri, Attributes attributes) throws Exception {
+ Map<String, String> headers = new HashMap<String, String>();
+ for (Map.Entry attr : attributes.entrySet()) {
+ headers.put(attr.getKey().toString(), attr.getValue().toString());
+ }
+ try {
+ return ResourceBuilder.build(uri, headers);
+ } catch (BundleException e) {
+ throw new Exception("Unable to create resource for bundle " + uri, e);
+ }
+ }
+
+ protected Attributes getAttributes(String uri, StreamProvider provider) throws Exception {
+ InputStream is = provider.open();
+ try {
+ ZipInputStream zis = new ZipInputStream(is);
+ ZipEntry entry;
+ while ( (entry = zis.getNextEntry()) != null ) {
+ if ("META-INF/MANIFEST.MF".equals(entry.getName())) {
+ return new Manifest(zis).getMainAttributes();
+ }
+ }
+ } finally {
+ is.close();
+ }
+ throw new IllegalArgumentException("Resource " + uri + " does not contain a manifest");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java
new file mode 100644
index 0000000..2d5dd98
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/Downloader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.karaf.features.internal.deployment;
+
+import java.net.MalformedURLException;
+
+import org.apache.karaf.features.internal.util.MultiException;
+
+public interface Downloader {
+
+ void await() throws InterruptedException, MultiException;
+
+ void download(String location, DownloadCallback downloadCallback) throws MalformedURLException;
+
+ interface DownloadCallback {
+
+ void downloaded(StreamProvider provider) throws Exception;
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
new file mode 100644
index 0000000..60a3dfc
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/deployment/StreamProvider.java
@@ -0,0 +1,26 @@
+/*
+ * 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.karaf.features.internal.deployment;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface StreamProvider {
+
+ InputStream open() throws IOException;
+
+}