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:52:52 UTC
[01/13] git commit: [KARAF-2805] Refactor Registry#hasCommand to
Registry#getCommand
Repository: karaf
Updated Branches:
refs/heads/master fe2627f22 -> 38502e415
[KARAF-2805] Refactor Registry#hasCommand to Registry#getCommand
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/5337778a
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/5337778a
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/5337778a
Branch: refs/heads/master
Commit: 5337778a3e2df088d77157fe876f14103fc9710c
Parents: fe2627f
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Thu Apr 3 09:43:01 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon Apr 7 10:52:23 2014 +0200
----------------------------------------------------------------------
.../org/apache/karaf/itests/KarafTestSupport.java | 2 +-
.../org/apache/karaf/shell/api/console/Registry.java | 3 +--
.../impl/action/osgi/AggregateServiceTracker.java | 2 +-
.../shell/impl/action/osgi/CommandExtension.java | 2 +-
.../karaf/shell/impl/action/osgi/RegistryImpl.java | 14 ++++++++++----
.../apache/karaf/shell/impl/console/RegistryImpl.java | 14 ++++++++++----
6 files changed, 24 insertions(+), 13 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/5337778a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
index ea7ac9c..5062c64 100644
--- a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
+++ b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
@@ -291,7 +291,7 @@ public class KarafTestSupport {
long start = System.currentTimeMillis();
long cur = start;
while (cur - start < SERVICE_TIMEOUT) {
- if (sessionFactory.getRegistry().hasCommand(scope, name)) {
+ if (sessionFactory.getRegistry().getCommand(scope, name) != null) {
return;
}
Thread.sleep(100);
http://git-wip-us.apache.org/repos/asf/karaf/blob/5337778a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
index 35c7c75..8df1b22 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
@@ -40,9 +40,8 @@ public interface Registry {
* @param scope
* @param name
* @return
- * @throws InterruptedException
*/
- boolean hasCommand(String scope, String name);
+ Command getCommand(String scope, String name);
/**
* Register a delayed service (or factory).
http://git-wip-us.apache.org/repos/asf/karaf/blob/5337778a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
index dad5ab2..8f45115 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/AggregateServiceTracker.java
@@ -144,7 +144,7 @@ public abstract class AggregateServiceTracker {
public List<String> getMissingServices() {
List<String> missing = new ArrayList<String>();
for (SingleServiceTracker tracker : singleTrackers.values()) {
- if (single.containsKey(tracker.getTrackedClass())) {
+ if (!single.containsKey(tracker.getTrackedClass())) {
missing.add(tracker.getTrackedClass().getName());
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/5337778a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
index 80f9d21..a2cf8a4 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/CommandExtension.java
@@ -132,7 +132,7 @@ public class CommandExtension implements Extension {
} else if (isSatisfied) {
action = "Registering";
} else {
- action = null;
+ return;
}
LOGGER.info("{} commands for bundle {}/{}",
action,
http://git-wip-us.apache.org/repos/asf/karaf/blob/5337778a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/RegistryImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/RegistryImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/RegistryImpl.java
index e1cdd67..ad1a1e5 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/RegistryImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/osgi/RegistryImpl.java
@@ -44,14 +44,20 @@ public class RegistryImpl implements Registry {
}
@Override
- public boolean hasCommand(String scope, String name) {
- if (parent != null && parent.hasCommand(scope, name)) {
- return true;
+ public Command getCommand(String scope, String name) {
+ if (parent != null) {
+ Command command = parent.getCommand(scope, name);
+ if (command != null) {
+ return command;
+ }
}
synchronized (services) {
List<Command> cmds = commands.get(scope + ":" + name);
- return cmds != null && !cmds.isEmpty();
+ if (cmds != null && !cmds.isEmpty()) {
+ return cmds.get(0);
+ }
}
+ return null;
}
@Override
http://git-wip-us.apache.org/repos/asf/karaf/blob/5337778a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
index 99113b7..0d39a95 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/RegistryImpl.java
@@ -44,14 +44,20 @@ public class RegistryImpl implements Registry {
}
@Override
- public boolean hasCommand(String scope, String name) {
- if (parent != null && parent.hasCommand(scope, name)) {
- return true;
+ public Command getCommand(String scope, String name) {
+ if (parent != null) {
+ Command command = parent.getCommand(scope, name);
+ if (command != null) {
+ return command;
+ }
}
synchronized (services) {
List<Command> cmds = commands.get(scope + ":" + name);
- return cmds != null && !cmds.isEmpty();
+ if (cmds != null && !cmds.isEmpty()) {
+ return cmds.get(0);
+ }
}
+ return null;
}
@Override
[07/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/Overrides.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Overrides.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Overrides.java
new file mode 100644
index 0000000..233a8a2
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Overrides.java
@@ -0,0 +1,132 @@
+/*
+ * 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.service;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Parser;
+import org.apache.felix.utils.version.VersionRange;
+import org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.felix.resolver.Util.getSymbolicName;
+import static org.apache.felix.resolver.Util.getVersion;
+
+/**
+ * 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";
+
+ /**
+ * 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 resources to resolve will be scanned and for each bundle,
+ * if a bundle override matches that resource, 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
+ * resource to be resolved, 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 resource to be resolved is within the given range.
+ *
+ * @param resources the list of resources to resolve
+ * @param overrides list of bundle overrides
+ */
+ public static void override(Map<String, Resource> resources, Collection<String> overrides) {
+ // Do override replacement
+ for (Clause override : Parser.parseClauses(overrides.toArray(new String[overrides.size()]))) {
+ String url = override.getName();
+ String vr = override.getAttribute(OVERRIDE_RANGE);
+ Resource over = resources.get(url);
+ if (over == null) {
+ // Ignore invalid overrides
+ continue;
+ }
+ for (String uri : new ArrayList<String>(resources.keySet())) {
+ Resource res = resources.get(uri);
+ if (getSymbolicName(res).equals(getSymbolicName(over))) {
+ VersionRange range;
+ if (vr == null) {
+ // default to micro version compatibility
+ Version v1 = getVersion(res);
+ Version v2 = new Version(v1.getMajor(), v1.getMinor() + 1, 0);
+ range = new VersionRange(false, v1, v2, true);
+ } else {
+ range = VersionRange.parseVersionRange(vr);
+ }
+ // 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(getVersion(over)) && getVersion(res).compareTo(getVersion(over)) < 0) {
+ resources.put(uri, over);
+ }
+ }
+ }
+ }
+ }
+
+ public static Set<String> loadOverrides(String overridesUrl) {
+ Set<String> overrides = new HashSet<String>();
+ 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("#")) {
+ overrides.add(line);
+ }
+ }
+ } finally {
+ is.close();
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.debug("Unable to load overrides bundles list", e);
+ }
+ return overrides;
+ }
+
+ public static String extractUrl(String override) {
+ Clause[] cs = Parser.parseClauses(new String[] { override });
+ if (cs.length != 1) {
+ throw new IllegalStateException("Override contains more than one clause: " + override);
+ }
+ 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/service/RepositoryImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java
new file mode 100644
index 0000000..4bf1502
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/RepositoryImpl.java
@@ -0,0 +1,103 @@
+/*
+ * 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.service;
+
+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 final URI uri;
+ private Features features;
+
+ public RepositoryImpl(URI uri) {
+ this.uri = uri;
+ }
+
+ public URI getURI() {
+ return uri;
+ }
+
+ public String getName() {
+ // TODO: catching this exception is ugly
+ try {
+ load();
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to load repository", e);
+ }
+ return features.getName();
+ }
+
+ 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[features.getFeature().size()]);
+ }
+
+
+ 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();
+ }
+ } catch (IllegalArgumentException e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ } catch (Exception e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ }
+ }
+ }
+
+ @Override
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java
new file mode 100644
index 0000000..e9ecece
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/RequirementSort.java
@@ -0,0 +1,107 @@
+/*
+ * 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.service;
+
+
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class RequirementSort {
+
+ /**
+ * Sorts {@link Resource} based on their {@link Requirement}s and {@link Capability}s.
+ * @param resources
+ * @return
+ */
+ public static <T extends Resource> Collection<T> sort(Collection<T> resources) {
+ Set<T> sorted = new LinkedHashSet<T>();
+ Set<T> visited = new LinkedHashSet<T>();
+ for (T r : resources) {
+ visit(r, resources, visited, sorted);
+ }
+ return sorted;
+ }
+
+
+ private static <T extends Resource> void visit(T resource, Collection<T> resources, Set<T> visited, Set<T> sorted) {
+ if (visited.contains(resource)) {
+ return;
+ }
+ visited.add(resource);
+ for (T r : collectDependencies(resource, resources)) {
+ visit(r, resources, visited, sorted);
+ }
+ sorted.add(resource);
+ }
+
+ /**
+ * Finds the dependencies of the current resource.
+ * @param resource
+ * @param allResources
+ * @return
+ */
+ private static <T extends Resource> Set<T> collectDependencies(T resource, Collection<T> allResources) {
+ Set<T> result = new LinkedHashSet<T>();
+ List<Requirement> requirements = resource.getRequirements(null);
+ for (Requirement requirement : requirements) {
+ boolean isSatisfied = false;
+ for (Resource r : result) {
+ for (Capability capability : r.getCapabilities(null)) {
+ if (isSatisfied(requirement, capability)) {
+ isSatisfied = true;
+ break;
+ }
+ }
+ }
+
+ for (T r : allResources) {
+ if (!isSatisfied) {
+ for (Capability capability : r.getCapabilities(null)) {
+ if (isSatisfied(requirement, capability)) {
+ result.add(r);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static boolean isSatisfied(Requirement requirement, Capability capability) {
+ RequirementImpl br;
+ if (requirement instanceof RequirementImpl) {
+ br = (RequirementImpl) requirement;
+ } else {
+ String filter = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+ SimpleFilter sf = (filter != null)
+ ? SimpleFilter.parse(filter)
+ : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ br = new RequirementImpl(null, requirement.getNamespace(), requirement.getDirectives(), requirement.getAttributes(), sf);
+ }
+ return br.matches(capability);
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java
new file mode 100644
index 0000000..d1f16b9
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/SimpleDownloader.java
@@ -0,0 +1,51 @@
+/*
+ * 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.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.karaf.features.internal.deployment.Downloader;
+import org.apache.karaf.features.internal.deployment.StreamProvider;
+import org.apache.karaf.features.internal.util.MultiException;
+
+public class SimpleDownloader implements Downloader {
+
+ private final MultiException exception = new MultiException("Error");
+
+ @Override
+ public void await() throws InterruptedException, MultiException {
+ exception.throwIfExceptions();
+ }
+
+ @Override
+ public void download(final String location, final DownloadCallback downloadCallback) throws MalformedURLException {
+ final URL url = new URL(location);
+ try {
+ downloadCallback.downloaded(new StreamProvider() {
+ @Override
+ public InputStream open() throws IOException {
+ return url.openStream();
+ }
+ });
+ } catch (Exception e) {
+ exception.addException(e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java
new file mode 100644
index 0000000..c84f4e0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java
@@ -0,0 +1,34 @@
+/*
+ * 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.service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class State {
+
+ public final AtomicBoolean bootDone = new AtomicBoolean();
+ public final Set<String> repositories = new TreeSet<String>();
+ public final Set<String> features = new TreeSet<String>();
+ public final Set<String> installedFeatures = new TreeSet<String>();
+ public final Set<Long> managedBundles = new TreeSet<Long>();
+ public final Map<String, Long> bundleChecksums = new HashMap<String, Long>();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java
new file mode 100644
index 0000000..ac54ab0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java
@@ -0,0 +1,175 @@
+/*
+ * 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.service;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.karaf.features.Feature;
+
+public abstract class StateStorage {
+
+ public void load(State state) throws IOException {
+ state.repositories.clear();
+ state.features.clear();
+ state.installedFeatures.clear();
+ state.managedBundles.clear();
+ InputStream is = getInputStream();
+ if (is != null) {
+ try {
+ Properties props = new Properties();
+ props.load(is);
+ state.bootDone.set(loadBool(props, "bootDone"));
+ state.repositories.addAll(loadSet(props, "repositories."));
+ state.features.addAll(loadSet(props, "features."));
+ state.installedFeatures.addAll(loadSet(props, "installed."));
+ state.managedBundles.addAll(toLongSet(loadSet(props, "managed.")));
+ state.bundleChecksums.putAll(toStringLongMap(loadMap(props, "checksums.")));
+ } finally {
+ close(is);
+ }
+ }
+ }
+
+ public void save(State state) throws IOException {
+ OutputStream os = getOutputStream();
+ if (os != null) {
+ try {
+ Properties props = new Properties();
+ saveBool(props, "bootDone", state.bootDone.get());
+ saveSet(props, "repositories.", state.repositories);
+ saveSet(props, "features.", state.features);
+ saveSet(props, "installed.", state.installedFeatures);
+ saveSet(props, "managed.", toStringSet(state.managedBundles));
+ saveMap(props, "checksums.", toStringStringMap(state.bundleChecksums));
+ props.store(os, "FeaturesService State");
+ } finally {
+ close(os);
+ }
+ }
+ }
+
+ protected abstract InputStream getInputStream() throws IOException;
+ protected abstract OutputStream getOutputStream() throws IOException;
+
+ protected boolean loadBool(Properties props, String key) {
+ return Boolean.parseBoolean(props.getProperty(key));
+ }
+
+ protected void saveBool(Properties props, String key, boolean val) {
+ props.setProperty(key, Boolean.toString(val));
+ }
+
+ protected Set<String> toStringSet(Set<Long> set) {
+ Set<String> ns = new TreeSet<String>();
+ for (long l : set) {
+ ns.add(Long.toString(l));
+ }
+ return ns;
+ }
+
+ protected Set<Long> toLongSet(Set<String> set) {
+ Set<Long> ns = new TreeSet<Long>();
+ for (String s : set) {
+ ns.add(Long.parseLong(s));
+ }
+ return ns;
+ }
+
+ protected void saveSet(Properties props, String prefix, Set<String> set) {
+ List<String> l = new ArrayList<String>(set);
+ props.put(prefix + "count", Integer.toString(l.size()));
+ for (int i = 0; i < l.size(); i++) {
+ props.put(prefix + "item." + i, l.get(i));
+ }
+ }
+
+ protected Set<String> loadSet(Properties props, String prefix) {
+ Set<String> l = new HashSet<String>();
+ String countStr = (String) props.get(prefix + "count");
+ if (countStr != null) {
+ int count = Integer.parseInt(countStr);
+ for (int i = 0; i < count; i++) {
+ l.add((String) props.get(prefix + "item." + i));
+ }
+ }
+ return l;
+ }
+
+ protected Map<String, String> toStringStringMap(Map<String, Long> map) {
+ Map<String, String> nm = new HashMap<String, String>();
+ for (Map.Entry<String, Long> entry : map.entrySet()) {
+ nm.put(entry.getKey(), Long.toString(entry.getValue()));
+ }
+ return nm;
+ }
+
+ protected Map<String, Long> toStringLongMap(Map<String, String> map) {
+ Map<String, Long> nm = new HashMap<String, Long>();
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ nm.put(entry.getKey(), Long.parseLong(entry.getValue()));
+ }
+ return nm;
+ }
+
+
+ protected void saveMap(Properties props, String prefix, Map<String, String> map) {
+ List<Map.Entry<String, String>> l = new ArrayList<Map.Entry<String, String>>(map.entrySet());
+ props.put(prefix + "count", Integer.toString(l.size()));
+ for (int i = 0; i < l.size(); i++) {
+ props.put(prefix + "key." + i, l.get(i).getKey());
+ props.put(prefix + "val." + i, l.get(i).getValue());
+ }
+ }
+
+ protected Map<String, String> loadMap(Properties props, String prefix) {
+ Map<String, String> l = new HashMap<String, String>();
+ String countStr = (String) props.get(prefix + "count");
+ if (countStr != null) {
+ int count = Integer.parseInt(countStr);
+ for (int i = 0; i < count; i++) {
+ String key = (String) props.get(prefix + "key." + i);
+ String val = (String) props.get(prefix + "val." + i);
+ l.put(key, val);
+ }
+ }
+ return l;
+ }
+
+
+ protected void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java
new file mode 100644
index 0000000..19fc706
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/ChecksumUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.CRC32;
+
+public class ChecksumUtils {
+
+ private ChecksumUtils() {
+ }
+
+ /**
+ * Compute a cheksum for the file or directory that consists of the name, length and the last modified date
+ * for a file and its children in case of a directory
+ *
+ * @param is the input stream
+ * @return a checksum identifying any change
+ */
+ public static long checksum(InputStream is) throws IOException
+ {
+ try {
+ CRC32 crc = new CRC32();
+ byte[] buffer = new byte[8192];
+ int l;
+ while ((l = is.read(buffer)) > 0) {
+ crc.update(buffer, 0, l);
+ }
+ return crc.getValue();
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java
new file mode 100644
index 0000000..35b7308
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonReader.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * Copyright (c) 2013 EclipseSource.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Ralf Sternberg - initial implementation and API
+ ******************************************************************************/
+package org.apache.karaf.features.internal.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ */
+public class JsonReader {
+
+ public static Object read(Reader reader) throws IOException {
+ return new JsonReader(reader).parse();
+ }
+
+ public static Object read(InputStream is) throws IOException {
+ return new JsonReader(new InputStreamReader(is)).parse();
+ }
+
+ //
+ // Implementation
+ //
+
+ private final Reader reader;
+ private final StringBuilder recorder;
+ private int current;
+ private int line = 1;
+ private int column = 0;
+
+ JsonReader(Reader reader) {
+ this.reader = reader;
+ recorder = new StringBuilder();
+ }
+
+ public Object parse() throws IOException {
+ read();
+ skipWhiteSpace();
+ Object result = readValue();
+ skipWhiteSpace();
+ if (!endOfText()) {
+ throw error("Unexpected character");
+ }
+ return result;
+ }
+
+ private Object readValue() throws IOException {
+ switch (current) {
+ case 'n':
+ return readNull();
+ case 't':
+ return readTrue();
+ case 'f':
+ return readFalse();
+ case '"':
+ return readString();
+ case '[':
+ return readArray();
+ case '{':
+ return readObject();
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return readNumber();
+ default:
+ throw expected("value");
+ }
+ }
+
+ private Collection<?> readArray() throws IOException {
+ read();
+ Collection<Object> array = new ArrayList<Object>();
+ skipWhiteSpace();
+ if (readChar(']')) {
+ return array;
+ }
+ do {
+ skipWhiteSpace();
+ array.add(readValue());
+ skipWhiteSpace();
+ } while (readChar(','));
+ if (!readChar(']')) {
+ throw expected("',' or ']'");
+ }
+ return array;
+ }
+
+ private Map<String, Object> readObject() throws IOException {
+ read();
+ Map<String, Object> object = new HashMap<String, Object>();
+ skipWhiteSpace();
+ if (readChar('}')) {
+ return object;
+ }
+ do {
+ skipWhiteSpace();
+ String name = readName();
+ skipWhiteSpace();
+ if (!readChar(':')) {
+ throw expected("':'");
+ }
+ skipWhiteSpace();
+ object.put(name, readValue());
+ skipWhiteSpace();
+ } while (readChar(','));
+ if (!readChar('}')) {
+ throw expected("',' or '}'");
+ }
+ return object;
+ }
+
+ private Object readNull() throws IOException {
+ read();
+ readRequiredChar('u');
+ readRequiredChar('l');
+ readRequiredChar('l');
+ return null;
+ }
+
+ private Boolean readTrue() throws IOException {
+ read();
+ readRequiredChar('r');
+ readRequiredChar('u');
+ readRequiredChar('e');
+ return Boolean.TRUE;
+ }
+
+ private Boolean readFalse() throws IOException {
+ read();
+ readRequiredChar('a');
+ readRequiredChar('l');
+ readRequiredChar('s');
+ readRequiredChar('e');
+ return Boolean.FALSE;
+ }
+
+ private void readRequiredChar(char ch) throws IOException {
+ if (!readChar(ch)) {
+ throw expected("'" + ch + "'");
+ }
+ }
+
+ private String readString() throws IOException {
+ read();
+ recorder.setLength(0);
+ while (current != '"') {
+ if (current == '\\') {
+ readEscape();
+ } else if (current < 0x20) {
+ throw expected("valid string character");
+ } else {
+ recorder.append((char) current);
+ read();
+ }
+ }
+ read();
+ return recorder.toString();
+ }
+
+ private void readEscape() throws IOException {
+ read();
+ switch (current) {
+ case '"':
+ case '/':
+ case '\\':
+ recorder.append((char) current);
+ break;
+ case 'b':
+ recorder.append('\b');
+ break;
+ case 'f':
+ recorder.append('\f');
+ break;
+ case 'n':
+ recorder.append('\n');
+ break;
+ case 'r':
+ recorder.append('\r');
+ break;
+ case 't':
+ recorder.append('\t');
+ break;
+ case 'u':
+ char[] hexChars = new char[4];
+ for (int i = 0; i < 4; i++) {
+ read();
+ if (!isHexDigit(current)) {
+ throw expected("hexadecimal digit");
+ }
+ hexChars[i] = (char) current;
+ }
+ recorder.append((char) Integer.parseInt(String.valueOf(hexChars), 16));
+ break;
+ default:
+ throw expected("valid escape sequence");
+ }
+ read();
+ }
+
+ private Number readNumber() throws IOException {
+ recorder.setLength(0);
+ readAndAppendChar('-');
+ int firstDigit = current;
+ if (!readAndAppendDigit()) {
+ throw expected("digit");
+ }
+ if (firstDigit != '0') {
+ while (readAndAppendDigit()) {
+ }
+ }
+ readFraction();
+ readExponent();
+ return Double.parseDouble(recorder.toString());
+ }
+
+ private boolean readFraction() throws IOException {
+ if (!readAndAppendChar('.')) {
+ return false;
+ }
+ if (!readAndAppendDigit()) {
+ throw expected("digit");
+ }
+ while (readAndAppendDigit()) {
+ }
+ return true;
+ }
+
+ private boolean readExponent() throws IOException {
+ if (!readAndAppendChar('e') && !readAndAppendChar('E')) {
+ return false;
+ }
+ if (!readAndAppendChar('+')) {
+ readAndAppendChar('-');
+ }
+ if (!readAndAppendDigit()) {
+ throw expected("digit");
+ }
+ while (readAndAppendDigit()) {
+ }
+ return true;
+ }
+
+ private String readName() throws IOException {
+ if (current != '"') {
+ throw expected("name");
+ }
+ readString();
+ return recorder.toString();
+ }
+
+ private boolean readAndAppendChar(char ch) throws IOException {
+ if (current != ch) {
+ return false;
+ }
+ recorder.append(ch);
+ read();
+ return true;
+ }
+
+ private boolean readChar(char ch) throws IOException {
+ if (current != ch) {
+ return false;
+ }
+ read();
+ return true;
+ }
+
+ private boolean readAndAppendDigit() throws IOException {
+ if (!isDigit(current)) {
+ return false;
+ }
+ recorder.append((char) current);
+ read();
+ return true;
+ }
+
+ private void skipWhiteSpace() throws IOException {
+ while (isWhiteSpace(current) && !endOfText()) {
+ read();
+ }
+ }
+
+ private void read() throws IOException {
+ if (endOfText()) {
+ throw error("Unexpected end of input");
+ }
+ column++;
+ if (current == '\n') {
+ line++;
+ column = 0;
+ }
+ current = reader.read();
+ }
+
+ private boolean endOfText() {
+ return current == -1;
+ }
+
+ private IOException expected(String expected) {
+ if (endOfText()) {
+ return error("Unexpected end of input");
+ }
+ return error("Expected " + expected);
+ }
+
+ private IOException error(String message) {
+ return new IOException(message + " at " + line + ":" + column);
+ }
+
+ private static boolean isWhiteSpace(int ch) {
+ return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
+ }
+
+ private static boolean isDigit(int ch) {
+ return ch >= '0' && ch <= '9';
+ }
+
+ private static boolean isHexDigit(int ch) {
+ return ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F';
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
new file mode 100644
index 0000000..8294faa
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (C) FuseSource, Inc.
+ * http://fusesource.com
+ *
+ * Licensed 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.util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ */
+public class JsonWriter {
+
+ public static void write(Writer writer, Object value) throws IOException {
+ if (value instanceof Map) {
+ writeObject(writer, (Map) value);
+ } else if (value instanceof Collection) {
+ writeArray(writer, (Collection) value);
+ } else if (value instanceof Number) {
+ writeNumber(writer, (Number) value);
+ } else if (value instanceof String) {
+ writeString(writer, (String) value);
+ } else if (value instanceof Boolean) {
+ writeBoolean(writer, (Boolean) value);
+ } else if (value == null) {
+ writeNull(writer);
+ } else {
+ throw new IllegalArgumentException("Unsupported value: " + value);
+ }
+ }
+
+ private static void writeObject(Writer writer, Map<?, ?> value) throws IOException {
+ writer.append('{');
+ boolean first = true;
+ for (Map.Entry entry : value.entrySet()) {
+ if (!first) {
+ writer.append(',');
+ } else {
+ first = false;
+ }
+ writeString(writer, (String) entry.getKey());
+ writer.append(':');
+ write(writer, entry.getValue());
+ }
+ writer.append('}');
+ }
+
+ private static void writeString(Writer writer, String value) throws IOException {
+ writer.append('"');
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ writer.append('\\');
+ writer.append(c);
+ break;
+ default:
+ if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+ String s = Integer.toHexString(c);
+ writer.append('\\');
+ writer.append('u');
+ for (int j = s.length(); j < 4; j++) {
+ writer.append('0');
+ }
+ writer.append(s);
+ } else {
+ writer.append(c);
+ }
+ break;
+ }
+ }
+ writer.append('"');
+ }
+
+ private static void writeNumber(Writer writer, Number value) throws IOException {
+ writer.append(value.toString());
+ }
+
+ private static void writeBoolean(Writer writer, Boolean value) throws IOException {
+ writer.append(Boolean.toString(value));
+ }
+
+ private static void writeArray(Writer writer, Collection<?> value) throws IOException {
+ writer.append('[');
+ boolean first = true;
+ for (Object obj : value) {
+ if (!first) {
+ writer.append(',');
+ } else {
+ first = false;
+ }
+ write(writer, obj);
+ }
+ writer.append(']');
+ }
+
+ private static void writeNull(Writer writer) throws IOException {
+ writer.append("null");
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java
new file mode 100644
index 0000000..d30b7b5
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/Macro.java
@@ -0,0 +1,142 @@
+/*
+ * 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.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.utils.version.VersionTable;
+import org.osgi.framework.Version;
+
+public class Macro {
+
+ public static String transform(String macro, String value) {
+ if (macro.startsWith("${") && macro.endsWith("}")) {
+ String[] args = macro.substring(2, macro.length() - 1).split(";");
+ if ("version".equals(args[0])) {
+ if (args.length != 2) {
+ throw new IllegalArgumentException("Invalid syntax for macro: " + macro);
+ }
+ return version(args[1], VersionTable.getVersion(value));
+ } else if ("range".equals(args[0])) {
+ if (args.length != 2) {
+ throw new IllegalArgumentException("Invalid syntax for macro: " + macro);
+ }
+ return range(args[1], VersionTable.getVersion(value));
+ } else {
+ throw new IllegalArgumentException("Unknown macro: " + macro);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Modify a version to set a version policy. Thed policy is a mask that is
+ * mapped to a version.
+ *
+ * <pre>
+ * + increment
+ * - decrement
+ * = maintain
+ * ˜ discard
+ *
+ * ==+ = maintain major, minor, increment micro, discard qualifier
+ * ˜˜˜= = just get the qualifier
+ * version="[${version;==;${@}},${version;=+;${@}})"
+ * </pre>
+ *
+ * @param args
+ * @return
+ */
+ final static String MASK_STRING = "[\\-+=~0123456789]{0,3}[=~]?";
+
+ static String version(String mask, Version version) {
+ StringBuilder sb = new StringBuilder();
+ String del = "";
+
+ for (int i = 0; i < mask.length(); i++) {
+ char c = mask.charAt(i);
+ String result = null;
+ if (c != '~') {
+ if (i > 3) {
+ throw new IllegalArgumentException("Version mask can only specify 3 digits");
+ } else if (i == 3) {
+ result = version.getQualifier();
+ if (result.isEmpty()) {
+ result = null;
+ }
+ } else if (Character.isDigit(c)) {
+ // Handle masks like +00, =+0
+ result = String.valueOf(c);
+ } else {
+ int x = 0;
+ switch (i) {
+ case 0: x = version.getMajor(); break;
+ case 1: x = version.getMinor(); break;
+ case 2: x = version.getMicro(); break;
+ }
+ switch (c) {
+ case '+' :
+ x++;
+ break;
+ case '-' :
+ x--;
+ break;
+ case '=' :
+ break;
+ }
+ result = Integer.toString(x);
+ }
+ if (result != null) {
+ sb.append(del);
+ del = ".";
+ sb.append(result);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Schortcut for version policy
+ *
+ * <pre>
+ * -provide-policy : ${policy;[==,=+)}
+ * -consume-policy : ${policy;[==,+)}
+ * </pre>
+ *
+ * @param args
+ * @return
+ */
+
+ static Pattern RANGE_MASK = Pattern.compile("(\\[|\\()(" + MASK_STRING + "),(" + MASK_STRING + ")(\\]|\\))");
+
+ static String range(String spec, Version version) {
+ Matcher m = RANGE_MASK.matcher(spec);
+ m.matches();
+ String floor = m.group(1);
+ String floorMask = m.group(2);
+ String ceilingMask = m.group(3);
+ String ceiling = m.group(4);
+
+ String left = version(floorMask, version);
+ String right = version(ceilingMask, version);
+
+ return floor + left + "," + right + ceiling;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java
new file mode 100644
index 0000000..36af452
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/MultiException.java
@@ -0,0 +1,95 @@
+/*
+ * 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.util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class MultiException extends Exception {
+
+ private List<Exception> exceptions = new ArrayList<Exception>();
+
+ public MultiException(String message) {
+ super(message);
+ }
+
+ public MultiException(String message, List<Exception> exceptions) {
+ super(message);
+ this.exceptions = exceptions;
+ }
+
+ public void addException(Exception e) {
+ exceptions.add(e);
+ }
+
+ public void throwIfExceptions() throws MultiException {
+ if (!exceptions.isEmpty()) {
+ throw this;
+ }
+ }
+
+ public Throwable[] getCauses() {
+ return exceptions.toArray(new Throwable[exceptions.size()]);
+ }
+
+ @Override
+ public void printStackTrace()
+ {
+ super.printStackTrace();
+ for (Exception e : exceptions) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /* ------------------------------------------------------------------------------- */
+ /**
+ * @see Throwable#printStackTrace(java.io.PrintStream)
+ */
+ @Override
+ public void printStackTrace(PrintStream out)
+ {
+ super.printStackTrace(out);
+ for (Exception e : exceptions) {
+ e.printStackTrace(out);
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintWriter out)
+ {
+ super.printStackTrace(out);
+ for (Exception e : exceptions) {
+ e.printStackTrace(out);
+ }
+ }
+
+ public static void throwIf(String message, List<Exception> exceptions) throws MultiException {
+ if (exceptions != null && !exceptions.isEmpty()) {
+ StringBuilder sb = new StringBuilder(message);
+ sb.append(":");
+ for (Exception e : exceptions) {
+ sb.append("\n\t");
+ sb.append(e.getMessage());
+ }
+ throw new MultiException(sb.toString(), exceptions);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/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 da4a36e..284f465 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
@@ -133,6 +133,6 @@ public interface FeaturesServiceMBean {
* The item names in the CompositeData representing the event raised for
* feature events within the OSGi container by this bean
*/
- String[] REPOSITORY_EVENT = { REPOSITORY_NAME, REPOSITORY_URI, REPOSITORY_EVENT_EVENT_TYPE };
+ String[] REPOSITORY_EVENT = { REPOSITORY_URI, REPOSITORY_EVENT_EVENT_TYPE };
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
index bf7910a..e00e85d 100644
--- a/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
+++ b/features/core/src/main/java/org/apache/karaf/features/management/codec/JmxRepositoryEvent.java
@@ -36,11 +36,10 @@ public class JmxRepositoryEvent {
try {
String[] itemNames = FeaturesServiceMBean.REPOSITORY_EVENT;
Object[] itemValues = new Object[itemNames.length];
- itemValues[0] = event.getRepository().getName();
- itemValues[1] = event.getRepository().getURI().toString();
+ itemValues[0] = event.getRepository().getURI().toString();
switch (event.getType()) {
- case RepositoryAdded: itemValues[2] = FeaturesServiceMBean.REPOSITORY_EVENT_EVENT_TYPE_ADDED; break;
- case RepositoryRemoved: itemValues[2] = FeaturesServiceMBean.REPOSITORY_EVENT_EVENT_TYPE_REMOVED; break;
+ case RepositoryAdded: itemValues[1] = FeaturesServiceMBean.REPOSITORY_EVENT_EVENT_TYPE_ADDED; break;
+ case RepositoryRemoved: itemValues[1] = FeaturesServiceMBean.REPOSITORY_EVENT_EVENT_TYPE_REMOVED; break;
default: throw new IllegalStateException("Unsupported event type: " + event.getType());
}
data = new CompositeDataSupport(REPOSITORY_EVENT, itemNames, itemValues);
@@ -65,11 +64,9 @@ public class JmxRepositoryEvent {
String[] itemDescriptions = new String[itemNames.length];
itemTypes[0] = SimpleType.STRING;
itemTypes[1] = SimpleType.STRING;
- itemTypes[2] = SimpleType.STRING;
- itemDescriptions[0] = "The name of the repository";
- itemDescriptions[1] = "The uri of the repository";
- itemDescriptions[2] = "The type of event";
+ itemDescriptions[0] = "The uri of the repository";
+ itemDescriptions[1] = "The type of event";
return new CompositeType("RepositoryEvent", description, itemNames,
itemDescriptions, itemTypes);
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/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
deleted file mode 100644
index 700e835..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/management/internal/FeaturesServiceMBeanImpl.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Licensed 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.management.internal;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.management.MBeanNotificationInfo;
-import javax.management.MBeanRegistration;
-import javax.management.MBeanServer;
-import javax.management.NotCompliantMBeanException;
-import javax.management.Notification;
-import javax.management.ObjectName;
-import javax.management.openmbean.TabularData;
-
-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;
-import org.apache.karaf.features.management.codec.JmxRepository;
-import org.apache.karaf.features.management.codec.JmxRepositoryEvent;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-
-/**
- * Implementation of {@link FeaturesServiceMBean}.
- */
-public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
- MBeanRegistration, FeaturesServiceMBean {
-
- private ServiceRegistration<FeaturesListener> registration;
-
- private BundleContext bundleContext;
-
- private ObjectName objectName;
-
- private volatile long sequenceNumber = 0;
-
- private org.apache.karaf.features.FeaturesService featuresService;
-
- public FeaturesServiceMBeanImpl() throws NotCompliantMBeanException {
- super(FeaturesServiceMBean.class);
- }
-
- public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
- objectName = name;
- return name;
- }
-
- public void postRegister(Boolean registrationDone) {
- registration = bundleContext.registerService(FeaturesListener.class,
- getFeaturesListener(), new Hashtable<String, String>());
- }
-
- public void preDeregister() throws Exception {
- registration.unregister();
- }
-
- public void postDeregister() {
- }
-
- /**
- * {@inheritDoc}
- */
- public TabularData getFeatures() throws Exception {
- try {
- List<Feature> allFeatures = Arrays.asList(featuresService.listFeatures());
- List<Feature> insFeatures = Arrays.asList(featuresService.listInstalledFeatures());
- ArrayList<JmxFeature> features = new ArrayList<JmxFeature>();
- for (Feature feature : allFeatures) {
- try {
- features.add(new JmxFeature(feature, insFeatures.contains(feature)));
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- TabularData table = JmxFeature.tableFrom(features);
- return table;
- } catch (Throwable t) {
- t.printStackTrace();
- return null;
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public TabularData getRepositories() throws Exception {
- try {
- List<Repository> allRepositories = Arrays.asList(featuresService.listRepositories());
- ArrayList<JmxRepository> repositories = new ArrayList<JmxRepository>();
- for (Repository repository : allRepositories) {
- try {
- repositories.add(new JmxRepository(repository));
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- TabularData table = JmxRepository.tableFrom(repositories);
- return table;
- } catch (Throwable t) {
- t.printStackTrace();
- return null;
- }
- }
-
- public void addRepository(String uri) throws Exception {
- featuresService.addRepository(new URI(uri));
- }
-
- public void addRepository(String uri, boolean install) throws Exception {
- featuresService.addRepository(new URI(uri), install);
- }
-
- public void removeRepository(String uri) throws Exception {
- featuresService.removeRepository(new URI(uri));
- }
-
- public void removeRepository(String uri, boolean uninstall) throws Exception {
- featuresService.removeRepository(new URI(uri), uninstall);
- }
-
- public void installFeature(String name) throws Exception {
- featuresService.installFeature(name);
- }
-
- public void installFeature(String name, boolean noClean, boolean noRefresh) throws Exception {
- EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
- if (noClean) {
- options.add(org.apache.karaf.features.FeaturesService.Option.NoCleanIfFailure);
- }
- if (noRefresh) {
- options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
- }
- featuresService.installFeature(name, options);
- }
-
- public void installFeature(String name, String version) throws Exception {
- featuresService.installFeature(name, version);
- }
-
- public void installFeature(String name, String version, boolean noClean, boolean noRefresh) throws Exception {
- EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
- if (noClean) {
- options.add(org.apache.karaf.features.FeaturesService.Option.NoCleanIfFailure);
- }
- if (noRefresh) {
- options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
- }
- featuresService.installFeature(name, version, options);
- }
-
- public TabularData infoFeature(String name) throws Exception {
- try {
- Feature feature = featuresService.getFeature(name);
- return infoFeature(feature);
- } catch (Throwable t) {
- t.printStackTrace();
- return null;
- }
- }
-
- public TabularData infoFeature(String name, String version) throws Exception {
- try {
- Feature feature = featuresService.getFeature(name, version);
- return infoFeature(feature);
- } catch (Throwable t) {
- t.printStackTrace();
- return null;
- }
- }
-
- private TabularData infoFeature(Feature feature) throws Exception {
- JmxFeature jmxFeature = null;
- if (featuresService.isInstalled(feature)) {
- jmxFeature = new JmxFeature(feature, true);
- } else {
- jmxFeature = new JmxFeature(feature, false);
- }
- ArrayList<JmxFeature> features = new ArrayList<JmxFeature>();
- features.add(jmxFeature);
- TabularData table = JmxFeature.tableFrom(features);
- return table;
- }
-
- public void uninstallFeature(String name) throws Exception {
- 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;
- }
-
- public void setFeaturesService(org.apache.karaf.features.FeaturesService featuresService) {
- this.featuresService = featuresService;
- }
-
- public FeaturesListener getFeaturesListener() {
- return new FeaturesListener() {
- public void featureEvent(FeatureEvent event) {
- if (!event.isReplay()) {
- Notification notification = new Notification(FEATURE_EVENT_TYPE, objectName, sequenceNumber++);
- notification.setUserData(new JmxFeatureEvent(event).asCompositeData());
- sendNotification(notification);
- }
- }
-
- public void repositoryEvent(RepositoryEvent event) {
- if (!event.isReplay()) {
- Notification notification = new Notification(REPOSITORY_EVENT_TYPE, objectName, sequenceNumber++);
- notification.setUserData(new JmxRepositoryEvent(event).asCompositeData());
- sendNotification(notification);
- }
- }
-
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- return o.equals(this);
- }
-
- };
- }
-
- public MBeanNotificationInfo[] getNotificationInfo() {
- return getBroadcastInfo();
- }
-
- private static MBeanNotificationInfo[] getBroadcastInfo() {
- String type = Notification.class.getCanonicalName();
- MBeanNotificationInfo info1 = new MBeanNotificationInfo(new String[]{FEATURE_EVENT_EVENT_TYPE},
- type, "Some features notification");
- MBeanNotificationInfo info2 = new MBeanNotificationInfo(new String[]{REPOSITORY_EVENT_EVENT_TYPE},
- type, "Some repository notification");
- return new MBeanNotificationInfo[]{info1, info2};
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/management/internal/StandardEmitterMBean.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/internal/StandardEmitterMBean.java b/features/core/src/main/java/org/apache/karaf/features/management/internal/StandardEmitterMBean.java
deleted file mode 100644
index 9777897..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/management/internal/StandardEmitterMBean.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Licensed 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.management.internal;
-
-import javax.management.*;
-
-public class StandardEmitterMBean extends StandardMBean implements NotificationEmitter {
-
- private final NotificationBroadcasterSupport emitter;
-
- @SuppressWarnings("rawtypes")
- public StandardEmitterMBean(Class mbeanInterface) throws NotCompliantMBeanException {
- super(mbeanInterface);
- this.emitter = new NotificationBroadcasterSupport() {
- @Override
- public MBeanNotificationInfo[] getNotificationInfo() {
- return StandardEmitterMBean.this.getNotificationInfo();
- }
- };
- }
-
- public void sendNotification(Notification notification) {
- emitter.sendNotification(notification);
- }
-
-
- public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
- emitter.removeNotificationListener(listener, filter, handback);
- }
-
- public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException {
- emitter.addNotificationListener(listener, filter, handback);
- }
-
- public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
- emitter.removeNotificationListener(listener);
- }
-
- public MBeanNotificationInfo[] getNotificationInfo() {
- return new MBeanNotificationInfo[0];
- }
-
- @Override
- public MBeanInfo getMBeanInfo() {
- MBeanInfo mbeanInfo = super.getMBeanInfo();
- if (mbeanInfo != null) {
- MBeanNotificationInfo[] notificationInfo = getNotificationInfo();
- mbeanInfo = new MBeanInfo(mbeanInfo.getClassName(), mbeanInfo.getDescription(), mbeanInfo.getAttributes(),
- mbeanInfo.getConstructors(), mbeanInfo.getOperations(), notificationInfo);
- }
- return mbeanInfo;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
----------------------------------------------------------------------
diff --git a/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
new file mode 100644
index 0000000..60ec8d2
--- /dev/null
+++ b/features/core/src/main/resources/org/apache/karaf/features/karaf-features-1.3.0.xsd
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+
+-->
+<xs:schema elementFormDefault="qualified"
+ targetNamespace="http://karaf.apache.org/xmlns/features/v1.3.0"
+ xmlns:tns="http://karaf.apache.org/xmlns/features/v1.3.0"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Karaf features mechanism. For documentation please visit the
+<a href="http://karaf.apache.org/">Karaf website</a>.
+ ]]></xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType name="features">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Root element of Feature definition. It contains an required attribute for
+designating from which repository this feature should be loaded. The Karaf
+shell will show the repository name when displaying information about the feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="repository" type="xs:anyURI">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Additional repositories where dependencies are stored.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="feature" type="tns:feature">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Feature definition.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ </xs:choice>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="feature">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Definition of the Feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="details" minOccurs="0" type="xs:string">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+The help text shown for this feature when using feature:info console command.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+ <xs:element name="config" type="tns:config" />
+ <xs:element name="configfile" type="tns:configFile" />
+ <xs:element name="feature" type="tns:dependency" />
+ <xs:element name="bundle" type="tns:bundle" />
+ <xs:element name="conditional" type="tns:conditional" />
+ <xs:element name="requirement" type="tns:requirement" />
+ <xs:element name="capability" type="tns:capability" />
+ </xs:choice>
+ <xs:attribute name="name" type="tns:featureName" use="required" />
+ <xs:attribute name="version" type="xs:string" default="0.0.0" />
+ <xs:attribute name="description" type="xs:string" />
+ <xs:attribute name="resolver" type="tns:resolver">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Optional alternative resolver to use for determining the list of bundles to install for a given feature.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="install" type="tns:install">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Marks if the feaute will be automatically started when thrown to the deploy folder.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="start-level" type="xs:int">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Set this attribute to have an OSGi start level for this feature different
+from the default start level defined in Karaf's config.properties.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+ <xs:complexType name="conditional">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Definition of the Conditional.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="config" type="tns:config" />
+ <xs:element name="configfile" type="tns:configFile" />
+ <xs:element name="feature" type="tns:dependency" />
+ <xs:element name="bundle" type="tns:bundle" />
+ <xs:element name="condition" type="tns:dependency" minOccurs="0" maxOccurs="1" />
+ </xs:choice>
+ </xs:complexType>
+
+
+ <xs:complexType name="bundle">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Deployable element to install.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:anyURI">
+ <xs:attribute name="start-level" type="xs:int">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Set this attribute to have an OSGi start level for this bundle different
+from the default start level defined in the Karaf's config.properties.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="start" type="xs:boolean" default="true">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+If false, leaves bundle in resolved state rather than the default active state.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="dependency" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Mark this bundle as a dependency for the resolver.
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="dependency">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Dependency of feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="tns:featureName">
+ <xs:attribute name="version" type="xs:string" default="0.0.0" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="config">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Configuration entries which should be created during feature installation. This
+configuration may be used with OSGi Configuration Admin. The element content is
+read in as a properties file.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" type="xs:string" use="required" />
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="configFile">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Additional configuration files which should be created during feature installation.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:anyURI">
+ <xs:attribute name="finalname" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+The final destination path and name for the configuration file.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="override" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+If the configFile already exists at the finalname location, whether or not to replace it.
+ ]]></xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="requirement">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Additional requirements of this feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:complexType name="capability">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Additional capability of this feature.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:simpleType name="featureName">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Feature name should be non empty string.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="resolver">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Resolver to use. Karaf will look for OSGi service which have following properties:
+objectClass: org.apache.karaf.features.Resolver
+name: the value
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:simpleType name="install">
+ <xs:annotation>
+ <xs:documentation><![CDATA[
+Installation mode. Can be either manual or auto. Specifies whether the feature should be automatically installed when
+dropped inside the deploy folder. Note: This attribute doesn't affect feature descriptors that are installed from the
+command line or as part of the org.apache.karaf.features.cfg.
+ ]]></xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1" />
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="features" type="tns:features" />
+
+</xs:schema>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java b/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java
index 21bd919..d5e7e46 100644
--- a/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/ConditionalTest.java
@@ -16,15 +16,14 @@
*/
package org.apache.karaf.features;
-import java.net.URI;
import junit.framework.TestCase;
-import org.apache.karaf.features.internal.RepositoryImpl;
+import org.apache.karaf.features.internal.service.RepositoryImpl;
public class ConditionalTest extends TestCase {
public void testLoad() throws Exception {
- RepositoryImpl r = new RepositoryImpl(getClass().getResource("internal/f06.xml").toURI());
+ RepositoryImpl r = new RepositoryImpl(getClass().getResource("internal/service/f06.xml").toURI());
// Check repo
Feature[] features = r.getFeatures();
assertNotNull(features);
[03/13] git commit: Slightly more informative error reporting when
resolution fails in the karaf-maven-plugin
Posted by gn...@apache.org.
Slightly more informative error reporting when resolution fails in the karaf-maven-plugin
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/9605df33
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/9605df33
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/9605df33
Branch: refs/heads/master
Commit: 9605df3365d9d1f6309a7e3140aaaeb582fde4f0
Parents: 0d87c62
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Thu Apr 3 09:53:23 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon Apr 7 10:52:34 2014 +0200
----------------------------------------------------------------------
.../karaf/tooling/features/AbstractFeatureMojo.java | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/9605df33/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/AbstractFeatureMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/AbstractFeatureMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/AbstractFeatureMojo.java
index 76e6492..1da45f9 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/AbstractFeatureMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/AbstractFeatureMojo.java
@@ -239,8 +239,12 @@ public abstract class AbstractFeatureMojo extends MojoSupport {
getLog().info("Base repo: " + localRepo.getUrl());
for (Feature feature : featuresSet) {
- resolveArtifacts(feature.getBundles());
- resolveArtifacts(feature.getConfigFiles());
+ try {
+ resolveArtifacts(feature.getBundles());
+ resolveArtifacts(feature.getConfigFiles());
+ } catch (RuntimeException e) {
+ throw new RuntimeException("Error resolving feature " + feature.getName() + "/" + feature.getVersion(), e);
+ }
}
} catch (Exception e) {
throw new MojoExecutionException("Error populating repository", e);
@@ -253,7 +257,11 @@ public abstract class AbstractFeatureMojo extends MojoSupport {
Artifact artifact = resourceToArtifact(artifactRef.getUrl(), skipNonMavenProtocols);
if (artifact != null) {
artifactRef.setArtifact(artifact);
- resolveArtifact(artifact, remoteRepos);
+ try {
+ resolveArtifact(artifact, remoteRepos);
+ } catch (RuntimeException e) {
+ throw new RuntimeException("Error resolving artifact " + artifactRef.getUrl(), e);
+ }
}
checkDoGarbageCollect();
}
[04/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/src/test/java/org/apache/karaf/features/obr/internal/ObrResolverTest.java
----------------------------------------------------------------------
diff --git a/features/obr/src/test/java/org/apache/karaf/features/obr/internal/ObrResolverTest.java b/features/obr/src/test/java/org/apache/karaf/features/obr/internal/ObrResolverTest.java
deleted file mode 100644
index 0c2676b..0000000
--- a/features/obr/src/test/java/org/apache/karaf/features/obr/internal/ObrResolverTest.java
+++ /dev/null
@@ -1,174 +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.obr.internal;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-import org.apache.felix.bundlerepository.Reason;
-import org.apache.felix.bundlerepository.RepositoryAdmin;
-import org.apache.felix.bundlerepository.Requirement;
-import org.apache.felix.bundlerepository.Resolver;
-import org.apache.felix.bundlerepository.Resource;
-import org.apache.felix.bundlerepository.impl.DataModelHelperImpl;
-import org.apache.felix.bundlerepository.impl.ReasonImpl;
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.internal.model.Bundle;
-import org.easymock.Capture;
-import org.easymock.EasyMock;
-import org.easymock.IAnswer;
-import org.junit.Test;
-
-public class ObrResolverTest {
-
- @Test
- public void testResolver() throws Exception {
- final String requirement = "bundle:(&(symbolicname=org.apache.camel.camel-blueprint)(version>=2.4.0)(version<2.4.1))";
-
- final org.apache.karaf.features.internal.model.Feature f = new org.apache.karaf.features.internal.model.Feature("f1", "1.0");
- f.setResolver("obr");
- f.getBundle().add(new Bundle(requirement));
- final RepositoryAdmin admin = createMock(RepositoryAdmin.class);
- final Resolver resolver = createMock(Resolver.class);
- final Resource resource = createMock(Resource.class);
- final ObrResolver obrResolver = new ObrResolver();
- obrResolver.setRepositoryAdmin(admin);
-
- final Capture<Requirement> captureReq = new Capture<Requirement>();
-
- expect(admin.getHelper()).andReturn(new DataModelHelperImpl()).anyTimes();
- expect(admin.getSystemRepository()).andReturn(createMock(org.apache.felix.bundlerepository.Repository.class));
- expect(admin.getLocalRepository()).andReturn(createMock(org.apache.felix.bundlerepository.Repository.class));
- expect(admin.listRepositories()).andReturn(new org.apache.felix.bundlerepository.Repository[0]);
- expect(admin.resolver(EasyMock.<org.apache.felix.bundlerepository.Repository[]>anyObject())).andReturn(resolver);
- resolver.add(EasyMock.capture(captureReq));
- expect(resolver.resolve(Resolver.NO_OPTIONAL_RESOURCES)).andReturn(true);
- expect(resolver.getAddedResources()).andReturn(new Resource[] { });
- expect(resolver.getRequiredResources()).andReturn(new Resource[] { resource });
- expect(resolver.getReason(resource)).andAnswer(new IAnswer<Reason[]>() {
- public Reason[] answer() throws Throwable {
- return new Reason[] { new ReasonImpl( resource, captureReq.getValue()) };
- }
- });
- expect(resource.getURI()).andReturn("foo:bar");
- replay(admin, resolver, resource);
-
- List<BundleInfo> bundles = obrResolver.resolve(f);
- assertNotNull(bundles);
- assertEquals(1, bundles.size());
- assertEquals("foo:bar", bundles.get(0).getLocation());
- assertEquals(obrResolver.parseRequirement(requirement).toString(), captureReq.getValue().toString());
- verify(admin, resolver, resource);
- }
-
- @Test
- public void testResolverWithOptionalImports() throws Exception {
- final String requirement = "bundle:(&(symbolicname=org.apache.camel.camel-blueprint)(version>=2.4.0)(version<2.4.1))";
-
- final org.apache.karaf.features.internal.model.Feature f = new org.apache.karaf.features.internal.model.Feature("f1", "1.0");
- f.setResolver("obr");
- f.getBundle().add(new Bundle(requirement));
- final RepositoryAdmin admin = createMock(RepositoryAdmin.class);
- final Resolver resolver = createMock(Resolver.class);
- final Resource resource = createMock(Resource.class);
- final Resource optionalResource = createMock(Resource.class);
- final ObrResolver obrResolver = new ObrResolver();
- obrResolver.setRepositoryAdmin(admin);
- obrResolver.setResolveOptionalImports(true);
-
- final Capture<Requirement> captureReq = new Capture<Requirement>();
-
- expect(admin.getHelper()).andReturn(new DataModelHelperImpl()).anyTimes();
- expect(admin.getSystemRepository()).andReturn(createMock(org.apache.felix.bundlerepository.Repository.class));
- expect(admin.getLocalRepository()).andReturn(createMock(org.apache.felix.bundlerepository.Repository.class));
- expect(admin.listRepositories()).andReturn(new org.apache.felix.bundlerepository.Repository[0]);
- expect(admin.resolver(EasyMock.<org.apache.felix.bundlerepository.Repository[]>anyObject())).andReturn(resolver);
- resolver.add(EasyMock.capture(captureReq));
- expect(resolver.resolve()).andReturn(true);
- expect(resolver.getAddedResources()).andReturn(new Resource[] { });
- expect(resolver.getRequiredResources()).andReturn(new Resource[] { resource });
- expect(resolver.getOptionalResources()).andReturn(new Resource[] { optionalResource});
- expect(resolver.getReason(resource)).andAnswer(new IAnswer<Reason[]>() {
- public Reason[] answer() throws Throwable {
- return new Reason[] { new ReasonImpl( resource, captureReq.getValue()) };
- }
- });
- expect(resolver.getReason(optionalResource)).andAnswer(new IAnswer<Reason[]>() {
- public Reason[] answer() throws Throwable {
- return new Reason[] { new ReasonImpl( optionalResource, captureReq.getValue()) };
- }
- });
- expect(resource.getURI()).andReturn("foo:bar");
- expect(optionalResource.getURI()).andReturn("foo:optional:baz");
- replay(admin, resolver, resource, optionalResource);
-
- List<BundleInfo> bundles = obrResolver.resolve(f);
- assertNotNull(bundles);
- assertEquals(2, bundles.size());
- assertEquals("foo:bar", bundles.get(0).getLocation());
- assertEquals("foo:optional:baz", bundles.get(1).getLocation());
- assertEquals(obrResolver.parseRequirement(requirement).toString(), captureReq.getValue().toString());
- verify(admin, resolver, resource);
- }
-
- /**
- * Test resolving a mvn url when pax url is configured with a repo that contains no protocol like: "test"
- * We expect to get a MalFormedUrlException not a IllegalArgumentException as in the issue
- * @throws Exception
- */
- @Test(expected=MalformedURLException.class)
- public void testResolverWithInvalidMvnRepoIssueKaraf667() throws Exception {
- final org.apache.karaf.features.internal.model.Feature f = new org.apache.karaf.features.internal.model.Feature("f1", "1.0");
- f.setResolver("obr");
- // Using file instead of mvn: as we do not want to mess with URL handlers
- f.getBundle().add(new Bundle("file:org.foo/foo/1.0"));
-
- final RepositoryAdmin admin = createMock(RepositoryAdmin.class);
- final Resolver resolver = createMock(Resolver.class);
- final Resource resource = createMock(Resource.class);
- final ObrResolver obrResolver = new ObrResolver();
- obrResolver.setRepositoryAdmin(admin);
-
- expect(admin.getHelper()).andReturn(new DummyDataModelHelper()).anyTimes();
- replay(admin, resolver, resource);
-
- obrResolver.resolve(f);
- }
-
- /**
- * Recreates behaviour of DataModelHelper when an invalid maven url is set for pax url repos
- * TODO Remove as soon as DataModelHelper does not throw this exception any more
- */
- class DummyDataModelHelper extends DataModelHelperImpl {
-
- @Override
- public Resource createResource(URL bundleUrl) throws IOException {
- throw new MalformedURLException("Missing protocol");
- }
-
-
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/pom.xml
----------------------------------------------------------------------
diff --git a/features/pom.xml b/features/pom.xml
index 7597051..8cfede1 100644
--- a/features/pom.xml
+++ b/features/pom.xml
@@ -36,7 +36,6 @@
<modules>
<module>core</module>
<module>command</module>
- <module>obr</module>
</modules>
</project>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/instance/core/pom.xml
----------------------------------------------------------------------
diff --git a/instance/core/pom.xml b/instance/core/pom.xml
index 4c745ec..0180bd3 100644
--- a/instance/core/pom.xml
+++ b/instance/core/pom.xml
@@ -148,6 +148,9 @@
<Export-Package>
org.apache.karaf.instance.core,
</Export-Package>
+ <Provide-Capability>
+ service-reference;effective:=active;objectClass=org.apache.karaf.instance.core.InstanceService
+ </Provide-Capability>
<Private-Package>
org.apache.karaf.jpm,
org.apache.karaf.jpm.impl,
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/itests/src/test/java/org/apache/karaf/itests/ConditionalFeaturesTest.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/ConditionalFeaturesTest.java b/itests/src/test/java/org/apache/karaf/itests/ConditionalFeaturesTest.java
index 46908d1..9045b82 100644
--- a/itests/src/test/java/org/apache/karaf/itests/ConditionalFeaturesTest.java
+++ b/itests/src/test/java/org/apache/karaf/itests/ConditionalFeaturesTest.java
@@ -70,7 +70,7 @@ public class ConditionalFeaturesTest extends KarafTestSupport {
@Test
public void testWebconsole() throws Exception {
try {
- featureService.uninstallFeature("eventadmin");
+ featureService.uninstallFeature("scr");
} catch (Exception e) {
}
featureService.installFeature("webconsole");
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/itests/src/test/java/org/apache/karaf/itests/FeatureSshCommandSecurityTest.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/FeatureSshCommandSecurityTest.java b/itests/src/test/java/org/apache/karaf/itests/FeatureSshCommandSecurityTest.java
index 76350c0..223c9a2 100644
--- a/itests/src/test/java/org/apache/karaf/itests/FeatureSshCommandSecurityTest.java
+++ b/itests/src/test/java/org/apache/karaf/itests/FeatureSshCommandSecurityTest.java
@@ -24,31 +24,32 @@ public class FeatureSshCommandSecurityTest extends SshCommandTestBase {
@Test
public void testFeatureCommandSecurityViaSsh() throws Exception {
String vieweruser = "viewer" + System.nanoTime() + "_features";
+ String feature = "wrapper";
addViewer(vieweruser);
String r = assertCommand(vieweruser, "feature:list -i --no-format", Result.OK);
- Assert.assertFalse("Precondition failed, this test uses the eventadmin subsystem to test features with...",
- r.contains("eventadmin"));
+ Assert.assertFalse("Precondition failed, this test uses the " + feature + " subsystem to test features with...",
+ r.contains(feature));
- assertCommand(vieweruser, "feature:install eventadmin", Result.NOT_FOUND);
+ assertCommand(vieweruser, "feature:install " + feature, Result.NOT_FOUND);
String r2 = assertCommand("karaf", "feature:list -i --no-format", Result.OK);
- Assert.assertFalse("eventadmin features should not have been installed, as viewer doesn't have credentials",
- r2.contains("eventadmin"));
+ Assert.assertFalse(feature + " features should not have been installed, as viewer doesn't have credentials",
+ r2.contains(feature));
- assertCommand("karaf", "feature:install eventadmin", Result.OK);
+ assertCommand("karaf", "feature:install " + feature, Result.OK);
String r3 = assertCommand(vieweruser, "feature:list -i --no-format", Result.OK);
- Assert.assertTrue("eventadmin feature should have been installed by 'karaf' user",
- r3.contains("eventadmin"));
+ Assert.assertTrue(feature + " feature should have been installed by 'karaf' user",
+ r3.contains(feature));
- assertCommand(vieweruser, "feature:uninstall eventadmin", Result.NOT_FOUND);
+ assertCommand(vieweruser, "feature:uninstall " + feature, Result.NOT_FOUND);
String r4 = assertCommand("karaf", "feature:list -i --no-format", Result.OK);
- Assert.assertTrue("eventadmin feature should still be there, as viewer doesn't have credentials",
- r4.contains("eventadmin"));
+ Assert.assertTrue(feature + " feature should still be there, as viewer doesn't have credentials",
+ r4.contains(feature));
- assertCommand("karaf", "feature:uninstall eventadmin", Result.OK);
+ assertCommand("karaf", "feature:uninstall " + feature, Result.OK);
String r5 = assertCommand(vieweruser, "feature:list -i --no-format", Result.OK);
- Assert.assertFalse("The eventadmin subsystem should have been uninstalled",
- r5.contains("eventadmin"));
+ Assert.assertFalse(feature + " feature should have been uninstalled",
+ r5.contains(feature));
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/itests/src/test/java/org/apache/karaf/itests/FeatureTest.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/FeatureTest.java b/itests/src/test/java/org/apache/karaf/itests/FeatureTest.java
index 2098683..c9d9c77 100644
--- a/itests/src/test/java/org/apache/karaf/itests/FeatureTest.java
+++ b/itests/src/test/java/org/apache/karaf/itests/FeatureTest.java
@@ -64,14 +64,14 @@ public class FeatureTest extends KarafTestSupport {
@Test
public void installUninstallCommand() throws Exception {
- String featureInstallOutput = executeCommand("feature:install -v eventadmin", new RolePrincipal("admin"));
+ String featureInstallOutput = executeCommand("feature:install -v wrapper", new RolePrincipal("admin"));
System.out.println(featureInstallOutput);
assertFalse(featureInstallOutput.isEmpty());
- String featureListOutput = executeCommand("feature:list -i | grep eventadmin");
+ String featureListOutput = executeCommand("feature:list -i | grep wrapper");
System.out.println(featureListOutput);
assertFalse(featureListOutput.isEmpty());
- System.out.println(executeCommand("feature:uninstall eventadmin", new RolePrincipal("admin")));
- featureListOutput = executeCommand("feature:list -i | grep eventadmin");
+ System.out.println(executeCommand("feature:uninstall wrapper", new RolePrincipal("admin")));
+ featureListOutput = executeCommand("feature:list -i | grep wrapper");
System.out.println(featureListOutput);
assertTrue(featureListOutput.isEmpty());
}
@@ -83,8 +83,8 @@ public class FeatureTest extends KarafTestSupport {
connector = this.getJMXConnector();
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName name = new ObjectName("org.apache.karaf:type=feature,name=root");
- connection.invoke(name, "installFeature", new Object[] { "eventadmin" }, new String[]{ "java.lang.String" });
- connection.invoke(name, "uninstallFeature", new Object[] { "eventadmin" }, new String[]{ "java.lang.String" });
+ connection.invoke(name, "installFeature", new Object[] { "wrapper" }, new String[]{ "java.lang.String" });
+ connection.invoke(name, "uninstallFeature", new Object[] { "wrapper" }, new String[]{ "java.lang.String" });
} finally {
close(connector);
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/itests/src/test/java/org/apache/karaf/itests/HttpTest.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/HttpTest.java b/itests/src/test/java/org/apache/karaf/itests/HttpTest.java
index 459301b..66ea463 100644
--- a/itests/src/test/java/org/apache/karaf/itests/HttpTest.java
+++ b/itests/src/test/java/org/apache/karaf/itests/HttpTest.java
@@ -26,6 +26,7 @@ import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.Constants;
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
@@ -38,6 +39,7 @@ public class HttpTest extends KarafTestSupport {
@Test
public void list() throws Exception {
+ waitForService("(objectClass=javax.servlet.ServletContext)", 5000);
assertContains("/system/console", executeCommand("http:list"));
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
index 5062c64..207d75a 100644
--- a/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
+++ b/itests/src/test/java/org/apache/karaf/itests/KarafTestSupport.java
@@ -302,7 +302,7 @@ public class KarafTestSupport {
}
}
- private void waitForService(String filter, long timeout) throws InvalidSyntaxException, InterruptedException {
+ protected void waitForService(String filter, long timeout) throws InvalidSyntaxException, InterruptedException {
ServiceTracker<Object, Object> st = new ServiceTracker<Object, Object>(bundleContext, bundleContext.createFilter(filter), null);
try {
st.open();
@@ -351,23 +351,27 @@ public class KarafTestSupport {
}
public void assertFeatureInstalled(String featureName) throws Exception {
- Feature[] features = featureService.listInstalledFeatures();
- for (Feature feature : features) {
- if (featureName.equals(feature.getName())) {
- return;
- }
+ String name;
+ String version;
+ if (featureName.contains("/")) {
+ name = featureName.substring(0, featureName.indexOf("/"));
+ version = featureName.substring(featureName.indexOf("/") + 1);
+ } else {
+ name = featureName;
+ version = null;
}
- Assert.fail("Feature " + featureName + " should be installed but is not");
+ assertFeatureInstalled(name, version);
}
public void assertFeatureInstalled(String featureName, String featureVersion) throws Exception {
+ Feature featureToAssert = featureService.getFeature(featureName, featureVersion);
Feature[] features = featureService.listInstalledFeatures();
for (Feature feature : features) {
- if (featureName.equals(feature.getName()) && featureVersion.equals(feature.getVersion())) {
+ if (featureToAssert.equals(feature)) {
return;
}
}
- Assert.fail("Feature " + featureName + "/" + featureVersion + " should be installed but is not");
+ Assert.fail("Feature " + featureName + (featureVersion != null ? "/" + featureVersion : "") + " should be installed but is not");
}
public void assertFeaturesInstalled(String ... expectedFeatures) throws Exception {
@@ -412,24 +416,28 @@ public class KarafTestSupport {
}
protected void installAssertAndUninstallFeature(String feature, String version) throws Exception {
- Set<Feature> featuresBefore = new HashSet<Feature>(Arrays.asList(featureService.listInstalledFeatures()));
- try {
- featureService.installFeature(feature, version);
- assertFeatureInstalled(feature, version);
- } finally {
- uninstallNewFeatures(featuresBefore);
- }
+ installAssertAndUninstallFeatures(feature + "/" + version);
}
protected void installAssertAndUninstallFeatures(String... feature) throws Exception {
- Set<Feature> featuresBefore = new HashSet<Feature>(Arrays.asList(featureService.listInstalledFeatures()));
+ boolean success = false;
try {
for (String curFeature : feature) {
featureService.installFeature(curFeature);
assertFeatureInstalled(curFeature);
}
+ success = true;
} finally {
- uninstallNewFeatures(featuresBefore);
+ for (String curFeature : feature) {
+ System.out.println("Uninstalling " + curFeature);
+ try {
+ featureService.uninstallFeature(curFeature);
+ } catch (Exception e) {
+ if (success) {
+ throw e;
+ }
+ }
+ }
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/itests/src/test/java/org/apache/karaf/itests/features/Spring3FeaturesTest.java
----------------------------------------------------------------------
diff --git a/itests/src/test/java/org/apache/karaf/itests/features/Spring3FeaturesTest.java b/itests/src/test/java/org/apache/karaf/itests/features/Spring3FeaturesTest.java
index 22e1113..19b6f43 100644
--- a/itests/src/test/java/org/apache/karaf/itests/features/Spring3FeaturesTest.java
+++ b/itests/src/test/java/org/apache/karaf/itests/features/Spring3FeaturesTest.java
@@ -14,6 +14,7 @@
package org.apache.karaf.itests.features;
import org.apache.karaf.itests.KarafTestSupport;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.junit.PaxExam;
@@ -70,6 +71,7 @@ public class Spring3FeaturesTest extends KarafTestSupport {
}
@Test
+ @Ignore
public void installSpringOrmFeature() throws Exception {
installAssertAndUninstallFeature("spring-orm", System.getProperty("spring32.version"));
}
@@ -85,11 +87,13 @@ public class Spring3FeaturesTest extends KarafTestSupport {
}
@Test
+ @Ignore
public void installSpringWebFeature() throws Exception {
installAssertAndUninstallFeature("spring-web", System.getProperty("spring32.version"));
}
@Test
+ @Ignore
public void installSpringWebPortletFeature() throws Exception {
installAssertAndUninstallFeature("spring-web-portlet", System.getProperty("spring32.version"));
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/management/server/pom.xml
----------------------------------------------------------------------
diff --git a/management/server/pom.xml b/management/server/pom.xml
index 4c92be9..c522874 100644
--- a/management/server/pom.xml
+++ b/management/server/pom.xml
@@ -117,6 +117,9 @@
org.apache.karaf.service.guard.tools,
org.apache.karaf.util.tracker
</Private-Package>
+ <Provide-Capability>
+ service-reference;effective:=active;objectClass=javax.management.MBeanServer
+ </Provide-Capability>
<Bundle-Activator>
org.apache.karaf.management.internal.Activator
</Bundle-Activator>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4f78489..3d90475 100644
--- a/pom.xml
+++ b/pom.xml
@@ -172,6 +172,7 @@
<felix.scr.version>1.8.2</felix.scr.version>
<felix.scr.webconsole.plugin.version>1.0.0</felix.scr.webconsole.plugin.version>
<felix.scr.annotation.version>1.6.0</felix.scr.annotation.version>
+ <felix.resolver.version>1.0.0</felix.resolver.version>
<aries.application.version>1.0.0</aries.application.version>
<aries.application.api.version>1.0.0</aries.application.api.version>
@@ -998,6 +999,11 @@
<artifactId>org.apache.felix.scr</artifactId>
<version>${felix.scr.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.resolver</artifactId>
+ <version>${felix.resolver.version}</version>
+ </dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/shell/console/pom.xml
----------------------------------------------------------------------
diff --git a/shell/console/pom.xml b/shell/console/pom.xml
index 69f1dbb..90cdeb5 100644
--- a/shell/console/pom.xml
+++ b/shell/console/pom.xml
@@ -148,27 +148,27 @@
*
</Import-Package>
<Export-Package>
- org.apache.karaf.shell.commands;version=${project.version},
- org.apache.karaf.shell.commands;version=2.3.0,
- org.apache.karaf.shell.commands.basic;version=${project.version},
- org.apache.karaf.shell.commands.basic;version=2.3.0,
- org.apache.karaf.shell.commands.converter;version=${project.version},
- org.apache.karaf.shell.commands.converter;version=2.3.0,
- org.apache.karaf.shell.commands.meta;version=${project.version},
- org.apache.karaf.shell.commands.meta;version=2.3.0,
- org.apache.karaf.shell.console;version=${project.version},
- org.apache.karaf.shell.console;version=2.3.0,
- org.apache.karaf.shell.console.commands;version=${project.version},
- org.apache.karaf.shell.console.commands;version=2.3.0,
- org.apache.karaf.shell.console.completer;version=${project.version},
- org.apache.karaf.shell.console.completer;version=2.3.0,
- org.apache.karaf.shell.console.factory;version=${project.version},
- org.apache.karaf.shell.inject;version=${project.version},
- org.apache.karaf.shell.util;version=${project.version},
- org.apache.karaf.shell.util;version=2.3.0,
- org.apache.felix.gogo*;version=${felix.gogo.version},
- org.apache.felix.service.command;version=${felix.gogo.version};status=provisional;mandatory:=status,
- org.apache.felix.service.threadio;version=${felix.gogo.version};status=provisional;mandatory:=status,
+ org.apache.karaf.shell.commands;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.commands;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.commands.basic;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.commands.basic;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.commands.converter;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.commands.converter;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.commands.meta;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.commands.meta;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.console;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.console;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.console.commands;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.console.commands;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.console.completer;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.console.completer;version=2.3.0;-noimport:=true,
+ org.apache.karaf.shell.console.factory;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.inject;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.util;version=${project.version};-noimport:=true,
+ org.apache.karaf.shell.util;version=2.3.0;-noimport:=true,
+ org.apache.felix.gogo*;version=${felix.gogo.version};-noimport:=true,
+ org.apache.felix.service.command;version=${felix.gogo.version};status=provisional;mandatory:=status;-noimport:=true,
+ org.apache.felix.service.threadio;version=${felix.gogo.version};status=provisional;mandatory:=status;-noimport:=true,
</Export-Package>
<Private-Package>
org.apache.karaf.shell.commands.ansi,
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/shell/core/pom.xml
----------------------------------------------------------------------
diff --git a/shell/core/pom.xml b/shell/core/pom.xml
index c5a0bac..92c7555 100644
--- a/shell/core/pom.xml
+++ b/shell/core/pom.xml
@@ -165,6 +165,9 @@
org.apache.felix.service.command,
org.apache.felix.service.threadio,
</Private-Package>
+ <Provide-Capability>
+ service-reference;effective:=active;objectClass=org.apache.karaf.shell.api.console.SessionFactory
+ </Provide-Capability>
<Bundle-Activator>
org.apache.karaf.shell.impl.console.osgi.Activator
</Bundle-Activator>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml b/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
index 7c50b96..01a6721 100644
--- a/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-aggregate-features/control.xml
@@ -18,7 +18,7 @@
~ under the License.
-->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="aggregate-features">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" name="aggregate-features">
<feature description="aggregate-recursive-module-c" version="1.0-SNAPSHOT" name="aggregate-recursive-module-c">
<details>Test Description</details>
<bundle>mvn:test/aggregate-recursive-module-b/1.0-SNAPSHOT</bundle>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml b/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
index 06139fe..2318b17 100644
--- a/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-basic-generation/control.xml
@@ -18,4 +18,4 @@
~ under the License.
-->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="test-basic-generation"/>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" name="test-basic-generation"/>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/it/test-check-dependencies-failure/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-check-dependencies-failure/control.xml b/tooling/karaf-maven-plugin/src/it/test-check-dependencies-failure/control.xml
index a1a81c4..223d090 100644
--- a/tooling/karaf-maven-plugin/src/it/test-check-dependencies-failure/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-check-dependencies-failure/control.xml
@@ -18,7 +18,7 @@
~ under the License.
-->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="check-dependencies-features">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" name="check-dependencies-features">
<feature description="dependency-module-c" version="1.0-SNAPSHOT" name="dependency-module-c">
<bundle>mvn:test/dependency-module-a/1.0-SNAPSHOT</bundle>
<bundle>mvn:test/dependency-module-b/1.0-SNAPSHOT</bundle>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml b/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
index a1a81c4..223d090 100644
--- a/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-check-dependencies/control.xml
@@ -18,7 +18,7 @@
~ under the License.
-->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="check-dependencies-features">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" name="check-dependencies-features">
<feature description="dependency-module-c" version="1.0-SNAPSHOT" name="dependency-module-c">
<bundle>mvn:test/dependency-module-a/1.0-SNAPSHOT</bundle>
<bundle>mvn:test/dependency-module-b/1.0-SNAPSHOT</bundle>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml b/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
index 25a2acb..08e8f0e 100644
--- a/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-input-file/control.xml
@@ -18,7 +18,7 @@
~ under the License.
-->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="test-input-file">
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" name="test-input-file">
<feature description="Test Description" version="1.0-SNAPSHOT" name="test-input-file">
<details>Test Description</details>
<bundle>mvn:test/test-input-file/1.0-SNAPSHOT</bundle>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml b/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
index 9b84d85..a378636 100644
--- a/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
+++ b/tooling/karaf-maven-plugin/src/it/test-type-classifier/control.xml
@@ -18,4 +18,4 @@
~ under the License.
-->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="test-type-classifier"/>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" name="test-type-classifier"/>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/ValidateDescriptorMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/ValidateDescriptorMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/ValidateDescriptorMojo.java
index bcf608a..f5cb55f 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/ValidateDescriptorMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/ValidateDescriptorMojo.java
@@ -22,8 +22,8 @@ import org.apache.karaf.features.BundleInfo;
import org.apache.karaf.features.Dependency;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.Repository;
-import org.apache.karaf.features.internal.FeatureValidationUtil;
-import org.apache.karaf.features.internal.RepositoryImpl;
+import org.apache.karaf.features.internal.service.FeatureValidationUtil;
+import org.apache.karaf.features.internal.service.RepositoryImpl;
import org.apache.karaf.tooling.url.CustomBundleURLStreamHandlerFactory;
import org.apache.karaf.tooling.utils.MojoSupport;
import org.apache.maven.artifact.Artifact;
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
----------------------------------------------------------------------
diff --git a/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java b/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
index 65a1bdd..4f34b06 100644
--- a/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
+++ b/util/src/main/java/org/apache/karaf/util/tracker/BaseActivator.java
@@ -45,11 +45,21 @@ public class BaseActivator implements BundleActivator, SingleServiceTracker.Sing
new LinkedBlockingQueue<Runnable>());
private AtomicBoolean scheduled = new AtomicBoolean();
+ private long schedulerStopTimeout = TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS);
+
private List<ServiceRegistration> registrations;
private Map<String, SingleServiceTracker> trackers = new HashMap<String, SingleServiceTracker>();
private ServiceRegistration managedServiceRegistration;
private Dictionary<String, ?> configuration;
+ public long getSchedulerStopTimeout() {
+ return schedulerStopTimeout;
+ }
+
+ public void setSchedulerStopTimeout(long schedulerStopTimeout) {
+ this.schedulerStopTimeout = schedulerStopTimeout;
+ }
+
@Override
public void start(BundleContext context) throws Exception {
bundleContext = context;
@@ -72,7 +82,7 @@ public class BaseActivator implements BundleActivator, SingleServiceTracker.Sing
scheduled.set(true);
doClose();
executor.shutdown();
- executor.awaitTermination(30, TimeUnit.SECONDS);
+ executor.awaitTermination(schedulerStopTimeout, TimeUnit.MILLISECONDS);
doStop();
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/webconsole/branding/pom.xml
----------------------------------------------------------------------
diff --git a/webconsole/branding/pom.xml b/webconsole/branding/pom.xml
index 30359c6..5ec04c6 100644
--- a/webconsole/branding/pom.xml
+++ b/webconsole/branding/pom.xml
@@ -79,7 +79,7 @@
<configuration>
<instructions>
<Bundle-DocURL>http://felix.apache.org/site/apache-karaf.html</Bundle-DocURL>
- <Fragment-Host>org.apache.karaf.webconsole.console;bundle-version="[3,4)"</Fragment-Host>
+ <Fragment-Host>org.apache.karaf.webconsole.console;bundle-version="[4,5)"</Fragment-Host>
<Export-Package>!*</Export-Package>
<Import-Package>
javax.servlet;version=2.4,
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
----------------------------------------------------------------------
diff --git a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
index 401cf29..47052ab 100644
--- a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
+++ b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/ExtendedFeature.java
@@ -20,10 +20,12 @@ import java.util.List;
import java.util.Map;
import org.apache.karaf.features.BundleInfo;
+import org.apache.karaf.features.Capability;
import org.apache.karaf.features.Conditional;
import org.apache.karaf.features.ConfigFileInfo;
import org.apache.karaf.features.Dependency;
import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.Requirement;
public class ExtendedFeature implements Feature {
@@ -69,6 +71,16 @@ public class ExtendedFeature implements Feature {
}
@Override
+ public List<? extends Capability> getCapabilities() {
+ return feature.getCapabilities();
+ }
+
+ @Override
+ public List<? extends Requirement> getRequirements() {
+ return feature.getRequirements();
+ }
+
+ @Override
public List<Dependency> getDependencies() {
return this.feature.getDependencies();
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
----------------------------------------------------------------------
diff --git a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
index 41cb7b3..8e0af3a 100644
--- a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
+++ b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
@@ -272,8 +272,6 @@ public class FeaturesPlugin extends AbstractWebConsolePlugin {
jw.array();
for (Repository r : repositories) {
jw.object();
- jw.key("name");
- jw.value(r.getName());
jw.key("url");
String uri = r.getURI().toString();
jw.value(uri);
[02/13] git commit: [KARAF-2833] Remove unused imports in ssh
activator
Posted by gn...@apache.org.
[KARAF-2833] Remove unused imports in ssh activator
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/0d87c62d
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/0d87c62d
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/0d87c62d
Branch: refs/heads/master
Commit: 0d87c62dfb5c56b99a7195716a851c30f22bb72c
Parents: 5337778
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Thu Apr 3 09:43:45 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon Apr 7 10:52:30 2014 +0200
----------------------------------------------------------------------
.../java/org/apache/karaf/shell/ssh/Activator.java | 14 --------------
1 file changed, 14 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/0d87c62d/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
----------------------------------------------------------------------
diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
index 16f08ab..b87cefb 100644
--- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
+++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java
@@ -20,32 +20,18 @@ package org.apache.karaf.shell.ssh;
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.karaf.shell.api.action.lifecycle.Manager;
import org.apache.karaf.shell.api.console.Session;
import org.apache.karaf.shell.api.console.SessionFactory;
import org.apache.karaf.util.tracker.BaseActivator;
-import org.apache.karaf.util.tracker.SingleServiceTracker;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.sftp.SftpSubsystem;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
[09/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
new file mode 100644
index 0000000..cb2c36a
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
@@ -0,0 +1,1129 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.utils.version.VersionRange;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class ResourceBuilder {
+
+ public static final String RESOLUTION_DYNAMIC = "dynamic";
+
+ public static Resource build(String uri, Map<String, String> headerMap)
+ throws BundleException {
+
+ // Verify that only manifest version 2 is specified.
+ String manifestVersion = getManifestVersion(headerMap);
+ if (manifestVersion == null || !manifestVersion.equals("2")) {
+ throw new BundleException("Unsupported 'Bundle-ManifestVersion' value: " + manifestVersion);
+ }
+
+ //
+ // Parse bundle version.
+ //
+
+ Version bundleVersion = Version.emptyVersion;
+ if (headerMap.get(Constants.BUNDLE_VERSION) != null) {
+ bundleVersion = Version.parseVersion(headerMap.get(Constants.BUNDLE_VERSION));
+ }
+
+ //
+ // Parse bundle symbolic name.
+ //
+
+ String bundleSymbolicName = null;
+ ParsedHeaderClause bundleCap = parseBundleSymbolicName(headerMap);
+ if (bundleCap == null) {
+ throw new BundleException("Bundle manifest must include bundle symbolic name");
+ }
+ bundleSymbolicName = (String) bundleCap.attrs.get(BundleRevision.BUNDLE_NAMESPACE);
+
+ // Now that we have symbolic name and version, create the resource
+ String type = headerMap.get(Constants.FRAGMENT_HOST) == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT;
+ ResourceImpl resource = new ResourceImpl(bundleSymbolicName, type, bundleVersion);
+ if (uri != null) {
+ Map<String, Object> attrs = new HashMap<String, Object>();
+ attrs.put(UriNamespace.URI_NAMESPACE, uri);
+ resource.addCapability(new CapabilityImpl(resource, UriNamespace.URI_NAMESPACE, Collections.<String, String>emptyMap(), attrs));
+ }
+
+ // Add a bundle and host capability to all
+ // non-fragment bundles. A host capability is the same
+ // as a require capability, but with a different capability
+ // namespace. Bundle capabilities resolve required-bundle
+ // dependencies, while host capabilities resolve fragment-host
+ // dependencies.
+ if (headerMap.get(Constants.FRAGMENT_HOST) == null) {
+ // All non-fragment bundles have bundle capability.
+ resource.addCapability(new CapabilityImpl(resource, BundleRevision.BUNDLE_NAMESPACE, bundleCap.dirs, bundleCap.attrs));
+ // A non-fragment bundle can choose to not have a host capability.
+ String attachment = bundleCap.dirs.get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+ attachment = (attachment == null) ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME : attachment;
+ if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
+ Map<String, Object> hostAttrs = new HashMap<String, Object>(bundleCap.attrs);
+ Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+ hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
+ resource.addCapability(new CapabilityImpl(
+ resource, BundleRevision.HOST_NAMESPACE,
+ bundleCap.dirs,
+ hostAttrs));
+ }
+ }
+
+ //
+ // Parse Fragment-Host.
+ //
+
+ List<RequirementImpl> hostReqs = parseFragmentHost(resource, headerMap);
+
+ //
+ // Parse Require-Bundle
+ //
+
+ List<ParsedHeaderClause> rbClauses = parseStandardHeader(headerMap.get(Constants.REQUIRE_BUNDLE));
+ rbClauses = normalizeRequireClauses(rbClauses);
+ List<Requirement> rbReqs = convertRequires(rbClauses, resource);
+
+ //
+ // Parse Import-Package.
+ //
+
+ List<ParsedHeaderClause> importClauses = parseStandardHeader(headerMap.get(Constants.IMPORT_PACKAGE));
+ importClauses = normalizeImportClauses(importClauses);
+ List<Requirement> importReqs = convertImports(importClauses, resource);
+
+ //
+ // Parse DynamicImport-Package.
+ //
+
+ List<ParsedHeaderClause> dynamicClauses = parseStandardHeader(headerMap.get(Constants.DYNAMICIMPORT_PACKAGE));
+ dynamicClauses = normalizeDynamicImportClauses(dynamicClauses);
+ List<Requirement> dynamicReqs = convertImports(dynamicClauses, resource);
+
+ //
+ // Parse Require-Capability.
+ //
+
+ List<ParsedHeaderClause> requireClauses = parseStandardHeader(headerMap.get(Constants.REQUIRE_CAPABILITY));
+ requireClauses = normalizeRequireCapabilityClauses(requireClauses);
+ List<Requirement> requireReqs = convertRequireCapabilities(requireClauses, resource);
+
+ //
+ // Parse Export-Package.
+ //
+
+ List<ParsedHeaderClause> exportClauses = parseStandardHeader(headerMap.get(Constants.EXPORT_PACKAGE));
+ exportClauses = normalizeExportClauses(exportClauses, bundleSymbolicName, bundleVersion);
+ List<Capability> exportCaps = convertExports(exportClauses, resource);
+
+ //
+ // Parse Provide-Capability.
+ //
+
+ List<ParsedHeaderClause> provideClauses = parseStandardHeader(headerMap.get(Constants.PROVIDE_CAPABILITY));
+ provideClauses = normalizeProvideCapabilityClauses(provideClauses);
+ List<Capability> provideCaps = convertProvideCapabilities(provideClauses, resource);
+
+ //
+ // Parse Import-Service and Export-Service
+ // if Require-Capability and Provide-Capability are not set for services
+ //
+
+ boolean hasServiceReferenceCapability = false;
+ for (Capability cap : exportCaps) {
+ hasServiceReferenceCapability |= ServiceNamespace.SERVICE_NAMESPACE.equals(cap.getNamespace());
+ }
+ if (!hasServiceReferenceCapability) {
+ List<ParsedHeaderClause> exportServices = parseStandardHeader(headerMap.get(Constants.EXPORT_SERVICE));
+ List<Capability> caps = convertExportService(exportServices, resource);
+ provideCaps.addAll(caps);
+ }
+
+ boolean hasServiceReferenceRequirement = false;
+ for (Requirement req : requireReqs) {
+ hasServiceReferenceRequirement |= ServiceNamespace.SERVICE_NAMESPACE.equals(req.getNamespace());
+ }
+ if (!hasServiceReferenceRequirement) {
+ List<ParsedHeaderClause> importServices = parseStandardHeader(headerMap.get(Constants.IMPORT_SERVICE));
+ List<Requirement> reqs = convertImportService(importServices, resource);
+ requireReqs.addAll(reqs);
+ }
+
+ // Combine all capabilities.
+ resource.addCapabilities(exportCaps);
+ resource.addCapabilities(provideCaps);
+
+ // Combine all requirements.
+ resource.addRequirements(hostReqs);
+ resource.addRequirements(importReqs);
+ resource.addRequirements(rbReqs);
+ resource.addRequirements(requireReqs);
+ resource.addRequirements(dynamicReqs);
+
+ return resource;
+ }
+
+ public static List<Requirement> parseImport(Resource resource, String imports) throws BundleException {
+ List<ParsedHeaderClause> importClauses = parseStandardHeader(imports);
+ importClauses = normalizeImportClauses(importClauses);
+ List<Requirement> importReqs = convertImports(importClauses, resource);
+ return importReqs;
+ }
+
+ public static List<Requirement> parseRequirement(Resource resource, String requirement) throws BundleException {
+ List<ParsedHeaderClause> requireClauses = parseStandardHeader(requirement);
+ requireClauses = normalizeRequireCapabilityClauses(requireClauses);
+ List<Requirement> requireReqs = convertRequireCapabilities(requireClauses, resource);
+ return requireReqs;
+ }
+
+ public static List<Capability> parseExport(Resource resource, String bundleSymbolicName, Version bundleVersion, String exports) throws BundleException {
+ List<ParsedHeaderClause> exportClauses = parseStandardHeader(exports);
+ exportClauses = normalizeExportClauses(exportClauses, bundleSymbolicName, bundleVersion);
+ List<Capability> exportCaps = convertExports(exportClauses, resource);
+ return exportCaps;
+ }
+
+ public static List<Capability> parseCapability(Resource resource, String capability) throws BundleException {
+ List<ParsedHeaderClause> provideClauses = parseStandardHeader(capability);
+ provideClauses = normalizeProvideCapabilityClauses(provideClauses);
+ List<Capability> provideCaps = convertProvideCapabilities(provideClauses, resource);
+ return provideCaps;
+ }
+
+ @SuppressWarnings( "deprecation" )
+ private static List<ParsedHeaderClause> normalizeImportClauses(
+ List<ParsedHeaderClause> clauses)
+ throws BundleException {
+ // Verify that the values are equals if the package specifies
+ // both version and specification-version attributes.
+ Set<String> dupeSet = new HashSet<String>();
+ for (ParsedHeaderClause clause : clauses) {
+ // Check for "version" and "specification-version" attributes
+ // and verify they are the same if both are specified.
+ Object v = clause.attrs.get(Constants.VERSION_ATTRIBUTE);
+ Object sv = clause.attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if ((v != null) && (sv != null)) {
+ // Verify they are equal.
+ if (!((String) v).trim().equals(((String) sv).trim())) {
+ throw new IllegalArgumentException(
+ "Both version and specification-version are specified, but they are not equal.");
+ }
+ }
+
+ // Ensure that only the "version" attribute is used and convert
+ // it to the VersionRange type.
+ if ((v != null) || (sv != null)) {
+ clause.attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+ v = (v == null) ? sv : v;
+ clause.attrs.put(Constants.VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // If bundle version is specified, then convert its type to VersionRange.
+ v = clause.attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (v != null) {
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // Verify java.* is not imported, nor any duplicate imports.
+ for (String pkgName : clause.paths) {
+ if (!dupeSet.contains(pkgName)) {
+ // Verify that java.* packages are not imported.
+ if (pkgName.startsWith("java.")) {
+ throw new BundleException("Importing java.* packages not allowed: " + pkgName);
+ }
+ // The character "." has no meaning in the OSGi spec except
+ // when placed on the bundle class path. Some people, however,
+ // mistakenly think it means the default package when imported
+ // or exported. This is not correct. It is invalid.
+ else if (pkgName.equals(".")) {
+ throw new BundleException("Importing '.' is invalid.");
+ }
+ // Make sure a package name was specified.
+ else if (pkgName.length() == 0) {
+ throw new BundleException(
+ "Imported package names cannot be zero length.");
+ }
+ dupeSet.add(pkgName);
+ } else {
+ throw new BundleException("Duplicate import: " + pkgName);
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<Capability> convertExportService(List<ParsedHeaderClause> clauses, Resource resource) {
+ List<Capability> capList = new ArrayList<Capability>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ Map<String, String> dirs = new LinkedHashMap<String, String>();
+ dirs.put(ServiceNamespace.CAPABILITY_EFFECTIVE_DIRECTIVE, ServiceNamespace.EFFECTIVE_ACTIVE);
+ Map<String, Object> attrs = new LinkedHashMap<String, Object>();
+ attrs.put(Constants.OBJECTCLASS, path);
+ attrs.putAll(clause.attrs);
+ capList.add(new CapabilityImpl(
+ resource,
+ ServiceNamespace.SERVICE_NAMESPACE,
+ dirs,
+ attrs));
+ }
+ }
+ return capList;
+ }
+
+ private static List<Requirement> convertImportService(List<ParsedHeaderClause> clauses, Resource resource) throws BundleException {
+ try {
+ List<Requirement> reqList = new ArrayList<Requirement>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ String multiple = clause.dirs.get("multiple");
+ String avail = clause.dirs.get("availability");
+ String filter = (String) clause.attrs.get("filter");
+ Map<String, String> dirs = new LinkedHashMap<String, String>();
+ dirs.put(ServiceNamespace.REQUIREMENT_EFFECTIVE_DIRECTIVE, ServiceNamespace.EFFECTIVE_ACTIVE);
+ if ("optional".equals(avail)) {
+ dirs.put(ServiceNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, ServiceNamespace.RESOLUTION_OPTIONAL);
+ }
+ if ("true".equals(multiple)) {
+ dirs.put(ServiceNamespace.REQUIREMENT_CARDINALITY_DIRECTIVE, ServiceNamespace.CARDINALITY_MULTIPLE);
+ }
+ if (filter == null) {
+ filter = "(" + Constants.OBJECTCLASS + "=" + path + ")";
+ } else if (!filter.startsWith("(") && !filter.endsWith(")")) {
+ filter = "(&(" + Constants.OBJECTCLASS + "=" + path + ")(" + filter + "))";
+ } else {
+ filter = "(&(" + Constants.OBJECTCLASS + "=" + path + ")" + filter + ")";
+ }
+ dirs.put(ServiceNamespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
+ reqList.add(new RequirementImpl(
+ resource,
+ ServiceNamespace.SERVICE_NAMESPACE,
+ dirs,
+ Collections.<String, Object>emptyMap(),
+ SimpleFilter.parse(filter)));
+ }
+ }
+ return reqList;
+ } catch (Exception ex) {
+ throw new BundleException("Error creating requirement: " + ex, ex);
+ }
+ }
+
+ private static List<Requirement> convertImports(List<ParsedHeaderClause> clauses, Resource resource) {
+ // Now convert generic header clauses into requirements.
+ List<Requirement> reqList = new ArrayList<Requirement>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ // Prepend the package name to the array of attributes.
+ Map<String, Object> attrs = clause.attrs;
+ // Note that we use a linked hash map here to ensure the
+ // package attribute is first, which will make indexing
+ // more efficient.
+ // TODO: OSGi R4.3 - This is ordering is kind of hacky.
+ // Prepend the package name to the array of attributes.
+ Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+ // We want this first from an indexing perspective.
+ newAttrs.put(BundleRevision.PACKAGE_NAMESPACE, path);
+ newAttrs.putAll(attrs);
+ // But we need to put it again to make sure it wasn't overwritten.
+ newAttrs.put(BundleRevision.PACKAGE_NAMESPACE, path);
+
+ // Create filter now so we can inject filter directive.
+ SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+ // Inject filter directive.
+ // TODO: OSGi R4.3 - Can we insert this on demand somehow?
+ Map<String, String> dirs = clause.dirs;
+ Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+ newDirs.putAll(dirs);
+ newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
+
+ // Create package requirement and add to requirement list.
+ reqList.add(
+ new RequirementImpl(
+ resource,
+ BundleRevision.PACKAGE_NAMESPACE,
+ newDirs,
+ Collections.<String, Object>emptyMap(),
+ sf));
+ }
+ }
+
+ return reqList;
+ }
+
+ @SuppressWarnings( "deprecation" )
+ private static List<ParsedHeaderClause> normalizeDynamicImportClauses(
+ List<ParsedHeaderClause> clauses)
+ throws BundleException {
+ // Verify that the values are equals if the package specifies
+ // both version and specification-version attributes.
+ for (ParsedHeaderClause clause : clauses) {
+ // Add the resolution directive to indicate that these are
+ // dynamic imports.
+ clause.dirs.put(Constants.RESOLUTION_DIRECTIVE, RESOLUTION_DYNAMIC);
+
+ // Check for "version" and "specification-version" attributes
+ // and verify they are the same if both are specified.
+ Object v = clause.attrs.get(Constants.VERSION_ATTRIBUTE);
+ Object sv = clause.attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if ((v != null) && (sv != null)) {
+ // Verify they are equal.
+ if (!((String) v).trim().equals(((String) sv).trim())) {
+ throw new IllegalArgumentException(
+ "Both version and specification-version are specified, but they are not equal.");
+ }
+ }
+
+ // Ensure that only the "version" attribute is used and convert
+ // it to the VersionRange type.
+ if ((v != null) || (sv != null)) {
+ clause.attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+ v = (v == null) ? sv : v;
+ clause.attrs.put(Constants.VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // If bundle version is specified, then convert its type to VersionRange.
+ v = clause.attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (v != null) {
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, VersionRange.parseVersionRange(v.toString()));
+ }
+
+ // Dynamic imports can have duplicates, so verify that java.*
+ // packages are not imported.
+ for (String pkgName : clause.paths) {
+ if (pkgName.startsWith("java.")) {
+ throw new BundleException("Dynamically importing java.* packages not allowed: " + pkgName);
+ } else if (!pkgName.equals("*") && pkgName.endsWith("*") && !pkgName.endsWith(".*")) {
+ throw new BundleException("Partial package name wild carding is not allowed: " + pkgName);
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<ParsedHeaderClause> normalizeRequireCapabilityClauses(
+ List<ParsedHeaderClause> clauses)
+ throws BundleException {
+
+ return clauses;
+ }
+
+ private static List<ParsedHeaderClause> normalizeProvideCapabilityClauses(
+ List<ParsedHeaderClause> clauses)
+ throws BundleException
+ {
+
+ // Convert attributes into specified types.
+ for (ParsedHeaderClause clause : clauses)
+ {
+ for (Map.Entry<String, String> entry : clause.types.entrySet())
+ {
+ String type = entry.getValue();
+ if (!type.equals("String"))
+ {
+ if (type.equals("Double"))
+ {
+ clause.attrs.put(
+ entry.getKey(),
+ new Double(clause.attrs.get(entry.getKey()).toString().trim()));
+ }
+ else if (type.equals("Version"))
+ {
+ clause.attrs.put(
+ entry.getKey(),
+ new Version(clause.attrs.get(entry.getKey()).toString().trim()));
+ }
+ else if (type.equals("Long"))
+ {
+ clause.attrs.put(
+ entry.getKey(),
+ new Long(clause.attrs.get(entry.getKey()).toString().trim()));
+ }
+ else if (type.startsWith("List"))
+ {
+ int startIdx = type.indexOf('<');
+ int endIdx = type.indexOf('>');
+ if (((startIdx > 0) && (endIdx <= startIdx))
+ || ((startIdx < 0) && (endIdx > 0)))
+ {
+ throw new BundleException(
+ "Invalid Provide-Capability attribute list type for '"
+ + entry.getKey()
+ + "' : "
+ + type);
+ }
+
+ String listType = "String";
+ if (endIdx > startIdx)
+ {
+ listType = type.substring(startIdx + 1, endIdx).trim();
+ }
+
+ List<String> tokens = parseDelimitedString(
+ clause.attrs.get(entry.getKey()).toString(), ",", false);
+ List<Object> values = new ArrayList<Object>(tokens.size());
+ for (String token : tokens)
+ {
+ if (listType.equals("String"))
+ {
+ values.add(token);
+ }
+ else if (listType.equals("Double"))
+ {
+ values.add(new Double(token.trim()));
+ }
+ else if (listType.equals("Version"))
+ {
+ values.add(new Version(token.trim()));
+ }
+ else if (listType.equals("Long"))
+ {
+ values.add(new Long(token.trim()));
+ }
+ else
+ {
+ throw new BundleException(
+ "Unknown Provide-Capability attribute list type for '"
+ + entry.getKey()
+ + "' : "
+ + type);
+ }
+ }
+ clause.attrs.put(
+ entry.getKey(),
+ values);
+ }
+ else
+ {
+ throw new BundleException(
+ "Unknown Provide-Capability attribute type for '"
+ + entry.getKey()
+ + "' : "
+ + type);
+ }
+ }
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<Requirement> convertRequireCapabilities(
+ List<ParsedHeaderClause> clauses, Resource resource)
+ throws BundleException {
+ // Now convert generic header clauses into requirements.
+ List<Requirement> reqList = new ArrayList<Requirement>();
+ for (ParsedHeaderClause clause : clauses) {
+ try {
+ String filterStr = clause.dirs.get(Constants.FILTER_DIRECTIVE);
+ SimpleFilter sf = (filterStr != null)
+ ? SimpleFilter.parse(filterStr)
+ : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ for (String path : clause.paths) {
+ // Create requirement and add to requirement list.
+ reqList.add(new RequirementImpl(
+ resource, path, clause.dirs, clause.attrs, sf));
+ }
+ } catch (Exception ex) {
+ throw new BundleException("Error creating requirement: " + ex, ex);
+ }
+ }
+
+ return reqList;
+ }
+
+ private static List<Capability> convertProvideCapabilities(
+ List<ParsedHeaderClause> clauses, Resource resource)
+ throws BundleException {
+ List<Capability> capList = new ArrayList<Capability>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ if (path.startsWith("osgi.wiring.")) {
+// throw new BundleException("Manifest cannot use Provide-Capability for '" + path + "' namespace.");
+ }
+
+ // Create package capability and add to capability list.
+ capList.add(new CapabilityImpl(resource, path, clause.dirs, clause.attrs));
+ }
+ }
+
+ return capList;
+ }
+
+ @SuppressWarnings( "deprecation" )
+ private static List<ParsedHeaderClause> normalizeExportClauses(
+ List<ParsedHeaderClause> clauses,
+ String bsn, Version bv)
+ throws BundleException {
+ // Verify that "java.*" packages are not exported.
+ for (ParsedHeaderClause clause : clauses) {
+ // Verify that the named package has not already been declared.
+ for (String pkgName : clause.paths) {
+ // Verify that java.* packages are not exported.
+ if (pkgName.startsWith("java.")) {
+ throw new BundleException("Exporting java.* packages not allowed: " + pkgName);
+ }
+ // The character "." has no meaning in the OSGi spec except
+ // when placed on the bundle class path. Some people, however,
+ // mistakenly think it means the default package when imported
+ // or exported. This is not correct. It is invalid.
+ else if (pkgName.equals(".")) {
+ throw new BundleException("Exporing '.' is invalid.");
+ }
+ // Make sure a package name was specified.
+ else if (pkgName.length() == 0) {
+ throw new BundleException("Exported package names cannot be zero length.");
+ }
+ }
+
+ // Check for "version" and "specification-version" attributes
+ // and verify they are the same if both are specified.
+ Object v = clause.attrs.get(Constants.VERSION_ATTRIBUTE);
+ Object sv = clause.attrs.get(Constants.PACKAGE_SPECIFICATION_VERSION);
+ if ((v != null) && (sv != null)) {
+ // Verify they are equal.
+ if (!((String) v).trim().equals(((String) sv).trim())) {
+ throw new IllegalArgumentException("Both version and specification-version are specified, but they are not equal.");
+ }
+ }
+
+ // Always add the default version if not specified.
+ if ((v == null) && (sv == null)) {
+ v = Version.emptyVersion;
+ }
+
+ // Ensure that only the "version" attribute is used and convert
+ // it to the appropriate type.
+ if ((v != null) || (sv != null)) {
+ // Convert version attribute to type Version.
+ clause.attrs.remove(Constants.PACKAGE_SPECIFICATION_VERSION);
+ v = (v == null) ? sv : v;
+ clause.attrs.put(Constants.VERSION_ATTRIBUTE, Version.parseVersion(v.toString()));
+ }
+
+ // Find symbolic name and version attribute, if present.
+ if (clause.attrs.containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE)
+ || clause.attrs.containsKey(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)) {
+ throw new BundleException("Exports must not specify bundle symbolic name or bundle version.");
+ }
+
+ // Now that we know that there are no bundle symbolic name and version
+ // attributes, add them since the spec says they are there implicitly.
+ clause.attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, bsn);
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bv);
+ }
+
+ return clauses;
+ }
+
+ private static List<Capability> convertExports(
+ List<ParsedHeaderClause> clauses, Resource resource) {
+ List<Capability> capList = new ArrayList<Capability>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String pkgName : clause.paths) {
+ // Prepend the package name to the array of attributes.
+ Map<String, Object> attrs = clause.attrs;
+ Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
+ newAttrs.putAll(attrs);
+ newAttrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
+
+ // Create package capability and add to capability list.
+ capList.add(new CapabilityImpl(resource, BundleRevision.PACKAGE_NAMESPACE, clause.dirs, newAttrs));
+ }
+ }
+
+ return capList;
+ }
+
+ private static String getManifestVersion(Map<String, String> headerMap) {
+ String manifestVersion = headerMap.get(Constants.BUNDLE_MANIFESTVERSION);
+ return (manifestVersion == null) ? "1" : manifestVersion.trim();
+ }
+
+ private static List<ParsedHeaderClause> calculateImplicitImports(
+ List<BundleCapability> exports, List<ParsedHeaderClause> imports)
+ throws BundleException {
+ List<ParsedHeaderClause> clauseList = new ArrayList<ParsedHeaderClause>();
+
+ // Since all R3 exports imply an import, add a corresponding
+ // requirement for each existing export capability. Do not
+ // duplicate imports.
+ Map<String, String> map = new HashMap<String, String>();
+ // Add existing imports.
+ for (ParsedHeaderClause anImport : imports) {
+ for (int pathIdx = 0; pathIdx < anImport.paths.size(); pathIdx++) {
+ map.put(anImport.paths.get(pathIdx), anImport.paths.get(pathIdx));
+ }
+ }
+ // Add import requirement for each export capability.
+ for (BundleCapability export : exports) {
+ if (map.get(export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString()) == null) {
+ // Convert Version to VersionRange.
+ Object version = export.getAttributes().get(Constants.VERSION_ATTRIBUTE);
+ ParsedHeaderClause clause = new ParsedHeaderClause();
+ if (version != null) {
+ clause.attrs.put(Constants.VERSION_ATTRIBUTE, VersionRange.parseVersionRange(version.toString()));
+ }
+ clause.paths.add((String) export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+ clauseList.add(clause);
+ }
+ }
+
+ return clauseList;
+ }
+
+ private static List<Capability> calculateImplicitUses(
+ List<Capability> exports, List<ParsedHeaderClause> imports)
+ throws BundleException {
+ // Add a "uses" directive onto each export of R3 bundles
+ // that references every other import (which will include
+ // exports, since export implies import); this is
+ // necessary since R3 bundles assumed a single class space,
+ // but R4 allows for multiple class spaces.
+ String usesValue = "";
+ for (ParsedHeaderClause anImport : imports) {
+ for (int pathIdx = 0; pathIdx < anImport.paths.size(); pathIdx++) {
+ usesValue = usesValue
+ + ((usesValue.length() > 0) ? "," : "")
+ + anImport.paths.get(pathIdx);
+ }
+ }
+ for (int i = 0; i < exports.size(); i++) {
+ Map<String, String> dirs = new HashMap<String, String>(1);
+ dirs.put(Constants.USES_DIRECTIVE, usesValue);
+ exports.set(i, new CapabilityImpl(
+ exports.get(i).getResource(),
+ BundleRevision.PACKAGE_NAMESPACE,
+ dirs,
+ exports.get(i).getAttributes()));
+ }
+
+ return exports;
+ }
+
+ private static ParsedHeaderClause parseBundleSymbolicName(Map<String, String> headerMap)
+ throws BundleException {
+ List<ParsedHeaderClause> clauses = parseStandardHeader(headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ if (clauses.size() > 0) {
+ if (clauses.size() > 1 || clauses.get(0).paths.size() > 1) {
+ throw new BundleException("Cannot have multiple symbolic names: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ }
+
+ // Get bundle version.
+ Version bundleVersion = Version.emptyVersion;
+ if (headerMap.get(Constants.BUNDLE_VERSION) != null) {
+ bundleVersion = Version.parseVersion(headerMap.get(Constants.BUNDLE_VERSION));
+ }
+
+ // Create a require capability and return it.
+ ParsedHeaderClause clause = clauses.get(0);
+ String symName = clause.paths.get(0);
+ clause.attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
+ return clause;
+ }
+
+ return null;
+ }
+
+ private static List<RequirementImpl> parseFragmentHost(
+ Resource resource, Map<String, String> headerMap)
+ throws BundleException {
+ List<RequirementImpl> reqs = new ArrayList<RequirementImpl>();
+
+ List<ParsedHeaderClause> clauses = parseStandardHeader(headerMap.get(Constants.FRAGMENT_HOST));
+ if (clauses.size() > 0) {
+ // Make sure that only one fragment host symbolic name is specified.
+ if (clauses.size() > 1 || clauses.get(0).paths.size() > 1) {
+ throw new BundleException("Fragments cannot have multiple hosts: " + headerMap.get(Constants.FRAGMENT_HOST));
+ }
+
+ // If the bundle-version attribute is specified, then convert
+ // it to the proper type.
+ Object value = clauses.get(0).attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ value = (value == null) ? "0.0.0" : value;
+ clauses.get(0).attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, VersionRange.parseVersionRange(value.toString()));
+
+ // Note that we use a linked hash map here to ensure the
+ // host symbolic name is first, which will make indexing
+ // more efficient.
+ // TODO: OSGi R4.3 - This is ordering is kind of hacky.
+ // Prepend the host symbolic name to the map of attributes.
+ Map<String, Object> attrs = clauses.get(0).attrs;
+ Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+ // We want this first from an indexing perspective.
+ newAttrs.put(BundleRevision.HOST_NAMESPACE, clauses.get(0).paths.get(0));
+ newAttrs.putAll(attrs);
+ // But we need to put it again to make sure it wasn't overwritten.
+ newAttrs.put(BundleRevision.HOST_NAMESPACE, clauses.get(0).paths.get(0));
+
+ // Create filter now so we can inject filter directive.
+ SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+ // Inject filter directive.
+ // TODO: OSGi R4.3 - Can we insert this on demand somehow?
+ Map<String, String> dirs = clauses.get(0).dirs;
+ Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+ newDirs.putAll(dirs);
+ newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
+
+ reqs.add(new RequirementImpl(
+ resource, BundleRevision.HOST_NAMESPACE,
+ newDirs,
+ newAttrs));
+ }
+
+ return reqs;
+ }
+
+ private static List<ParsedHeaderClause> normalizeRequireClauses(List<ParsedHeaderClause> clauses) {
+ // Convert bundle version attribute to VersionRange type.
+ for (ParsedHeaderClause clause : clauses) {
+ Object value = clause.attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+ if (value != null) {
+ clause.attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, VersionRange.parseVersionRange(value.toString()));
+ }
+ }
+
+ return clauses;
+ }
+
+ private static List<Requirement> convertRequires(List<ParsedHeaderClause> clauses, Resource resource) {
+ List<Requirement> reqList = new ArrayList<Requirement>();
+ for (ParsedHeaderClause clause : clauses) {
+ for (String path : clause.paths) {
+ // Prepend the bundle symbolic name to the array of attributes.
+ Map<String, Object> attrs = clause.attrs;
+ // Note that we use a linked hash map here to ensure the
+ // symbolic name attribute is first, which will make indexing
+ // more efficient.
+ // TODO: OSGi R4.3 - This is ordering is kind of hacky.
+ // Prepend the symbolic name to the array of attributes.
+ Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+ // We want this first from an indexing perspective.
+ newAttrs.put(BundleRevision.BUNDLE_NAMESPACE, path);
+ newAttrs.putAll(attrs);
+ // But we need to put it again to make sure it wasn't overwritten.
+ newAttrs.put(BundleRevision.BUNDLE_NAMESPACE, path);
+
+ // Create filter now so we can inject filter directive.
+ SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+ // Inject filter directive.
+ // TODO: OSGi R4.3 - Can we insert this on demand somehow?
+ Map<String, String> dirs = clause.dirs;
+ Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+ newDirs.putAll(dirs);
+ newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
+
+ // Create package requirement and add to requirement list.
+ reqList.add(new RequirementImpl(resource, BundleRevision.BUNDLE_NAMESPACE, newDirs, newAttrs));
+ }
+ }
+
+ return reqList;
+ }
+
+ private static final char EOF = (char) -1;
+
+ private static char charAt(int pos, String headers, int length)
+ {
+ if (pos >= length)
+ {
+ return EOF;
+ }
+ return headers.charAt(pos);
+ }
+
+ private static final int CLAUSE_START = 0;
+ private static final int PARAMETER_START = 1;
+ private static final int KEY = 2;
+ private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4;
+ private static final int ARGUMENT = 8;
+ private static final int VALUE = 16;
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static List<ParsedHeaderClause> parseStandardHeader(String header)
+ {
+ List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>();
+ if (header == null)
+ {
+ return clauses;
+ }
+ ParsedHeaderClause clause = null;
+ String key = null;
+ Map targetMap = null;
+ int state = CLAUSE_START;
+ int currentPosition = 0;
+ int startPosition = 0;
+ int length = header.length();
+ boolean quoted = false;
+ boolean escaped = false;
+
+ char currentChar = EOF;
+ do
+ {
+ currentChar = charAt(currentPosition, header, length);
+ switch (state)
+ {
+ case CLAUSE_START:
+ clause = new ParsedHeaderClause();
+ clauses.add(clause);
+ state = PARAMETER_START;
+ case PARAMETER_START:
+ startPosition = currentPosition;
+ state = KEY;
+ case KEY:
+ switch (currentChar)
+ {
+ case ':':
+ case '=':
+ key = header.substring(startPosition, currentPosition).trim();
+ startPosition = currentPosition + 1;
+ targetMap = clause.attrs;
+ state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT;
+ break;
+ case EOF:
+ case ',':
+ case ';':
+ clause.paths.add(header.substring(startPosition, currentPosition).trim());
+ state = currentChar == ',' ? CLAUSE_START : PARAMETER_START;
+ break;
+ default:
+ break;
+ }
+ currentPosition++;
+ break;
+ case DIRECTIVE_OR_TYPEDATTRIBUTE:
+ switch(currentChar)
+ {
+ case '=':
+ if (startPosition != currentPosition)
+ {
+ clause.types.put(key, header.substring(startPosition, currentPosition).trim());
+ }
+ else
+ {
+ targetMap = clause.dirs;
+ }
+ state = ARGUMENT;
+ startPosition = currentPosition + 1;
+ break;
+ default:
+ break;
+ }
+ currentPosition++;
+ break;
+ case ARGUMENT:
+ if (currentChar == '\"')
+ {
+ quoted = true;
+ currentPosition++;
+ }
+ else
+ {
+ quoted = false;
+ }
+ if (!Character.isWhitespace(currentChar)) {
+ state = VALUE;
+ }
+ else {
+ currentPosition++;
+ }
+ break;
+ case VALUE:
+ if (escaped)
+ {
+ escaped = false;
+ }
+ else
+ {
+ if (currentChar == '\\' )
+ {
+ escaped = true;
+ }
+ else if (quoted && currentChar == '\"')
+ {
+ quoted = false;
+ }
+ else if (!quoted)
+ {
+ String value = null;
+ switch(currentChar)
+ {
+ case EOF:
+ case ';':
+ case ',':
+ value = header.substring(startPosition, currentPosition).trim();
+ if (value.startsWith("\"") && value.endsWith("\""))
+ {
+ value = value.substring(1, value.length() - 1);
+ }
+ if (targetMap.put(key, value) != null)
+ {
+ throw new IllegalArgumentException(
+ "Duplicate '" + key + "' in: " + header);
+ }
+ state = currentChar == ';' ? PARAMETER_START : CLAUSE_START;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ currentPosition++;
+ break;
+ default:
+ break;
+ }
+ } while ( currentChar != EOF);
+
+ if (state > PARAMETER_START)
+ {
+ throw new IllegalArgumentException("Unable to parse header: " + header);
+ }
+ return clauses;
+ }
+
+ public static List<String> parseDelimitedString(String value, String delim)
+ {
+ return parseDelimitedString(value, delim, true);
+ }
+
+ /**
+ * Parses delimited string and returns an array containing the tokens. This
+ * parser obeys quotes, so the delimiter character will be ignored if it is
+ * inside of a quote. This method assumes that the quote character is not
+ * included in the set of delimiter characters.
+ * @param value the delimited string to parse.
+ * @param delim the characters delimiting the tokens.
+ * @return a list of string or an empty list if there are none.
+ **/
+ public static List<String> parseDelimitedString(String value, String delim, boolean trim)
+ {
+ if (value == null)
+ {
+ value = "";
+ }
+
+ List<String> list = new ArrayList();
+
+ int CHAR = 1;
+ int DELIMITER = 2;
+ int STARTQUOTE = 4;
+ int ENDQUOTE = 8;
+
+ StringBuffer sb = new StringBuffer();
+
+ int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+ boolean isEscaped = false;
+ for (int i = 0; i < value.length(); i++)
+ {
+ char c = value.charAt(i);
+
+ boolean isDelimiter = (delim.indexOf(c) >= 0);
+
+ if (!isEscaped && (c == '\\'))
+ {
+ isEscaped = true;
+ continue;
+ }
+
+ if (isEscaped)
+ {
+ sb.append(c);
+ }
+ else if (isDelimiter && ((expecting & DELIMITER) > 0))
+ {
+ if (trim)
+ {
+ list.add(sb.toString().trim());
+ }
+ else
+ {
+ list.add(sb.toString());
+ }
+ sb.delete(0, sb.length());
+ expecting = (CHAR | DELIMITER | STARTQUOTE);
+ }
+ else if ((c == '"') && ((expecting & STARTQUOTE) > 0))
+ {
+ sb.append(c);
+ expecting = CHAR | ENDQUOTE;
+ }
+ else if ((c == '"') && ((expecting & ENDQUOTE) > 0))
+ {
+ sb.append(c);
+ expecting = (CHAR | STARTQUOTE | DELIMITER);
+ }
+ else if ((expecting & CHAR) > 0)
+ {
+ sb.append(c);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid delimited string: " + value);
+ }
+
+ isEscaped = false;
+ }
+
+ if (sb.length() > 0)
+ {
+ if (trim)
+ {
+ list.add(sb.toString().trim());
+ }
+ else
+ {
+ list.add(sb.toString());
+ }
+ }
+
+ return list;
+ }
+
+
+ static class ParsedHeaderClause {
+ public final List<String> paths = new ArrayList<String>();
+ public final Map<String, String> dirs = new LinkedHashMap<String, String>();
+ public final Map<String, Object> attrs = new LinkedHashMap<String, Object>();
+ public final Map<String, String> types = new LinkedHashMap<String, String>();
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
new file mode 100644
index 0000000..18e0dc3
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
@@ -0,0 +1,110 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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;
+
+/**
+ */
+public class ResourceImpl implements Resource {
+
+ private final List<Capability> m_caps;
+ private final List<Requirement> m_reqs;
+
+ public ResourceImpl(String name, Version version) {
+ this(name, IdentityNamespace.TYPE_BUNDLE, version);
+ }
+
+ public ResourceImpl(String name, String type, Version version)
+ {
+ m_caps = new ArrayList<Capability>();
+ m_caps.add(0, new IdentityCapability(this, name, type, version));
+ m_reqs = new ArrayList<Requirement>();
+ }
+
+ public void addCapability(Capability capability) {
+ assert capability.getResource() == this;
+ m_caps.add(capability);
+ }
+
+ public void addCapabilities(Iterable<? extends Capability> capabilities) {
+ for (Capability cap : capabilities) {
+ addCapability(cap);
+ }
+ }
+
+ public void addRequirement(Requirement requirement) {
+ assert requirement.getResource() == this;
+ m_reqs.add(requirement);
+ }
+
+ public void addRequirements(Iterable<? extends Requirement> requirements) {
+ for (Requirement req : requirements) {
+ addRequirement(req);
+ }
+ }
+
+ public List<Capability> getCapabilities(String namespace)
+ {
+ List<Capability> result = m_caps;
+ if (namespace != null)
+ {
+ result = new ArrayList<Capability>();
+ for (Capability cap : m_caps)
+ {
+ if (cap.getNamespace().equals(namespace))
+ {
+ result.add(cap);
+ }
+ }
+ }
+ return result;
+ }
+
+ public List<Requirement> getRequirements(String namespace)
+ {
+ List<Requirement> result = m_reqs;
+ if (namespace != null)
+ {
+ result = new ArrayList<Requirement>();
+ for (Requirement req : m_reqs)
+ {
+ if (req.getNamespace().equals(namespace))
+ {
+ result.add(req);
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ Capability cap = getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0);
+ return cap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE) + "/"
+ + cap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ServiceNamespace.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ServiceNamespace.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ServiceNamespace.java
new file mode 100644
index 0000000..4fe3bf8
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ServiceNamespace.java
@@ -0,0 +1,30 @@
+/*
+ * 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.resolver;
+
+import org.osgi.resource.Namespace;
+
+/**
+ */
+public final class ServiceNamespace extends Namespace {
+
+ public static final String SERVICE_NAMESPACE = "service-reference";
+
+ private ServiceNamespace() {
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
new file mode 100644
index 0000000..ae10441
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/SimpleFilter.java
@@ -0,0 +1,649 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.felix.utils.version.VersionRange;
+
+public class SimpleFilter
+{
+ public static final int MATCH_ALL = 0;
+ public static final int AND = 1;
+ public static final int OR = 2;
+ public static final int NOT = 3;
+ public static final int EQ = 4;
+ public static final int LTE = 5;
+ public static final int GTE = 6;
+ public static final int SUBSTRING = 7;
+ public static final int PRESENT = 8;
+ public static final int APPROX = 9;
+
+ private final String m_name;
+ private final Object m_value;
+ private final int m_op;
+
+ public SimpleFilter(String attr, Object value, int op)
+ {
+ m_name = attr;
+ m_value = value;
+ m_op = op;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public Object getValue()
+ {
+ return m_value;
+ }
+
+ public int getOperation()
+ {
+ return m_op;
+ }
+
+ public String toString()
+ {
+ String s = null;
+ switch (m_op)
+ {
+ case AND:
+ s = "(&" + toString((List) m_value) + ")";
+ break;
+ case OR:
+ s = "(|" + toString((List) m_value) + ")";
+ break;
+ case NOT:
+ s = "(!" + toString((List) m_value) + ")";
+ break;
+ case EQ:
+ s = "(" + m_name + "=" + toEncodedString(m_value) + ")";
+ break;
+ case LTE:
+ s = "(" + m_name + "<=" + toEncodedString(m_value) + ")";
+ break;
+ case GTE:
+ s = "(" + m_name + ">=" + toEncodedString(m_value) + ")";
+ break;
+ case SUBSTRING:
+ s = "(" + m_name + "=" + unparseSubstring((List<String>) m_value) + ")";
+ break;
+ case PRESENT:
+ s = "(" + m_name + "=*)";
+ break;
+ case APPROX:
+ s = "(" + m_name + "~=" + toEncodedString(m_value) + ")";
+ break;
+ case MATCH_ALL:
+ s = "(*)";
+ break;
+ }
+ return s;
+ }
+
+ private static String toString(List list)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < list.size(); i++)
+ {
+ sb.append(list.get(i).toString());
+ }
+ return sb.toString();
+ }
+
+ private static String toDecodedString(String s, int startIdx, int endIdx)
+ {
+ StringBuffer sb = new StringBuffer(endIdx - startIdx);
+ boolean escaped = false;
+ for (int i = 0; i < (endIdx - startIdx); i++)
+ {
+ char c = s.charAt(startIdx + i);
+ if (!escaped && (c == '\\'))
+ {
+ escaped = true;
+ }
+ else
+ {
+ escaped = false;
+ sb.append(c);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private static String toEncodedString(Object o)
+ {
+ if (o instanceof String)
+ {
+ String s = (String) o;
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < s.length(); i++)
+ {
+ char c = s.charAt(i);
+ if ((c == '\\') || (c == '(') || (c == ')') || (c == '*'))
+ {
+ sb.append('\\');
+ }
+ sb.append(c);
+ }
+
+ o = sb.toString();
+ }
+
+ return o.toString();
+ }
+
+ public static SimpleFilter parse(String filter)
+ {
+ int idx = skipWhitespace(filter, 0);
+
+ if ((filter == null) || (filter.length() == 0) || (idx >= filter.length()))
+ {
+ throw new IllegalArgumentException("Null or empty filter.");
+ }
+ else if (filter.charAt(idx) != '(')
+ {
+ throw new IllegalArgumentException("Missing opening parenthesis: " + filter);
+ }
+
+ SimpleFilter sf = null;
+ List stack = new ArrayList();
+ boolean isEscaped = false;
+ while (idx < filter.length())
+ {
+ if (sf != null)
+ {
+ throw new IllegalArgumentException(
+ "Only one top-level operation allowed: " + filter);
+ }
+
+ if (!isEscaped && (filter.charAt(idx) == '('))
+ {
+ // Skip paren and following whitespace.
+ idx = skipWhitespace(filter, idx + 1);
+
+ if (filter.charAt(idx) == '&')
+ {
+ int peek = skipWhitespace(filter, idx + 1);
+ if (filter.charAt(peek) == '(')
+ {
+ idx = peek - 1;
+ stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.AND));
+ }
+ else
+ {
+ stack.add(0, new Integer(idx));
+ }
+ }
+ else if (filter.charAt(idx) == '|')
+ {
+ int peek = skipWhitespace(filter, idx + 1);
+ if (filter.charAt(peek) == '(')
+ {
+ idx = peek - 1;
+ stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.OR));
+ }
+ else
+ {
+ stack.add(0, new Integer(idx));
+ }
+ }
+ else if (filter.charAt(idx) == '!')
+ {
+ int peek = skipWhitespace(filter, idx + 1);
+ if (filter.charAt(peek) == '(')
+ {
+ idx = peek - 1;
+ stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT));
+ }
+ else
+ {
+ stack.add(0, new Integer(idx));
+ }
+ }
+ else
+ {
+ stack.add(0, new Integer(idx));
+ }
+ }
+ else if (!isEscaped && (filter.charAt(idx) == ')'))
+ {
+ Object top = stack.remove(0);
+ if (top instanceof SimpleFilter)
+ {
+ if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
+ {
+ ((List) ((SimpleFilter) stack.get(0)).m_value).add(top);
+ }
+ else
+ {
+ sf = (SimpleFilter) top;
+ }
+ }
+ else if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
+ {
+ ((List) ((SimpleFilter) stack.get(0)).m_value).add(
+ SimpleFilter.subfilter(filter, ((Integer) top).intValue(), idx));
+ }
+ else
+ {
+ sf = SimpleFilter.subfilter(filter, ((Integer) top).intValue(), idx);
+ }
+ }
+ else if (!isEscaped && (filter.charAt(idx) == '\\'))
+ {
+ isEscaped = true;
+ }
+ else
+ {
+ isEscaped = false;
+ }
+
+ idx = skipWhitespace(filter, idx + 1);
+ }
+
+ if (sf == null)
+ {
+ throw new IllegalArgumentException("Missing closing parenthesis: " + filter);
+ }
+
+ return sf;
+ }
+
+ private static SimpleFilter subfilter(String filter, int startIdx, int endIdx)
+ {
+ final String opChars = "=<>~";
+
+ // Determine the ending index of the attribute name.
+ int attrEndIdx = startIdx;
+ for (int i = 0; i < (endIdx - startIdx); i++)
+ {
+ char c = filter.charAt(startIdx + i);
+ if (opChars.indexOf(c) >= 0)
+ {
+ break;
+ }
+ else if (!Character.isWhitespace(c))
+ {
+ attrEndIdx = startIdx + i + 1;
+ }
+ }
+ if (attrEndIdx == startIdx)
+ {
+ throw new IllegalArgumentException(
+ "Missing attribute name: " + filter.substring(startIdx, endIdx));
+ }
+ String attr = filter.substring(startIdx, attrEndIdx);
+
+ // Skip the attribute name and any following whitespace.
+ startIdx = skipWhitespace(filter, attrEndIdx);
+
+ // Determine the operator type.
+ int op = -1;
+ switch (filter.charAt(startIdx))
+ {
+ case '=':
+ op = EQ;
+ startIdx++;
+ break;
+ case '<':
+ if (filter.charAt(startIdx + 1) != '=')
+ {
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+ op = LTE;
+ startIdx += 2;
+ break;
+ case '>':
+ if (filter.charAt(startIdx + 1) != '=')
+ {
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+ op = GTE;
+ startIdx += 2;
+ break;
+ case '~':
+ if (filter.charAt(startIdx + 1) != '=')
+ {
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+ op = APPROX;
+ startIdx += 2;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+
+ // Parse value.
+ Object value = toDecodedString(filter, startIdx, endIdx);
+
+ // Check if the equality comparison is actually a substring
+ // or present operation.
+ if (op == EQ)
+ {
+ String valueStr = filter.substring(startIdx, endIdx);
+ List<String> values = parseSubstring(valueStr);
+ if ((values.size() == 2)
+ && (values.get(0).length() == 0)
+ && (values.get(1).length() == 0))
+ {
+ op = PRESENT;
+ }
+ else if (values.size() > 1)
+ {
+ op = SUBSTRING;
+ value = values;
+ }
+ }
+
+ return new SimpleFilter(attr, value, op);
+ }
+
+ public static List<String> parseSubstring(String value)
+ {
+ List<String> pieces = new ArrayList();
+ StringBuffer ss = new StringBuffer();
+ // int kind = SIMPLE; // assume until proven otherwise
+ boolean wasStar = false; // indicates last piece was a star
+ boolean leftstar = false; // track if the initial piece is a star
+ boolean rightstar = false; // track if the final piece is a star
+
+ int idx = 0;
+
+ // We assume (sub)strings can contain leading and trailing blanks
+ boolean escaped = false;
+ loop: for (;;)
+ {
+ if (idx >= value.length())
+ {
+ if (wasStar)
+ {
+ // insert last piece as "" to handle trailing star
+ rightstar = true;
+ }
+ else
+ {
+ pieces.add(ss.toString());
+ // accumulate the last piece
+ // note that in the case of
+ // (cn=); this might be
+ // the string "" (!=null)
+ }
+ ss.setLength(0);
+ break loop;
+ }
+
+ // Read the next character and account for escapes.
+ char c = value.charAt(idx++);
+ if (!escaped && (c == '*'))
+ {
+ // If we have successive '*' characters, then we can
+ // effectively collapse them by ignoring succeeding ones.
+ if (!wasStar)
+ {
+ if (ss.length() > 0)
+ {
+ pieces.add(ss.toString()); // accumulate the pieces
+ // between '*' occurrences
+ }
+ ss.setLength(0);
+ // if this is a leading star, then track it
+ if (pieces.isEmpty())
+ {
+ leftstar = true;
+ }
+ wasStar = true;
+ }
+ }
+ else if (!escaped && (c == '\\'))
+ {
+ escaped = true;
+ }
+ else
+ {
+ escaped = false;
+ wasStar = false;
+ ss.append(c);
+ }
+ }
+ if (leftstar || rightstar || pieces.size() > 1)
+ {
+ // insert leading and/or trailing "" to anchor ends
+ if (rightstar)
+ {
+ pieces.add("");
+ }
+ if (leftstar)
+ {
+ pieces.add(0, "");
+ }
+ }
+ return pieces;
+ }
+
+ public static String unparseSubstring(List<String> pieces)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < pieces.size(); i++)
+ {
+ if (i > 0)
+ {
+ sb.append("*");
+ }
+ sb.append(toEncodedString(pieces.get(i)));
+ }
+ return sb.toString();
+ }
+
+ public static boolean compareSubstring(List<String> pieces, String s)
+ {
+ // Walk the pieces to match the string
+ // There are implicit stars between each piece,
+ // and the first and last pieces might be "" to anchor the match.
+ // assert (pieces.length > 1)
+ // minimal case is <string>*<string>
+
+ boolean result = true;
+ int len = pieces.size();
+
+ // Special case, if there is only one piece, then
+ // we must perform an equality test.
+ if (len == 1)
+ {
+ return s.equals(pieces.get(0));
+ }
+
+ // Otherwise, check whether the pieces match
+ // the specified string.
+
+ int index = 0;
+
+ loop: for (int i = 0; i < len; i++)
+ {
+ String piece = pieces.get(i);
+
+ // If this is the first piece, then make sure the
+ // string starts with it.
+ if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = false;
+ break loop;
+ }
+ }
+
+ // If this is the last piece, then make sure the
+ // string ends with it.
+ if (i == (len - 1))
+ {
+ if (s.endsWith(piece) && (s.length() >= (index + piece.length())))
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ break loop;
+ }
+
+ // If this is neither the first or last piece, then
+ // make sure the string contains it.
+ if ((i > 0) && (i < (len - 1)))
+ {
+ index = s.indexOf(piece, index);
+ if (index < 0)
+ {
+ result = false;
+ break loop;
+ }
+ }
+
+ // Move string index beyond the matching piece.
+ index += piece.length();
+ }
+
+ return result;
+ }
+
+ private static int skipWhitespace(String s, int startIdx)
+ {
+ int len = s.length();
+ while ((startIdx < len) && Character.isWhitespace(s.charAt(startIdx)))
+ {
+ startIdx++;
+ }
+ return startIdx;
+ }
+
+ /**
+ * Converts a attribute map to a filter. The filter is created by iterating
+ * over the map's entry set. If ordering of attributes is important (e.g.,
+ * for hitting attribute indices), then the map's entry set should iterate
+ * in the desired order. Equality testing is assumed for all attribute types
+ * other than version ranges, which are handled appropriated. If the attribute
+ * map is empty, then a filter that matches anything is returned.
+ * @param attrs Map of attributes to convert to a filter.
+ * @return A filter corresponding to the attributes.
+ */
+ public static SimpleFilter convert(Map<String, Object> attrs)
+ {
+ // Rather than building a filter string to be parsed into a SimpleFilter,
+ // we will just create the parsed SimpleFilter directly.
+
+ List<SimpleFilter> filters = new ArrayList<SimpleFilter>();
+
+ for (Entry<String, Object> entry : attrs.entrySet())
+ {
+ if (entry.getValue() instanceof VersionRange)
+ {
+ VersionRange vr = (VersionRange) entry.getValue();
+ if (!vr.isOpenFloor())
+ {
+ filters.add(
+ new SimpleFilter(
+ entry.getKey(),
+ vr.getFloor().toString(),
+ SimpleFilter.GTE));
+ }
+ else
+ {
+ SimpleFilter not =
+ new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT);
+ ((List) not.getValue()).add(
+ new SimpleFilter(
+ entry.getKey(),
+ vr.getFloor().toString(),
+ SimpleFilter.LTE));
+ filters.add(not);
+ }
+
+ if (vr.getCeiling() != null)
+ {
+ if (!vr.isOpenCeiling())
+ {
+ filters.add(
+ new SimpleFilter(
+ entry.getKey(),
+ vr.getCeiling().toString(),
+ SimpleFilter.LTE));
+ }
+ else
+ {
+ SimpleFilter not =
+ new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT);
+ ((List) not.getValue()).add(
+ new SimpleFilter(
+ entry.getKey(),
+ vr.getCeiling().toString(),
+ SimpleFilter.GTE));
+ filters.add(not);
+ }
+ }
+ }
+ else
+ {
+ List<String> values = SimpleFilter.parseSubstring(entry.getValue().toString());
+ if (values.size() > 1)
+ {
+ filters.add(
+ new SimpleFilter(
+ entry.getKey(),
+ values,
+ SimpleFilter.SUBSTRING));
+ }
+ else
+ {
+ filters.add(
+ new SimpleFilter(
+ entry.getKey(),
+ values.get(0),
+ SimpleFilter.EQ));
+ }
+ }
+ }
+
+ SimpleFilter sf = null;
+
+ if (filters.size() == 1)
+ {
+ sf = filters.get(0);
+ }
+ else if (attrs.size() > 1)
+ {
+ sf = new SimpleFilter(null, filters, SimpleFilter.AND);
+ }
+ else if (filters.isEmpty())
+ {
+ sf = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ }
+
+ return sf;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/Slf4jResolverLog.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/Slf4jResolverLog.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/Slf4jResolverLog.java
new file mode 100644
index 0000000..2f4a1f3
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/Slf4jResolverLog.java
@@ -0,0 +1,49 @@
+/*
+ * 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.resolver;
+
+import org.slf4j.Logger;
+
+/**
+ */
+public class Slf4jResolverLog extends org.apache.felix.resolver.Logger {
+
+ private final Logger logger;
+
+ public Slf4jResolverLog(Logger logger) {
+ super(LOG_DEBUG);
+ this.logger = logger;
+ }
+
+ @Override
+ protected void doLog(int level, String msg, Throwable throwable) {
+ switch (level) {
+ case LOG_ERROR:
+ logger.error(msg, throwable);
+ break;
+ case LOG_WARNING:
+ logger.warn(msg, throwable);
+ break;
+ case LOG_INFO:
+ logger.info(msg, throwable);
+ break;
+ case LOG_DEBUG:
+ logger.debug(msg, throwable);
+ break;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java
new file mode 100644
index 0000000..b5158bf
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/UriNamespace.java
@@ -0,0 +1,47 @@
+/*
+ * 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.resolver;
+
+import java.util.List;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Resource;
+
+/**
+ */
+public final class UriNamespace extends Namespace {
+
+ public static final String URI_NAMESPACE = "karaf.uri";
+
+ public static String getUri(Resource resource)
+ {
+ List<Capability> caps = resource.getCapabilities(null);
+ for (Capability cap : caps)
+ {
+ if (cap.getNamespace().equals(UriNamespace.URI_NAMESPACE))
+ {
+ return cap.getAttributes().get(UriNamespace.URI_NAMESPACE).toString();
+ }
+ }
+ return null;
+ }
+
+
+ private UriNamespace() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/Artifact.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Artifact.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Artifact.java
new file mode 100644
index 0000000..44e9a7c
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Artifact.java
@@ -0,0 +1,56 @@
+/*
+ * 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.service;
+
+import java.net.URI;
+
+/**
+ * Simple abstraction of a maven artifact to avoid external deps
+ */
+public class Artifact {
+ String groupId;
+ String artifactId;
+ String version;
+ String extension;
+ String classifier;
+
+ public Artifact(String coords) {
+ String[] coordsAr = coords.split(":");
+ if (coordsAr.length != 5) {
+ throw new IllegalArgumentException("Maven URL " + coords + " is malformed or not complete");
+ }
+ this.groupId = coordsAr[0];
+ this.artifactId = coordsAr[1];
+ this.version = coordsAr[4];
+ this.extension = coordsAr[2];
+ this.classifier = coordsAr[3];
+ }
+
+ public Artifact(String coords, String version) {
+ this(coords);
+ this.version = version;
+ }
+
+ public URI getMavenUrl(String version) {
+ String uriSt = "mvn:" + this.groupId + "/" + this.artifactId + "/" + version + "/" + this.extension + "/" + this.classifier;
+ try {
+ return new URI(uriSt);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
[10/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilitySet.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilitySet.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilitySet.java
new file mode 100644
index 0000000..4c5656d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilitySet.java
@@ -0,0 +1,612 @@
+/*
+ * 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.resolver;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+
+public class CapabilitySet
+{
+ private final Map<String, Map<Object, Set<Capability>>> m_indices;
+ private final Set<Capability> m_capSet = new HashSet<Capability>();
+
+public void dump()
+{
+ for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
+ {
+ boolean header1 = false;
+ for (Entry<Object, Set<Capability>> entry2 : entry.getValue().entrySet())
+ {
+ boolean header2 = false;
+ for (Capability cap : entry2.getValue())
+ {
+ if (!header1)
+ {
+ System.out.println(entry.getKey() + ":");
+ header1 = true;
+ }
+ if (!header2)
+ {
+ System.out.println(" " + entry2.getKey());
+ header2 = true;
+ }
+ System.out.println(" " + cap);
+ }
+ }
+ }
+}
+
+ public CapabilitySet(List<String> indexProps)
+ {
+ m_indices = new TreeMap<String, Map<Object, Set<Capability>>>();
+ for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++)
+ {
+ m_indices.put(
+ indexProps.get(i), new HashMap<Object, Set<Capability>>());
+ }
+ }
+
+ public void addCapability(Capability cap)
+ {
+ m_capSet.add(cap);
+
+ // Index capability.
+ for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
+ {
+ Object value = cap.getAttributes().get(entry.getKey());
+ if (value != null)
+ {
+ if (value.getClass().isArray())
+ {
+ value = convertArrayToList(value);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (value instanceof Collection)
+ {
+ Collection c = (Collection) value;
+ for (Object o : c)
+ {
+ indexCapability(index, cap, o);
+ }
+ }
+ else
+ {
+ indexCapability(index, cap, value);
+ }
+ }
+ }
+ }
+
+ private void indexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object capValue)
+ {
+ Set<Capability> caps = index.get(capValue);
+ if (caps == null)
+ {
+ caps = new HashSet<Capability>();
+ index.put(capValue, caps);
+ }
+ caps.add(cap);
+ }
+
+ public void removeCapability(Capability cap)
+ {
+ if (m_capSet.remove(cap))
+ {
+ for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
+ {
+ Object value = cap.getAttributes().get(entry.getKey());
+ if (value != null)
+ {
+ if (value.getClass().isArray())
+ {
+ value = convertArrayToList(value);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (value instanceof Collection)
+ {
+ Collection c = (Collection) value;
+ for (Object o : c)
+ {
+ deindexCapability(index, cap, o);
+ }
+ }
+ else
+ {
+ deindexCapability(index, cap, value);
+ }
+ }
+ }
+ }
+ }
+
+ private void deindexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object value)
+ {
+ Set<Capability> caps = index.get(value);
+ if (caps != null)
+ {
+ caps.remove(cap);
+ if (caps.isEmpty())
+ {
+ index.remove(value);
+ }
+ }
+ }
+
+ public Set<Capability> match(SimpleFilter sf, boolean obeyMandatory)
+ {
+ Set<Capability> matches = match(m_capSet, sf);
+ return (obeyMandatory)
+ ? matchMandatory(matches, sf)
+ : matches;
+ }
+
+ private Set<Capability> match(Set<Capability> caps, SimpleFilter sf)
+ {
+ Set<Capability> matches = new HashSet<Capability>();
+
+ if (sf.getOperation() == SimpleFilter.MATCH_ALL)
+ {
+ matches.addAll(caps);
+ }
+ else if (sf.getOperation() == SimpleFilter.AND)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++)
+ {
+ matches = match(caps, sfs.get(i));
+ caps = matches;
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.OR)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; i < sfs.size(); i++)
+ {
+ matches.addAll(match(caps, sfs.get(i)));
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.NOT)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matches.addAll(caps);
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; i < sfs.size(); i++)
+ {
+ matches.removeAll(match(caps, sfs.get(i)));
+ }
+ }
+ else
+ {
+ Map<Object, Set<Capability>> index = m_indices.get(sf.getName());
+ if ((sf.getOperation() == SimpleFilter.EQ) && (index != null))
+ {
+ Set<Capability> existingCaps = index.get(sf.getValue());
+ if (existingCaps != null)
+ {
+ matches.addAll(existingCaps);
+ matches.retainAll(caps);
+ }
+ }
+ else
+ {
+ for (Iterator<Capability> it = caps.iterator(); it.hasNext(); )
+ {
+ Capability cap = it.next();
+ Object lhs = cap.getAttributes().get(sf.getName());
+ if (lhs != null)
+ {
+ if (compare(lhs, sf.getValue(), sf.getOperation()))
+ {
+ matches.add(cap);
+ }
+ }
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ public static boolean matches(Capability cap, SimpleFilter sf)
+ {
+ return matchesInternal(cap, sf) && matchMandatory(cap, sf);
+ }
+
+ private static boolean matchesInternal(Capability cap, SimpleFilter sf)
+ {
+ boolean matched = true;
+
+ if (sf.getOperation() == SimpleFilter.MATCH_ALL)
+ {
+ matched = true;
+ }
+ else if (sf.getOperation() == SimpleFilter.AND)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; matched && (i < sfs.size()); i++)
+ {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.OR)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matched = false;
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; !matched && (i < sfs.size()); i++)
+ {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.NOT)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; i < sfs.size(); i++)
+ {
+ matched = !(matchesInternal(cap, sfs.get(i)));
+ }
+ }
+ else
+ {
+ matched = false;
+ Object lhs = cap.getAttributes().get(sf.getName());
+ if (lhs != null)
+ {
+ matched = compare(lhs, sf.getValue(), sf.getOperation());
+ }
+ }
+
+ return matched;
+ }
+
+ private static Set<Capability> matchMandatory(
+ Set<Capability> caps, SimpleFilter sf)
+ {
+ for (Iterator<Capability> it = caps.iterator(); it.hasNext(); )
+ {
+ Capability cap = it.next();
+ if (!matchMandatory(cap, sf))
+ {
+ it.remove();
+ }
+ }
+ return caps;
+ }
+
+ private static boolean matchMandatory(Capability cap, SimpleFilter sf)
+ {
+ if (cap instanceof CapabilityImpl) {
+ for (Entry<String, Object> entry : cap.getAttributes().entrySet())
+ {
+ if (((CapabilityImpl) cap).isAttributeMandatory(entry.getKey())
+ && !matchMandatoryAttribute(entry.getKey(), sf))
+ {
+ return false;
+ }
+ }
+ } else {
+ String value = cap.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
+ if (value != null) {
+ List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
+ for (Entry<String, Object> entry : cap.getAttributes().entrySet())
+ {
+ if (names.contains(entry.getKey())
+ && !matchMandatoryAttribute(entry.getKey(), sf))
+ {
+ return false;
+ }
+ }
+ }
+
+ }
+ return true;
+ }
+
+ private static boolean matchMandatoryAttribute(String attrName, SimpleFilter sf)
+ {
+ if ((sf.getName() != null) && sf.getName().equals(attrName))
+ {
+ return true;
+ }
+ else if (sf.getOperation() == SimpleFilter.AND)
+ {
+ List list = (List) sf.getValue();
+ for (int i = 0; i < list.size(); i++)
+ {
+ SimpleFilter sf2 = (SimpleFilter) list.get(i);
+ if ((sf2.getName() != null)
+ && sf2.getName().equals(attrName))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static final Class<?>[] STRING_CLASS = new Class[] { String.class };
+
+ private static boolean compare(Object lhs, Object rhsUnknown, int op)
+ {
+ if (lhs == null)
+ {
+ return false;
+ }
+
+ // If this is a PRESENT operation, then just return true immediately
+ // since we wouldn't be here if the attribute wasn't present.
+ if (op == SimpleFilter.PRESENT)
+ {
+ return true;
+ }
+
+ // If the type is comparable, then we can just return the
+ // result immediately.
+ if (lhs instanceof Comparable)
+ {
+ // Spec says SUBSTRING is false for all types other than string.
+ if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
+ {
+ return false;
+ }
+
+ Object rhs;
+ if (op == SimpleFilter.SUBSTRING)
+ {
+ rhs = rhsUnknown;
+ }
+ else
+ {
+ try
+ {
+ rhs = coerceType(lhs, (String) rhsUnknown);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ switch (op)
+ {
+ case SimpleFilter.EQ :
+ try
+ {
+ return (((Comparable) lhs).compareTo(rhs) == 0);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ case SimpleFilter.GTE :
+ try
+ {
+ return (((Comparable) lhs).compareTo(rhs) >= 0);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ case SimpleFilter.LTE :
+ try
+ {
+ return (((Comparable) lhs).compareTo(rhs) <= 0);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ case SimpleFilter.APPROX :
+ return compareApproximate(((Comparable) lhs), rhs);
+ case SimpleFilter.SUBSTRING :
+ return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
+ default:
+ throw new RuntimeException(
+ "Unknown comparison operator: " + op);
+ }
+ }
+ // Booleans do not implement comparable, so special case them.
+ else if (lhs instanceof Boolean)
+ {
+ Object rhs;
+ try
+ {
+ rhs = coerceType(lhs, (String) rhsUnknown);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+
+ switch (op)
+ {
+ case SimpleFilter.EQ :
+ case SimpleFilter.GTE :
+ case SimpleFilter.LTE :
+ case SimpleFilter.APPROX :
+ return (lhs.equals(rhs));
+ default:
+ throw new RuntimeException(
+ "Unknown comparison operator: " + op);
+ }
+ }
+
+ // If the LHS is not a comparable or boolean, check if it is an
+ // array. If so, convert it to a list so we can treat it as a
+ // collection.
+ if (lhs.getClass().isArray())
+ {
+ lhs = convertArrayToList(lhs);
+ }
+
+ // If LHS is a collection, then call compare() on each element
+ // of the collection until a match is found.
+ if (lhs instanceof Collection)
+ {
+ for (Iterator iter = ((Collection) lhs).iterator(); iter.hasNext(); )
+ {
+ if (compare(iter.next(), rhsUnknown, op))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Spec says SUBSTRING is false for all types other than string.
+ if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
+ {
+ return false;
+ }
+
+ // Since we cannot identify the LHS type, then we can only perform
+ // equality comparison.
+ try
+ {
+ return lhs.equals(coerceType(lhs, (String) rhsUnknown));
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ private static boolean compareApproximate(Object lhs, Object rhs)
+ {
+ if (rhs instanceof String)
+ {
+ return removeWhitespace((String) lhs)
+ .equalsIgnoreCase(removeWhitespace((String) rhs));
+ }
+ else if (rhs instanceof Character)
+ {
+ return Character.toLowerCase(((Character) lhs))
+ == Character.toLowerCase(((Character) rhs));
+ }
+ return lhs.equals(rhs);
+ }
+
+ private static String removeWhitespace(String s)
+ {
+ StringBuffer sb = new StringBuffer(s.length());
+ for (int i = 0; i < s.length(); i++)
+ {
+ if (!Character.isWhitespace(s.charAt(i)))
+ {
+ sb.append(s.charAt(i));
+ }
+ }
+ return sb.toString();
+ }
+
+ private static Object coerceType(Object lhs, String rhsString) throws Exception
+ {
+ // If the LHS expects a string, then we can just return
+ // the RHS since it is a string.
+ if (lhs.getClass() == rhsString.getClass())
+ {
+ return rhsString;
+ }
+
+ // Try to convert the RHS type to the LHS type by using
+ // the string constructor of the LHS class, if it has one.
+ Object rhs = null;
+ try
+ {
+ // The Character class is a special case, since its constructor
+ // does not take a string, so handle it separately.
+ if (lhs instanceof Character)
+ {
+ rhs = new Character(rhsString.charAt(0));
+ }
+ else
+ {
+ // Spec says we should trim number types.
+ if ((lhs instanceof Number) || (lhs instanceof Boolean))
+ {
+ rhsString = rhsString.trim();
+ }
+ Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
+ ctor.setAccessible(true);
+ rhs = ctor.newInstance(new Object[] { rhsString });
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception(
+ "Could not instantiate class "
+ + lhs.getClass().getName()
+ + " from string constructor with argument '"
+ + rhsString + "' because " + ex);
+ }
+
+ return rhs;
+ }
+
+ /**
+ * This is an ugly utility method to convert an array of primitives
+ * to an array of primitive wrapper objects. This method simplifies
+ * processing LDAP filters since the special case of primitive arrays
+ * can be ignored.
+ * @param array An array of primitive types.
+ * @return An corresponding array using pritive wrapper objects.
+ **/
+ private static List convertArrayToList(Object array)
+ {
+ int len = Array.getLength(array);
+ List list = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ list.add(Array.get(array, i));
+ }
+ return list;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java
new file mode 100644
index 0000000..e211618
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureNamespace.java
@@ -0,0 +1,72 @@
+/*
+ * 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.resolver;
+
+import java.util.List;
+
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Resource;
+
+/**
+ */
+public final class FeatureNamespace extends Namespace {
+
+ public static final String FEATURE_NAMESPACE = "karaf.feature";
+
+ public static final String CAPABILITY_VERSION_ATTRIBUTE = "version";
+
+ /**
+ * The attribute value identifying the resource
+ * {@link org.osgi.framework.namespace.IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE type} as an OSGi bundle.
+ *
+ * @see org.osgi.framework.namespace.IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE
+ */
+ public static final String TYPE_FEATURE = "karaf.feature";
+
+ public static String getName(Resource resource)
+ {
+ List<Capability> caps = resource.getCapabilities(null);
+ for (Capability cap : caps)
+ {
+ if (cap.getNamespace().equals(FEATURE_NAMESPACE))
+ {
+ return cap.getAttributes().get(FEATURE_NAMESPACE).toString();
+ }
+ }
+ return null;
+ }
+
+ public static Version getVersion(Resource resource)
+ {
+ List<Capability> caps = resource.getCapabilities(null);
+ for (Capability cap : caps)
+ {
+ if (cap.getNamespace().equals(FEATURE_NAMESPACE))
+ {
+ return (Version)
+ cap.getAttributes().get(CAPABILITY_VERSION_ATTRIBUTE);
+ }
+ }
+ return null;
+ }
+
+
+ private FeatureNamespace() {
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
new file mode 100644
index 0000000..7d1671c
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
@@ -0,0 +1,101 @@
+/*
+ * 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.resolver;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.Dependency;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.internal.util.Macro;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+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;
+
+/**
+*/
+public class FeatureResource extends ResourceImpl {
+
+ private final Feature feature;
+
+ public static Resource build(Feature feature, String featureRange, Map<String, Resource> locToRes) throws BundleException {
+ FeatureResource resource = new FeatureResource(feature);
+ Map<String, String> dirs = new HashMap<String, String>();
+ Map<String, Object> attrs = new HashMap<String, Object>();
+ attrs.put(FeatureNamespace.FEATURE_NAMESPACE, feature.getName());
+ attrs.put(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE, VersionTable.getVersion(feature.getVersion()));
+ resource.addCapability(new CapabilityImpl(resource, FeatureNamespace.FEATURE_NAMESPACE, dirs, attrs));
+ for (BundleInfo info : feature.getBundles()) {
+ if (!info.isDependency()) {
+ Resource res = locToRes.get(info.getLocation());
+ if (res == null) {
+ throw new IllegalStateException("Resource not found for url " + info.getLocation());
+ }
+ 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");
+ }
+ dirs = new HashMap<String, String>();
+ attrs = new HashMap<String, Object>();
+ attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, caps.get(0).getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+ attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, caps.get(0).getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+ attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, new VersionRange((Version) caps.get(0).getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE), true));
+ resource.addRequirement(new RequirementImpl(resource, IdentityNamespace.IDENTITY_NAMESPACE, dirs, attrs));
+ }
+ }
+ for (Dependency dep : feature.getDependencies()) {
+ String name = dep.getName();
+ String version = dep.getVersion();
+ if (version.equals("0.0.0")) {
+ version = null;
+ } else if (!version.startsWith("[") && !version.startsWith("(")) {
+ version = Macro.transform(featureRange, version);
+ }
+ dirs = new HashMap<String, String>();
+ attrs = new HashMap<String, Object>();
+ attrs.put(FeatureNamespace.FEATURE_NAMESPACE, name);
+ if (version != null) {
+ attrs.put(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(version));
+ }
+ resource.addRequirement(new RequirementImpl(resource, FeatureNamespace.FEATURE_NAMESPACE, dirs, attrs));
+ }
+ for (org.apache.karaf.features.Capability cap : feature.getCapabilities()) {
+ resource.addCapabilities(ResourceBuilder.parseCapability(resource, cap.getValue()));
+ }
+ for (org.apache.karaf.features.Requirement req : feature.getRequirements()) {
+ resource.addRequirements(ResourceBuilder.parseRequirement(resource, req.getValue()));
+ }
+ return resource;
+ }
+
+ public FeatureResource(Feature feature) {
+ super(feature.getName(), FeatureNamespace.TYPE_FEATURE, VersionTable.getVersion(feature.getVersion()));
+ this.feature = feature;
+ }
+
+ public Feature getFeature() {
+ return feature;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
new file mode 100644
index 0000000..cdc00d1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/IdentityCapability.java
@@ -0,0 +1,63 @@
+/*
+ * 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.resolver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+class IdentityCapability extends BaseClause implements Capability
+{
+ private final Resource m_resource;
+ private final Map<String, String> m_dirs;
+ private final Map<String, Object> m_attrs;
+
+ public IdentityCapability(Resource resource, String name, String type, Version version)
+ {
+ m_resource = resource;
+ m_dirs = new HashMap<String, String>();
+ m_attrs = new HashMap<String, Object>();
+ m_attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, name);
+ m_attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, type);
+ m_attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
+ }
+
+ public String getNamespace()
+ {
+ return IdentityNamespace.IDENTITY_NAMESPACE;
+ }
+
+ public Map<String, String> getDirectives()
+ {
+ return m_dirs;
+ }
+
+ public Map<String, Object> getAttributes()
+ {
+ return m_attrs;
+ }
+
+ public Resource getResource()
+ {
+ return m_resource;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
new file mode 100644
index 0000000..a4ef775
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.resolver;
+
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class RequirementImpl extends BaseClause implements Requirement {
+ private final Resource m_resource;
+ private final String m_namespace;
+ private final SimpleFilter m_filter;
+ private final boolean m_optional;
+ private final Map<String, String> m_dirs;
+ private final Map<String, Object> m_attrs;
+
+ public RequirementImpl(
+ Resource resource, String namespace,
+ Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter filter) {
+ m_resource = resource;
+ m_namespace = namespace;
+ m_dirs = dirs;
+ m_attrs = attrs;
+ m_filter = filter;
+ // Find resolution import directives.
+ m_optional = Constants.RESOLUTION_OPTIONAL.equals(m_dirs.get(Constants.RESOLUTION_DIRECTIVE));
+ }
+
+ public RequirementImpl(
+ Resource resource, String namespace,
+ Map<String, String> dirs, Map<String, Object> attrs) {
+ this(resource, namespace, dirs, attrs, SimpleFilter.convert(attrs));
+ }
+
+ public String getNamespace() {
+ return m_namespace;
+ }
+
+ public Map<String, String> getDirectives() {
+ return m_dirs;
+ }
+
+ public Map<String, Object> getAttributes() {
+ return m_attrs;
+ }
+
+ public Resource getResource() {
+ return m_resource;
+ }
+
+ public boolean matches(Capability cap) {
+ return CapabilitySet.matches(cap, getFilter());
+ }
+
+ public boolean isOptional() {
+ return m_optional;
+ }
+
+ public SimpleFilter getFilter() {
+ return m_filter;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java
new file mode 100644
index 0000000..e2ff793
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResolveContextImpl.java
@@ -0,0 +1,102 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wiring;
+import org.osgi.service.repository.Repository;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolveContext;
+
+/**
+*/
+public class ResolveContextImpl extends ResolveContext {
+
+ private final Set<Resource> mandatory;
+ private final Set<Resource> optional;
+ private final Repository repository;
+ private final Map<Resource, Wiring> wirings;
+ private final boolean resolveOptional;
+
+ private final CandidateComparator candidateComparator = new CandidateComparator();
+
+ public ResolveContextImpl(Set<Resource> mandatory,
+ Set<Resource> optional,
+ Repository repository,
+ boolean resolveOptional) {
+ this.mandatory = mandatory;
+ this.optional = optional;
+ this.repository = repository;
+ this.wirings = new HashMap<Resource, Wiring>();
+ this.resolveOptional = resolveOptional;
+ }
+
+ @Override
+ public Collection<Resource> getMandatoryResources() {
+ return mandatory;
+ }
+
+ @Override
+ public Collection<Resource> getOptionalResources() {
+ return optional;
+ }
+
+ @Override
+ public List<Capability> findProviders(Requirement requirement) {
+ List<Capability> caps = new ArrayList<Capability>();
+ Map<Requirement, Collection<Capability>> resMap =
+ repository.findProviders(Collections.singleton(requirement));
+ Collection<Capability> res = resMap != null ? resMap.get(requirement) : null;
+ if (res != null) {
+ caps.addAll(res);
+ }
+ Collections.sort(caps, candidateComparator);
+ return caps;
+ }
+ @Override
+ public int insertHostedCapability(List capabilities, HostedCapability hostedCapability) {
+ for (int i=0; i < capabilities.size(); i++) {
+ Capability cap = (Capability) capabilities.get(i);
+ if (candidateComparator.compare(hostedCapability, cap) <= 0) {
+ capabilities.add(i, hostedCapability);
+ return i;
+ }
+ }
+ capabilities.add(hostedCapability);
+ return capabilities.size() - 1;
+ }
+ @Override
+ public boolean isEffective(Requirement requirement) {
+ return resolveOptional ||
+ !Constants.RESOLUTION_OPTIONAL.equals(requirement.getDirectives().get(Constants.RESOLUTION_DIRECTIVE));
+ }
+ @Override
+ public Map<Resource, Wiring> getWirings() {
+ return wirings;
+ }
+}
[05/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/f01.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f01.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f01.xml
deleted file mode 100644
index 814c722..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/f01.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<features name="karaf-2.0.0">
- <feature name="spring" version="3.0.3.RELEASE">
- <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
- <bundle>mvn:org.springframework/spring-core/3.0.3.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-asm/3.0.3.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-expression/3.0.3.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-beans/3.0.3.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-aop/3.0.3.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-context/3.0.3.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-context-support/3.0.3.RELEASE</bundle>
- </feature>
- <feature name="spring-dm" version="1.2.0">
- <feature version="3.0.3.RELEASE">spring</feature>
- <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.cglib/2.2.2_1</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-io/1.2.0</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-core/1.2.0</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-extender/1.2.0</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-annotation/1.2.0</bundle>
- <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/2.0.0</bundle>
- </feature>
- <feature name="wrapper" version="2.0.0">
- <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.wrapper/2.0.0</bundle>
- </feature>
- <feature name="obr" version="2.0.0">
- <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/1.6.4</bundle>
- <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.obr/2.0.0</bundle>
- <bundle>mvn:org.apache.karaf.features/org.apache.karaf.features.obr/2.0.0</bundle>
- </feature>
- <feature name="http" version="2.0.0">
- <config name="org.ops4j.pax.web">
- org.osgi.service.http.port=8181
- </config>
- <bundle>mvn:org.apache.geronimo.specs/geronimo-servlet_2.5_spec/1.1.2</bundle>
- <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jetty-bundle/6.1.22_1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-api/0.7.2</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-spi/0.7.2</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-runtime/0.7.2</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-jetty/0.7.2</bundle>
- </feature>
- <feature name="war" version="2.0.0">
- <feature version="2.0.0">http</feature>
- <bundle>mvn:org.ops4j.pax.web/pax-web-jsp/0.7.2</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-extender-war/0.7.2</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-extender-whiteboard/0.7.2</bundle>
- <bundle>mvn:org.ops4j.pax.url/pax-url-war/1.1.3</bundle>
- <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.war/2.0.0</bundle>
- </feature>
- <feature name="webconsole" version="2.0.0">
- <feature version="2.0.0">http</feature>
- <config name="org.apache.karaf.webconsole">
- realm=karaf
- </config>
- <bundle>mvn:org.apache.felix/org.apache.felix.metatype/1.0.2</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/2.0.0</bundle>
- <bundle>mvn:org.apache.felix/org.apache.felix.webconsole/3.1.0</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.0.0</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.0.0</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.0.0</bundle>
- </feature>
- <feature name="ssh" version="2.0.0">
- <config name="org.apache.karaf.shell.ssh">
- sshPort=8101
- sshHost=0.0.0.0
- sshRealm=karaf
- </config>
- <bundle>mvn:org.apache.mina/mina-core/2.0.0-RC1</bundle>
- <bundle>mvn:org.apache.sshd/sshd-core/0.4.0</bundle>
- <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.ssh/2.0.0</bundle>
- </feature>
- <feature name="management" version="2.0.0">
- <bundle>mvn:org.apache.karaf/org.apache.karaf.management/2.0.0</bundle>
- <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx/0.1-r964701</bundle>
- <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx.blueprint/0.1-r964701</bundle>
- </feature>
-</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/f02.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f02.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f02.xml
deleted file mode 100644
index 1578faa..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/f02.xml
+++ /dev/null
@@ -1,164 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<features name="karaf-2.2.0" xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
- <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
- <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
- </feature>
- <feature name="spring-web" version="2.5.6.SEC02" resolver="(obr)">
- <feature version="2.5.6.SEC02">spring</feature>
- <feature version="2.2.0">http</feature>
- <bundle>mvn:org.springframework/spring-web/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-webmvc/2.5.6.SEC02</bundle>
- </feature>
- <feature name="spring" version="3.0.5.RELEASE" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
- <bundle>mvn:org.springframework/spring-core/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-asm/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-expression/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-beans/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-aop/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-context/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-context-support/3.0.5.RELEASE</bundle>
- </feature>
- <feature name="spring-web" version="3.0.5.RELEASE" resolver="(obr)">
- <feature version="3.0.5.RELEASE">spring</feature>
- <feature version="2.2.0">http</feature>
- <bundle>mvn:org.springframework/spring-web/3.0.5.RELEASE</bundle>
- <bundle>mvn:org.springframework/spring-webmvc/3.0.5.RELEASE</bundle>
- </feature>
- <feature name="spring-dm" version="1.2.1" resolver="(obr)">
- <feature version="[2.5.6,4)">spring</feature>
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.cglib/2.2.2_1</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-io/1.2.1</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-core/1.2.1</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-extender/1.2.1</bundle>
- <bundle>mvn:org.springframework.osgi/spring-osgi-annotation/1.2.1</bundle>
- <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/2.2.0</bundle>
- </feature>
- <feature name="spring-dm-web" version="1.2.1" resolver="(obr)">
- <feature version="1.2.1">spring-dm</feature>
- <feature version="[2.5.6,4)">spring-web</feature>
- <feature version="2.2.0">http</feature>
- <bundle>mvn:org.springframework.osgi/spring-osgi-web/1.2.1</bundle>
- </feature>
- <feature name="wrapper" version="2.2.0">
- <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.wrapper/2.2.0</bundle>
- </feature>
- <feature name="obr" version="2.2.0">
- <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/1.6.4</bundle>
- <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.obr/2.2.0</bundle>
- <bundle>mvn:org.apache.karaf.features/org.apache.karaf.features.obr/2.2.0</bundle>
- </feature>
- <feature name="config" version="2.2.0">
- <bundle start-level='30'>mvn:org.apache.karaf.shell/org.apache.karaf.shell.config/2.2.0</bundle>
- </feature>
- <feature name="jetty" version="7.2.2.v20101205" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.geronimo.specs/geronimo-servlet_2.5_spec/1.1.2</bundle>
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.asm/3.3_1</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-util/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-io/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-http/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-continuation/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-server/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-security/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-servlet/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-xml/7.2.2.v20101205</bundle>
- </feature>
- <feature name="jetty-jaas" version="7.2.2.v20101205" resolver="(obr)">
- <feature version="[7.0,8.0)">jetty</feature>
- <bundle dependency='true'>mvn:javax.mail/mail/1.4.3</bundle>
- <bundle dependency='true'>mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-webapp/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-jndi/7.2.2.v20101205</bundle>
- <bundle>mvn:org.eclipse.jetty/jetty-plus/7.2.2.v20101205</bundle>
- </feature>
- <feature name="http" version="2.2.0" resolver="(obr)">
- <configfile finalname="/etc/jetty.xml">mvn:org.apache.karaf/apache-karaf/2.2.0/xml/jettyconfig</configfile>
- <config name="org.ops4j.pax.web">
- org.osgi.service.http.port=8181
- javax.servlet.context.tempdir=${karaf.data}/pax-web-jsp
- org.ops4j.pax.web.config.file=${karaf.etc}/jetty.xml
- </config>
- <feature version="[7.0,8.0)">jetty</feature>
- <bundle>mvn:org.ops4j.pax.web/pax-web-api/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-spi/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-runtime/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-jetty/1.0.1</bundle>
- </feature>
- <feature name="war" version="2.2.0" resolver="(obr)">
- <config name="org.ops4j.pax.url.war">
- org.ops4j.pax.url.war.importPaxLoggingPackages=true
- </config>
- <feature>http</feature>
- <bundle start-level='30'>mvn:org.apache.karaf.shell/org.apache.karaf.shell.web/2.2.0</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-jsp/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-extender-war/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-extender-whiteboard/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.web/pax-web-deployer/1.0.1</bundle>
- <bundle>mvn:org.ops4j.pax.url/pax-url-war/1.2.5</bundle>
- </feature>
- <feature name="kar" version="2.2.0">
- <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.kar/2.2.0</bundle>
- </feature>
- <feature name="webconsole-base" version="2.2.0">
- <config name="org.apache.karaf.webconsole">
- realm=karaf
- </config>
- <feature>http</feature>
- <bundle>mvn:org.apache.felix/org.apache.felix.metatype/1.0.4</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/2.2.0</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.console/2.2.0</bundle>
- </feature>
- <feature name="webconsole" version="2.2.0">
- <feature version="2.2.0">webconsole-base</feature>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.2.0</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.2.0</bundle>
- <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.2.0</bundle>
- <bundle>mvn:org.apache.felix/org.apache.felix.webconsole.plugins.event/1.0.2</bundle>
- </feature>
- <feature name="ssh" version="2.2.0">
- <config name="org.apache.karaf.shell">
- sshPort=8101
- sshHost=0.0.0.0
- sshRealm=karaf
- hostKey=${karaf.etc}/host.key
- </config>
- <bundle dependency='true'>mvn:org.apache.mina/mina-core/2.0.1</bundle>
- <bundle dependency='true'>mvn:org.apache.sshd/sshd-core/0.5.0</bundle>
- <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.ssh/2.2.0</bundle>
- </feature>
- <feature name="management" version="2.2.0">
- <bundle>mvn:org.apache.karaf/org.apache.karaf.management/2.2.0</bundle>
- <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx/0.3</bundle>
- <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx.blueprint/0.3</bundle>
- </feature>
- <feature name="eventadmin" version="2.2.0">
- <bundle start-level='30'>mvn:org.apache.felix/org.apache.felix.eventadmin/1.2.8</bundle>
- </feature>
- <feature name="jasypt-encryption" version="2.2.0" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-codec/1.3_3</bundle>
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-lang/2.4_4</bundle>
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jasypt/1.7_1</bundle>
- <bundle>mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.jasypt/2.2.0</bundle>
- </feature>
-</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/f03.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f03.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f03.xml
deleted file mode 100644
index c058095..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/f03.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
- <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
- <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
- </feature>
-</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/f04.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f04.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f04.xml
deleted file mode 100644
index 85f28ad..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/f04.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<features name="karaf" xmlns="http://karaf.apache.org/xmlns/features/v1.1.0">
- <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
- <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
- </feature>
-</features>
-
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/f05.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f05.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f05.xml
deleted file mode 100644
index 15d84e2..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/f05.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<features xmlns="http://karaf.apache.org/xmlns/features/v1.1.0">
- <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
- <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
- </feature>
-</features>
-
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml
deleted file mode 100644
index 496cbdb..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/f06.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- 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.
--->
-<features name="karaf" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0">
- <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
- <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_4</bundle>
- <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
- <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
- <conditional>
- <condition>http</condition>
- <bundle>mvn:org.springframework/spring-web/2.5.6.SEC02</bundle>
- </conditional>
- </feature>
-</features>
-
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/overrides.properties
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/overrides.properties b/features/core/src/test/resources/org/apache/karaf/features/internal/overrides.properties
deleted file mode 100644
index d34fa7e..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/overrides.properties
+++ /dev/null
@@ -1,23 +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.
-#
-################################################################################
-
-# Sample etc/overrides.properties file for testing purposes
-mvn:org.apache.karaf.admin/org.apache.karaf.admin.command/2.3.0.redhat-61033X
-mvn:org.apache.karaf.admin/org.apache.karaf.admin.core/2.3.0.redhat-61033X;range=[2.3.0,2.5)
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
deleted file mode 100644
index 5fd51d0..0000000
--- a/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
+++ /dev/null
@@ -1,41 +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.
--->
-<features name="repo2">
- <feature name="common">
- <bundle>b1</bundle>
- </feature>
- <feature name="f1">
- <feature>common</feature>
- <bundle>b2</bundle>
- </feature>
- <feature name="f2">
- <feature>common</feature>
- <feature>f1</feature>
- <bundle>b3</bundle>
- </feature>
- <feature name="f3">
- <feature>f1</feature>
- <feature>f2</feature>
- <bundle>b4</bundle>
- </feature>
- <feature name="all">
- <feature>f1</feature>
- <feature>f2</feature>
- <feature>f3</feature>
- </feature>
-</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f01.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f01.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f01.xml
new file mode 100644
index 0000000..814c722
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f01.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features name="karaf-2.0.0">
+ <feature name="spring" version="3.0.3.RELEASE">
+ <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
+ <bundle>mvn:org.springframework/spring-core/3.0.3.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-asm/3.0.3.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-expression/3.0.3.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-beans/3.0.3.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-aop/3.0.3.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-context/3.0.3.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/3.0.3.RELEASE</bundle>
+ </feature>
+ <feature name="spring-dm" version="1.2.0">
+ <feature version="3.0.3.RELEASE">spring</feature>
+ <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.cglib/2.2.2_1</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-io/1.2.0</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-core/1.2.0</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-extender/1.2.0</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-annotation/1.2.0</bundle>
+ <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/2.0.0</bundle>
+ </feature>
+ <feature name="wrapper" version="2.0.0">
+ <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.wrapper/2.0.0</bundle>
+ </feature>
+ <feature name="obr" version="2.0.0">
+ <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/1.6.4</bundle>
+ <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.obr/2.0.0</bundle>
+ <bundle>mvn:org.apache.karaf.features/org.apache.karaf.features.obr/2.0.0</bundle>
+ </feature>
+ <feature name="http" version="2.0.0">
+ <config name="org.ops4j.pax.web">
+ org.osgi.service.http.port=8181
+ </config>
+ <bundle>mvn:org.apache.geronimo.specs/geronimo-servlet_2.5_spec/1.1.2</bundle>
+ <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jetty-bundle/6.1.22_1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-api/0.7.2</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-spi/0.7.2</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-runtime/0.7.2</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-jetty/0.7.2</bundle>
+ </feature>
+ <feature name="war" version="2.0.0">
+ <feature version="2.0.0">http</feature>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-jsp/0.7.2</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-extender-war/0.7.2</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-extender-whiteboard/0.7.2</bundle>
+ <bundle>mvn:org.ops4j.pax.url/pax-url-war/1.1.3</bundle>
+ <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.war/2.0.0</bundle>
+ </feature>
+ <feature name="webconsole" version="2.0.0">
+ <feature version="2.0.0">http</feature>
+ <config name="org.apache.karaf.webconsole">
+ realm=karaf
+ </config>
+ <bundle>mvn:org.apache.felix/org.apache.felix.metatype/1.0.2</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/2.0.0</bundle>
+ <bundle>mvn:org.apache.felix/org.apache.felix.webconsole/3.1.0</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.0.0</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.0.0</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.0.0</bundle>
+ </feature>
+ <feature name="ssh" version="2.0.0">
+ <config name="org.apache.karaf.shell.ssh">
+ sshPort=8101
+ sshHost=0.0.0.0
+ sshRealm=karaf
+ </config>
+ <bundle>mvn:org.apache.mina/mina-core/2.0.0-RC1</bundle>
+ <bundle>mvn:org.apache.sshd/sshd-core/0.4.0</bundle>
+ <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.ssh/2.0.0</bundle>
+ </feature>
+ <feature name="management" version="2.0.0">
+ <bundle>mvn:org.apache.karaf/org.apache.karaf.management/2.0.0</bundle>
+ <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx/0.1-r964701</bundle>
+ <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx.blueprint/0.1-r964701</bundle>
+ </feature>
+</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f02.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f02.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f02.xml
new file mode 100644
index 0000000..1578faa
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f02.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features name="karaf-2.2.0" xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
+ <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
+ <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
+ </feature>
+ <feature name="spring-web" version="2.5.6.SEC02" resolver="(obr)">
+ <feature version="2.5.6.SEC02">spring</feature>
+ <feature version="2.2.0">http</feature>
+ <bundle>mvn:org.springframework/spring-web/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-webmvc/2.5.6.SEC02</bundle>
+ </feature>
+ <feature name="spring" version="3.0.5.RELEASE" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
+ <bundle>mvn:org.springframework/spring-core/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-asm/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-expression/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-beans/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-aop/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-context/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/3.0.5.RELEASE</bundle>
+ </feature>
+ <feature name="spring-web" version="3.0.5.RELEASE" resolver="(obr)">
+ <feature version="3.0.5.RELEASE">spring</feature>
+ <feature version="2.2.0">http</feature>
+ <bundle>mvn:org.springframework/spring-web/3.0.5.RELEASE</bundle>
+ <bundle>mvn:org.springframework/spring-webmvc/3.0.5.RELEASE</bundle>
+ </feature>
+ <feature name="spring-dm" version="1.2.1" resolver="(obr)">
+ <feature version="[2.5.6,4)">spring</feature>
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.cglib/2.2.2_1</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-io/1.2.1</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-core/1.2.1</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-extender/1.2.1</bundle>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-annotation/1.2.1</bundle>
+ <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/2.2.0</bundle>
+ </feature>
+ <feature name="spring-dm-web" version="1.2.1" resolver="(obr)">
+ <feature version="1.2.1">spring-dm</feature>
+ <feature version="[2.5.6,4)">spring-web</feature>
+ <feature version="2.2.0">http</feature>
+ <bundle>mvn:org.springframework.osgi/spring-osgi-web/1.2.1</bundle>
+ </feature>
+ <feature name="wrapper" version="2.2.0">
+ <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.wrapper/2.2.0</bundle>
+ </feature>
+ <feature name="obr" version="2.2.0">
+ <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/1.6.4</bundle>
+ <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.obr/2.2.0</bundle>
+ <bundle>mvn:org.apache.karaf.features/org.apache.karaf.features.obr/2.2.0</bundle>
+ </feature>
+ <feature name="config" version="2.2.0">
+ <bundle start-level='30'>mvn:org.apache.karaf.shell/org.apache.karaf.shell.config/2.2.0</bundle>
+ </feature>
+ <feature name="jetty" version="7.2.2.v20101205" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.geronimo.specs/geronimo-servlet_2.5_spec/1.1.2</bundle>
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.asm/3.3_1</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-util/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-io/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-http/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-continuation/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-server/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-security/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-servlet/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-xml/7.2.2.v20101205</bundle>
+ </feature>
+ <feature name="jetty-jaas" version="7.2.2.v20101205" resolver="(obr)">
+ <feature version="[7.0,8.0)">jetty</feature>
+ <bundle dependency='true'>mvn:javax.mail/mail/1.4.3</bundle>
+ <bundle dependency='true'>mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-webapp/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-jndi/7.2.2.v20101205</bundle>
+ <bundle>mvn:org.eclipse.jetty/jetty-plus/7.2.2.v20101205</bundle>
+ </feature>
+ <feature name="http" version="2.2.0" resolver="(obr)">
+ <configfile finalname="/etc/jetty.xml">mvn:org.apache.karaf/apache-karaf/2.2.0/xml/jettyconfig</configfile>
+ <config name="org.ops4j.pax.web">
+ org.osgi.service.http.port=8181
+ javax.servlet.context.tempdir=${karaf.data}/pax-web-jsp
+ org.ops4j.pax.web.config.file=${karaf.etc}/jetty.xml
+ </config>
+ <feature version="[7.0,8.0)">jetty</feature>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-api/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-spi/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-runtime/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-jetty/1.0.1</bundle>
+ </feature>
+ <feature name="war" version="2.2.0" resolver="(obr)">
+ <config name="org.ops4j.pax.url.war">
+ org.ops4j.pax.url.war.importPaxLoggingPackages=true
+ </config>
+ <feature>http</feature>
+ <bundle start-level='30'>mvn:org.apache.karaf.shell/org.apache.karaf.shell.web/2.2.0</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-jsp/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-extender-war/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-extender-whiteboard/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.web/pax-web-deployer/1.0.1</bundle>
+ <bundle>mvn:org.ops4j.pax.url/pax-url-war/1.2.5</bundle>
+ </feature>
+ <feature name="kar" version="2.2.0">
+ <bundle>mvn:org.apache.karaf.deployer/org.apache.karaf.deployer.kar/2.2.0</bundle>
+ </feature>
+ <feature name="webconsole-base" version="2.2.0">
+ <config name="org.apache.karaf.webconsole">
+ realm=karaf
+ </config>
+ <feature>http</feature>
+ <bundle>mvn:org.apache.felix/org.apache.felix.metatype/1.0.4</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/2.2.0</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.console/2.2.0</bundle>
+ </feature>
+ <feature name="webconsole" version="2.2.0">
+ <feature version="2.2.0">webconsole-base</feature>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.admin/2.2.0</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.features/2.2.0</bundle>
+ <bundle>mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.gogo/2.2.0</bundle>
+ <bundle>mvn:org.apache.felix/org.apache.felix.webconsole.plugins.event/1.0.2</bundle>
+ </feature>
+ <feature name="ssh" version="2.2.0">
+ <config name="org.apache.karaf.shell">
+ sshPort=8101
+ sshHost=0.0.0.0
+ sshRealm=karaf
+ hostKey=${karaf.etc}/host.key
+ </config>
+ <bundle dependency='true'>mvn:org.apache.mina/mina-core/2.0.1</bundle>
+ <bundle dependency='true'>mvn:org.apache.sshd/sshd-core/0.5.0</bundle>
+ <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.ssh/2.2.0</bundle>
+ </feature>
+ <feature name="management" version="2.2.0">
+ <bundle>mvn:org.apache.karaf/org.apache.karaf.management/2.2.0</bundle>
+ <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx/0.3</bundle>
+ <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx.blueprint/0.3</bundle>
+ </feature>
+ <feature name="eventadmin" version="2.2.0">
+ <bundle start-level='30'>mvn:org.apache.felix/org.apache.felix.eventadmin/1.2.8</bundle>
+ </feature>
+ <feature name="jasypt-encryption" version="2.2.0" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-codec/1.3_3</bundle>
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-lang/2.4_4</bundle>
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jasypt/1.7_1</bundle>
+ <bundle>mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.jasypt/2.2.0</bundle>
+ </feature>
+</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f03.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f03.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f03.xml
new file mode 100644
index 0000000..c058095
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f03.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
+ <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
+ <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
+ </feature>
+</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f04.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f04.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f04.xml
new file mode 100644
index 0000000..85f28ad
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f04.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features name="karaf" xmlns="http://karaf.apache.org/xmlns/features/v1.1.0">
+ <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
+ <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
+ </feature>
+</features>
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f05.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f05.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f05.xml
new file mode 100644
index 0000000..15d84e2
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f05.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.1.0">
+ <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_6</bundle>
+ <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
+ </feature>
+</features>
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f06.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f06.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f06.xml
new file mode 100644
index 0000000..496cbdb
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f06.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features name="karaf" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0">
+ <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_4</bundle>
+ <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
+ <conditional>
+ <condition>http</condition>
+ <bundle>mvn:org.springframework/spring-web/2.5.6.SEC02</bundle>
+ </conditional>
+ </feature>
+</features>
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/f07.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/f07.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f07.xml
new file mode 100644
index 0000000..5b7dd90
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/f07.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features name="karaf" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0">
+ <feature name="spring" version="2.5.6.SEC02" resolver="(obr)">
+ <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/1.0_4</bundle>
+ <bundle>mvn:org.springframework/spring-core/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-beans/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-aop/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context/2.5.6.SEC02</bundle>
+ <bundle>mvn:org.springframework/spring-context-support/2.5.6.SEC02</bundle>
+ <conditional>
+ <condition>http</condition>
+ <bundle>mvn:org.springframework/spring-web/2.5.6.SEC02</bundle>
+ </conditional>
+ <capability>
+ service-reference;effective:=active;objectClass=org.apache.aries.proxy.ProxyManager
+ </capability>
+ </feature>
+</features>
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/overrides.properties
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/overrides.properties b/features/core/src/test/resources/org/apache/karaf/features/internal/service/overrides.properties
new file mode 100644
index 0000000..d34fa7e
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/overrides.properties
@@ -0,0 +1,23 @@
+
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# Sample etc/overrides.properties file for testing purposes
+mvn:org.apache.karaf.admin/org.apache.karaf.admin.command/2.3.0.redhat-61033X
+mvn:org.apache.karaf.admin/org.apache.karaf.admin.core/2.3.0.redhat-61033X;range=[2.3.0,2.5)
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/internal/service/repo2.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/internal/service/repo2.xml b/features/core/src/test/resources/org/apache/karaf/features/internal/service/repo2.xml
new file mode 100644
index 0000000..5fd51d0
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/internal/service/repo2.xml
@@ -0,0 +1,41 @@
+<!--
+
+ 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.
+-->
+<features name="repo2">
+ <feature name="common">
+ <bundle>b1</bundle>
+ </feature>
+ <feature name="f1">
+ <feature>common</feature>
+ <bundle>b2</bundle>
+ </feature>
+ <feature name="f2">
+ <feature>common</feature>
+ <feature>f1</feature>
+ <bundle>b3</bundle>
+ </feature>
+ <feature name="f3">
+ <feature>f1</feature>
+ <feature>f2</feature>
+ <bundle>b4</bundle>
+ </feature>
+ <feature name="all">
+ <feature>f1</feature>
+ <feature>f2</feature>
+ <feature>f3</feature>
+ </feature>
+</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/resources/org/apache/karaf/features/repo3.xml
----------------------------------------------------------------------
diff --git a/features/core/src/test/resources/org/apache/karaf/features/repo3.xml b/features/core/src/test/resources/org/apache/karaf/features/repo3.xml
new file mode 100644
index 0000000..ffe08ed
--- /dev/null
+++ b/features/core/src/test/resources/org/apache/karaf/features/repo3.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<features name="test" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0">
+ <feature name="f1">
+ <capability>
+ cap
+ </capability>
+ <requirement>
+ req
+ </requirement>
+ </feature>
+</features>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/NOTICE
----------------------------------------------------------------------
diff --git a/features/obr/NOTICE b/features/obr/NOTICE
deleted file mode 100644
index b70f1f9..0000000
--- a/features/obr/NOTICE
+++ /dev/null
@@ -1,71 +0,0 @@
-Apache Karaf
-Copyright 2010-2014 The Apache Software Foundation
-
-
-I. Included Software
-
-This product includes software developed at
-The Apache Software Foundation (http://www.apache.org/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-The OSGi Alliance (http://www.osgi.org/).
-Copyright (c) OSGi Alliance (2000, 2010).
-Licensed under the Apache License 2.0.
-
-This product includes software developed at
-OW2 (http://www.ow2.org/).
-Licensed under the BSD License.
-
-This product includes software developed at
-OPS4J (http://www.ops4j.org/).
-Licensed under the Apache License 2.0.
-
-This product includes software developed at
-Eclipse Foundation (http://www.eclipse.org/).
-Licensed under the EPL.
-
-This product includes software written by
-Antony Lesuisse.
-Licensed under Public Domain.
-
-
-II. Used Software
-
-This product uses software developed at
-FUSE Source (http://www.fusesource.org/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-AOP Alliance (http://aopalliance.sourceforge.net/).
-Licensed under the Public Domain.
-
-This product uses software developed at
-Tanuki Software (http://www.tanukisoftware.com/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-Jasypt (http://jasypt.sourceforge.net/).
-Licensed under the Apache License 2.0.
-
-This product uses software developed at
-JLine (http://jline.sourceforge.net).
-Licensed under the BSD License.
-
-This product uses software developed at
-SLF4J (http://www.slf4j.org/).
-Licensed under the MIT License.
-
-This product uses software developed at
-SpringSource (http://www.springsource.org/).
-Licensed under the Apache License 2.0.
-
-This product includes software from http://www.json.org.
-Copyright (c) 2002 JSON.org
-
-
-III. License Summary
-- Apache License 2.0
-- BSD License
-- EPL License
-- MIT License
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/pom.xml
----------------------------------------------------------------------
diff --git a/features/obr/pom.xml b/features/obr/pom.xml
deleted file mode 100644
index 3aff278..0000000
--- a/features/obr/pom.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <!--
-
- 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.
- -->
-
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.karaf.features</groupId>
- <artifactId>features</artifactId>
- <version>4.0.0-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
-
- <artifactId>org.apache.karaf.features.obr</artifactId>
- <packaging>bundle</packaging>
- <name>Apache Karaf :: Features :: OBR Resolver</name>
- <description>This bundle provide OBR (OSGi Bundle Repository) support for Karaf features.</description>
-
- <properties>
- <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.apache.karaf.features</groupId>
- <artifactId>org.apache.karaf.features.core</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.utils</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.bundlerepository</artifactId>
- </dependency>
-
- </dependencies>
-
- <build>
- <resources>
- <resource>
- <directory>${project.basedir}/src/main/resources</directory>
- <includes>
- <include>**/*</include>
- </includes>
- </resource>
- <resource>
- <directory>${project.basedir}/src/main/resources</directory>
- <filtering>true</filtering>
- <includes>
- <include>**/*.info</include>
- </includes>
- </resource>
- </resources>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <configuration>
- <instructions>
- <Import-Package>
- org.apache.aries.blueprint,
- org.osgi.service.blueprint.container,
- org.osgi.service.blueprint.reflect,
- *
- </Import-Package>
- <Private-Package>org.apache.karaf.features.obr.internal</Private-Package>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/src/main/java/org/apache/karaf/features/obr/internal/BundleInfoImpl.java
----------------------------------------------------------------------
diff --git a/features/obr/src/main/java/org/apache/karaf/features/obr/internal/BundleInfoImpl.java b/features/obr/src/main/java/org/apache/karaf/features/obr/internal/BundleInfoImpl.java
deleted file mode 100644
index ac48da8..0000000
--- a/features/obr/src/main/java/org/apache/karaf/features/obr/internal/BundleInfoImpl.java
+++ /dev/null
@@ -1,95 +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.obr.internal;
-
-import org.apache.karaf.features.BundleInfo;
-
-/**
- * A holder of bundle info
- */
-public class BundleInfoImpl implements BundleInfo {
-
- private int startLevel;
- private String location;
- private boolean start;
- private boolean dependency;
-
-
- public BundleInfoImpl() {
- }
-
- public BundleInfoImpl(String location) {
- this.location = location;
- }
-
-
- public BundleInfoImpl(String location, boolean start) {
- this.location = location;
- this.start = start;
- }
-
- public BundleInfoImpl(String location, int startLevel) {
- this.location = location;
- this.startLevel = startLevel;
- }
-
- public BundleInfoImpl(String location, int startLevel, boolean start) {
- this.location = location;
- this.startLevel = startLevel;
- this.start = start;
- }
-
- public BundleInfoImpl(String location, int startLevel, boolean start, boolean dependency) {
- this.location = location;
- this.startLevel = startLevel;
- this.start = start;
- this.dependency = dependency;
- }
-
- public void setStartLevel(Integer startLevel) {
- this.startLevel = startLevel;
- }
-
- public int getStartLevel() {
- return this.startLevel;
- }
-
- public void setName(String location) {
- this.location = location;
- }
-
- public String getLocation() {
- return this.location;
- }
-
- public void setStart(boolean start) {
- this.start = start;
- }
-
- public boolean isStart() {
- return start;
- }
-
- public void setDependency(boolean dependency) {
- this.dependency = dependency;
- }
-
- public boolean isDependency() {
- return dependency;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/src/main/java/org/apache/karaf/features/obr/internal/ObrResolver.java
----------------------------------------------------------------------
diff --git a/features/obr/src/main/java/org/apache/karaf/features/obr/internal/ObrResolver.java b/features/obr/src/main/java/org/apache/karaf/features/obr/internal/ObrResolver.java
deleted file mode 100644
index 2823e88..0000000
--- a/features/obr/src/main/java/org/apache/karaf/features/obr/internal/ObrResolver.java
+++ /dev/null
@@ -1,199 +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.obr.internal;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.bundlerepository.Reason;
-import org.apache.felix.bundlerepository.Repository;
-import org.apache.felix.bundlerepository.RepositoryAdmin;
-import org.apache.felix.bundlerepository.Requirement;
-import org.apache.felix.bundlerepository.Resource;
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.Resolver;
-import org.osgi.framework.InvalidSyntaxException;
-
-public class ObrResolver implements Resolver {
-
- private RepositoryAdmin repositoryAdmin;
- private boolean resolveOptionalImports;
- private boolean startByDefault;
- private int startLevel;
-
- public RepositoryAdmin getRepositoryAdmin() {
- return repositoryAdmin;
- }
-
- public void setRepositoryAdmin(RepositoryAdmin repositoryAdmin) {
- this.repositoryAdmin = repositoryAdmin;
- }
-
- public boolean isResolveOptionalImports() {
- return resolveOptionalImports;
- }
-
- /**
- * When set to <code>true</code>, the OBR resolver will try to resolve optional imports as well.
- * Defaults to <code>false</code>
- *
- * @param resolveOptionalImports
- */
- public void setResolveOptionalImports(boolean resolveOptionalImports) {
- this.resolveOptionalImports = resolveOptionalImports;
- }
-
- public void setStartByDefault(boolean startByDefault) {
- this.startByDefault = startByDefault;
- }
-
- public void setStartLevel(int startLevel) {
- this.startLevel = startLevel;
- }
-
- public List<BundleInfo> resolve(Feature feature) throws Exception {
- List<Requirement> reqs = new ArrayList<Requirement>();
- List<Resource> ress = new ArrayList<Resource>();
- List<Resource> featureDeploy = new ArrayList<Resource>();
- Map<Object, BundleInfo> infos = new HashMap<Object, BundleInfo>();
- for (BundleInfo bundleInfo : feature.getBundles()) {
- URL url = null;
- try {
- url = new URL(bundleInfo.getLocation());
- } catch (MalformedURLException e) {
- Requirement req = parseRequirement(bundleInfo.getLocation());
- reqs.add(req);
- infos.put(req, bundleInfo);
- }
- if (url != null) {
- Resource res = repositoryAdmin.getHelper().createResource(url);
- ress.add(res);
- infos.put(res, bundleInfo);
- }
- }
-
- Repository repository = repositoryAdmin.getHelper().repository(ress.toArray(new Resource[ress.size()]));
- List<Repository> repos = new ArrayList<Repository>();
- repos.add(repositoryAdmin.getSystemRepository());
- repos.add(repositoryAdmin.getLocalRepository());
- repos.add(repository);
- repos.addAll(Arrays.asList(repositoryAdmin.listRepositories()));
- org.apache.felix.bundlerepository.Resolver resolver = repositoryAdmin.resolver(repos.toArray(new Repository[repos.size()]));
-
- for (Resource res : ress) {
- if (!infos.get(res).isDependency()) {
- resolver.add(res);
- }
- }
- for (Requirement req : reqs) {
- resolver.add(req);
- }
-
- if (!doResolve(resolver)) {
- StringWriter w = new StringWriter();
- PrintWriter out = new PrintWriter(w);
- Reason[] failedReqs = resolver.getUnsatisfiedRequirements();
- if ((failedReqs != null) && (failedReqs.length > 0)) {
- out.println("Unsatisfied requirement(s):");
- printUnderline(out, 27);
- for (Reason r : failedReqs) {
- out.println(" " + r.getRequirement().getName() + ":" + r.getRequirement().getFilter());
- out.println(" " + r.getResource().getPresentationName());
- }
- } else {
- out.println("Could not resolve targets.");
- }
- out.flush();
- throw new Exception("Can not resolve feature:\n" + w.toString());
- }
-
- List<BundleInfo> bundles = new ArrayList<BundleInfo>();
- List<Resource> deploy = new ArrayList<Resource>();
- Collections.addAll(deploy, resolver.getRequiredResources());
- if (resolveOptionalImports) {
- Collections.addAll(deploy, resolver.getOptionalResources());
- }
- Collections.addAll(deploy, resolver.getAddedResources());
- deploy.addAll(featureDeploy);
- for (Resource res : deploy) {
- BundleInfo info = infos.get(res);
- if (info == null) {
- Reason[] reasons = resolver.getReason(res);
- if (reasons != null) {
- for (Reason r : reasons) {
- info = infos.get(r);
- if (info != null) {
- break;
- }
- }
- }
- }
- if (info == null) {
- info = new BundleInfoImpl(res.getURI(), this.startLevel, this.startByDefault, false);
- }
- bundles.add(info);
- }
- return bundles;
- }
-
- private boolean doResolve(org.apache.felix.bundlerepository.Resolver resolver) {
- if (resolveOptionalImports) {
- return resolver.resolve();
- } else {
- return resolver.resolve(org.apache.felix.bundlerepository.Resolver.NO_OPTIONAL_RESOURCES);
- }
- }
-
- protected void printUnderline(PrintWriter out, int length) {
- for (int i = 0; i < length; i++) {
- out.print('-');
- }
- out.println("");
- }
-
- protected Requirement parseRequirement(String req) throws InvalidSyntaxException {
- int p = req.indexOf(':');
- String name;
- String filter;
- if (p > 0) {
- name = req.substring(0, p);
- filter = req.substring(p + 1);
- } else {
- if (req.contains("package")) {
- name = "package";
- } else if (req.contains("service")) {
- name = "service";
- } else {
- name = "bundle";
- }
- filter = req;
- }
- if (!filter.startsWith("(")) {
- filter = "(" + filter + ")";
- }
- return repositoryAdmin.getHelper().requirement(name, filter);
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/src/main/resources/OSGI-INF/blueprint/features-obr.xml
----------------------------------------------------------------------
diff --git a/features/obr/src/main/resources/OSGI-INF/blueprint/features-obr.xml b/features/obr/src/main/resources/OSGI-INF/blueprint/features-obr.xml
deleted file mode 100644
index 590258f..0000000
--- a/features/obr/src/main/resources/OSGI-INF/blueprint/features-obr.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
- 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.
-
--->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
- xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
- default-activation="lazy">
-
- <ext:property-placeholder placeholder-prefix="$(" placeholder-suffix=")"/>
-
- <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]" ignore-missing-locations="true">
- <ext:default-properties>
- <ext:property name="resolveOptionalImports" value="false"/>
- <ext:property name="startByDefault" value="true"/>
- <ext:property name="startLevel" value="80"/>
- </ext:default-properties>
- <ext:location>file:$(karaf.etc)/org.apache.karaf.features.obr.cfg</ext:location>
- </ext:property-placeholder>
-
- <bean id="obrResolver" class="org.apache.karaf.features.obr.internal.ObrResolver">
- <property name="repositoryAdmin" ref="repositoryAdmin" />
- <property name="resolveOptionalImports" value="$[resolveOptionalImports]" />
- <property name="startByDefault" value="$[startByDefault]" />
- <property name="startLevel" value="$[startLevel]" />
- </bean>
-
- <reference id="repositoryAdmin" interface="org.apache.felix.bundlerepository.RepositoryAdmin" />
-
- <service ref="obrResolver" interface="org.apache.karaf.features.Resolver">
- <service-properties>
- <entry key="name" value="obr" />
- </service-properties>
- </service>
-
-</blueprint>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/obr/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/features/obr/src/main/resources/OSGI-INF/bundle.info b/features/obr/src/main/resources/OSGI-INF/bundle.info
deleted file mode 100644
index bf550bf..0000000
--- a/features/obr/src/main/resources/OSGI-INF/bundle.info
+++ /dev/null
@@ -1,18 +0,0 @@
-h1. Synopsis
-
-${project.name}
-
-${project.description}
-
-Maven URL:
-[mvn:${project.groupId}/${project.artifactId}/${project.version}]
-
-h1. Description
-
-This bundle provides the OBR (OSGi Bundle Repository) support for Karaf features.
-
-It allows you to use an OBR resolver for bundles.
-
-h1. See also
-
-Provisioning - section of the Karaf User Guide
[13/13] git commit: [KARAF-2888] New FeaturesService based on the
real OSGi resolver
Posted by gn...@apache.org.
[KARAF-2888] New FeaturesService based on the real OSGi resolver
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/38502e41
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/38502e41
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/38502e41
Branch: refs/heads/master
Commit: 38502e41540de3443b2d7f8215d43bb33db5e1c0
Parents: 9605df3
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Mon Apr 7 09:25:51 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Mon Apr 7 10:52:37 2014 +0200
----------------------------------------------------------------------
.../enterprise/src/main/feature/feature.xml | 17 +-
.../standard/src/main/feature/feature.xml | 9 +-
features/core/pom.xml | 19 +-
.../org/apache/karaf/features/Capability.java | 23 +
.../java/org/apache/karaf/features/Feature.java | 5 +
.../karaf/features/FeaturesNamespaces.java | 6 +-
.../org/apache/karaf/features/Requirement.java | 23 +
.../karaf/features/internal/Artifact.java | 56 -
.../internal/BootFeaturesInstaller.java | 171 ---
.../karaf/features/internal/BundleManager.java | 498 -------
.../features/internal/EventAdminListener.java | 92 --
.../internal/FeatureConfigInstaller.java | 166 ---
.../karaf/features/internal/FeatureFinder.java | 59 -
.../internal/FeatureValidationUtil.java | 110 --
.../features/internal/FeaturesServiceImpl.java | 1192 ---------------
.../features/internal/InstallationState.java | 35 -
.../karaf/features/internal/Overrides.java | 230 ---
.../karaf/features/internal/RepositoryImpl.java | 99 --
.../internal/deployment/DeploymentBuilder.java | 330 +++++
.../internal/deployment/Downloader.java | 35 +
.../internal/deployment/StreamProvider.java | 26 +
.../management/FeaturesServiceMBeanImpl.java | 274 ++++
.../management/StandardEmitterMBean.java | 65 +
.../features/internal/model/Capability.java | 91 ++
.../karaf/features/internal/model/Feature.java | 36 +-
.../features/internal/model/Requirement.java | 87 ++
.../karaf/features/internal/osgi/Activator.java | 114 +-
.../repository/AggregateRepository.java | 55 +
.../internal/repository/BaseRepository.java | 86 ++
.../internal/repository/CacheRepository.java | 59 +
.../repository/HttpMetadataProvider.java | 88 ++
.../internal/repository/MetadataProvider.java | 29 +
.../internal/repository/MetadataRepository.java | 43 +
.../internal/repository/StaticRepository.java | 33 +
.../features/internal/resolver/BaseClause.java | 114 ++
.../internal/resolver/CandidateComparator.java | 129 ++
.../internal/resolver/CapabilityImpl.java | 165 +++
.../internal/resolver/CapabilitySet.java | 612 ++++++++
.../internal/resolver/FeatureNamespace.java | 72 +
.../internal/resolver/FeatureResource.java | 101 ++
.../internal/resolver/IdentityCapability.java | 63 +
.../internal/resolver/RequirementImpl.java | 80 +
.../internal/resolver/ResolveContextImpl.java | 102 ++
.../internal/resolver/ResourceBuilder.java | 1129 ++++++++++++++
.../internal/resolver/ResourceImpl.java | 110 ++
.../internal/resolver/ServiceNamespace.java | 30 +
.../internal/resolver/SimpleFilter.java | 649 +++++++++
.../internal/resolver/Slf4jResolverLog.java | 49 +
.../internal/resolver/UriNamespace.java | 47 +
.../features/internal/service/Artifact.java | 56 +
.../internal/service/BootFeaturesInstaller.java | 193 +++
.../internal/service/EventAdminListener.java | 91 ++
.../service/FeatureConfigInstaller.java | 167 +++
.../internal/service/FeatureFinder.java | 68 +
.../internal/service/FeatureValidationUtil.java | 113 ++
.../internal/service/FeaturesServiceImpl.java | 1378 ++++++++++++++++++
.../features/internal/service/Overrides.java | 132 ++
.../internal/service/RepositoryImpl.java | 103 ++
.../internal/service/RequirementSort.java | 107 ++
.../internal/service/SimpleDownloader.java | 51 +
.../karaf/features/internal/service/State.java | 34 +
.../features/internal/service/StateStorage.java | 175 +++
.../features/internal/util/ChecksumUtils.java | 56 +
.../features/internal/util/JsonReader.java | 343 +++++
.../features/internal/util/JsonWriter.java | 120 ++
.../karaf/features/internal/util/Macro.java | 142 ++
.../features/internal/util/MultiException.java | 95 ++
.../management/FeaturesServiceMBean.java | 2 +-
.../management/codec/JmxRepositoryEvent.java | 13 +-
.../internal/FeaturesServiceMBeanImpl.java | 274 ----
.../internal/StandardEmitterMBean.java | 65 -
.../karaf/features/karaf-features-1.3.0.xsd | 280 ++++
.../apache/karaf/features/ConditionalTest.java | 5 +-
.../karaf/features/FeaturesServiceTest.java | 165 +--
.../apache/karaf/features/RepositoryTest.java | 24 +-
.../org/apache/karaf/features/TestBase.java | 105 ++
.../internal/BootFeaturesInstallerTest.java | 103 --
.../features/internal/BundleManagerTest.java | 74 -
.../internal/FeaturesServiceImplTest.java | 178 ---
.../internal/FeaturesValidationTest.java | 60 -
.../karaf/features/internal/OverridesTest.java | 243 ---
.../karaf/features/internal/TestBase.java | 108 --
.../service/BootFeaturesInstallerTest.java | 128 ++
.../internal/service/BundleManagerTest.java | 64 +
.../service/FeaturesServiceImplTest.java | 167 +++
.../service/FeaturesValidationTest.java | 65 +
.../internal/service/OverridesTest.java | 208 +++
.../org/apache/karaf/features/internal/f01.xml | 92 --
.../org/apache/karaf/features/internal/f02.xml | 164 ---
.../org/apache/karaf/features/internal/f03.xml | 27 -
.../org/apache/karaf/features/internal/f04.xml | 28 -
.../org/apache/karaf/features/internal/f05.xml | 28 -
.../org/apache/karaf/features/internal/f06.xml | 32 -
.../features/internal/overrides.properties | 23 -
.../apache/karaf/features/internal/repo2.xml | 41 -
.../karaf/features/internal/service/f01.xml | 92 ++
.../karaf/features/internal/service/f02.xml | 164 +++
.../karaf/features/internal/service/f03.xml | 27 +
.../karaf/features/internal/service/f04.xml | 28 +
.../karaf/features/internal/service/f05.xml | 28 +
.../karaf/features/internal/service/f06.xml | 32 +
.../karaf/features/internal/service/f07.xml | 35 +
.../internal/service/overrides.properties | 23 +
.../karaf/features/internal/service/repo2.xml | 41 +
.../org/apache/karaf/features/repo3.xml | 27 +
features/obr/NOTICE | 71 -
features/obr/pom.xml | 100 --
.../features/obr/internal/BundleInfoImpl.java | 95 --
.../features/obr/internal/ObrResolver.java | 199 ---
.../OSGI-INF/blueprint/features-obr.xml | 50 -
.../obr/src/main/resources/OSGI-INF/bundle.info | 18 -
.../features/obr/internal/ObrResolverTest.java | 174 ---
features/pom.xml | 1 -
instance/core/pom.xml | 3 +
.../karaf/itests/ConditionalFeaturesTest.java | 2 +-
.../itests/FeatureSshCommandSecurityTest.java | 29 +-
.../org/apache/karaf/itests/FeatureTest.java | 12 +-
.../java/org/apache/karaf/itests/HttpTest.java | 2 +
.../apache/karaf/itests/KarafTestSupport.java | 44 +-
.../itests/features/Spring3FeaturesTest.java | 4 +
management/server/pom.xml | 3 +
pom.xml | 6 +
shell/console/pom.xml | 42 +-
shell/core/pom.xml | 3 +
.../src/it/test-aggregate-features/control.xml | 2 +-
.../src/it/test-basic-generation/control.xml | 2 +-
.../test-check-dependencies-failure/control.xml | 2 +-
.../src/it/test-check-dependencies/control.xml | 2 +-
.../src/it/test-input-file/control.xml | 2 +-
.../src/it/test-type-classifier/control.xml | 2 +-
.../features/ValidateDescriptorMojo.java | 4 +-
.../karaf/util/tracker/BaseActivator.java | 12 +-
webconsole/branding/pom.xml | 2 +-
.../webconsole/features/ExtendedFeature.java | 12 +
.../webconsole/features/FeaturesPlugin.java | 2 -
135 files changed, 10070 insertions(+), 5232 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/assemblies/features/enterprise/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/enterprise/src/main/feature/feature.xml b/assemblies/features/enterprise/src/main/feature/feature.xml
index 06905e4..69065ba 100644
--- a/assemblies/features/enterprise/src/main/feature/feature.xml
+++ b/assemblies/features/enterprise/src/main/feature/feature.xml
@@ -33,8 +33,11 @@
aries.transaction.howl.bufferSizeKBytes = 4
</config>
<bundle dependency="true" start-level="30">mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/${geronimo.jta-spec.version}</bundle>
- <bundle start-level="30">mvn:org.apache.aries.transaction/org.apache.aries.transaction.blueprint/${aries.transaction.blueprint.version}</bundle>
<bundle start-level="30">mvn:org.apache.aries.transaction/org.apache.aries.transaction.manager/${aries.transaction.manager.version}</bundle>
+ <conditional>
+ <condition>aries-blueprint</condition>
+ <bundle start-level="30">mvn:org.apache.aries.transaction/org.apache.aries.transaction.blueprint/${aries.transaction.blueprint.version}</bundle>
+ </conditional>
</feature>
<feature name="jpa" description="OSGi Persistence Container" version="${aries.jpa.version}" resolver="(obr)">
@@ -42,9 +45,12 @@
<bundle dependency="true" start-level="30">mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/${geronimo.jta-spec.version}</bundle>
<bundle dependency="true" start-level="30">mvn:org.apache.geronimo.specs/geronimo-jpa_2.0_spec/${geronimo.jpa-spec.version}</bundle>
<bundle start-level="30">mvn:org.apache.aries.jpa/org.apache.aries.jpa.api/${aries.jpa.api.version}</bundle>
- <bundle start-level="30">mvn:org.apache.aries.jpa/org.apache.aries.jpa.blueprint.aries/${aries.jpa.blueprint.aries.version}</bundle>
<bundle start-level="30">mvn:org.apache.aries.jpa/org.apache.aries.jpa.container/${aries.jpa.container.version}</bundle>
<bundle start-level="30">mvn:org.apache.aries.jpa/org.apache.aries.jpa.container.context/${aries.jpa.container.context.version}</bundle>
+ <conditional>
+ <condition>aries-blueprint</condition>
+ <bundle start-level="30">mvn:org.apache.aries.jpa/org.apache.aries.jpa.blueprint.aries/${aries.jpa.blueprint.aries.version}</bundle>
+ </conditional>
</feature>
<feature name="openjpa" description="Apache OpenJPA 2.2.x persistence engine support" version="2.2.2" resolver="(obr)">
@@ -161,12 +167,14 @@
<bundle dependency="true">mvn:com.fasterxml/classmate/1.0.0</bundle>
<bundle dependency="true">mvn:javax.el/javax.el-api/2.2.4</bundle>
<bundle dependency="true">mvn:org.glassfish.web/javax.el/2.2.4</bundle>
- <bundle dependency="true">mvn:org.hibernate/hibernate-validator/${hibernate.validator.version}</bundle>
<bundle dependency="true">mvn:org.jboss.logging/jboss-logging/3.1.4.GA</bundle>
+ <bundle>mvn:org.hibernate/hibernate-validator/${hibernate.validator.version}</bundle>
</feature>
<feature name="jndi" description="OSGi Service Registry JNDI access" version="${project.version}" resolver="(obr)">
<details>JNDI support provided by Apache Aries JNDI ${aries.jndi.version}, including additional service, commands, and MBean.</details>
+ <feature>aries-proxy</feature>
+ <feature>aries-blueprint</feature>
<bundle start-level="30">mvn:org.apache.xbean/xbean-naming/${xbean.version}</bundle>
<bundle start-level="30">mvn:org.apache.karaf.jndi/org.apache.karaf.jndi.core/${project.version}</bundle>
<bundle start-level="30">mvn:org.apache.aries.jndi/org.apache.aries.jndi.api/${aries.jndi.api.version}</bundle>
@@ -180,6 +188,7 @@
<feature name="jdbc" description="JDBC service and commands" version="${project.version}" resolver="(obr)">
<details>JDBC support providing service, commands, and MBean.</details>
<feature>transaction</feature>
+ <feature>aries-blueprint</feature>
<bundle>mvn:commons-pool/commons-pool/${commons-pool.version}</bundle>
<bundle>mvn:commons-dbcp/commons-dbcp/${commons-dbcp.version}</bundle>
<bundle>mvn:org.apache.karaf.jdbc/org.apache.karaf.jdbc.core/${project.version}</bundle>
@@ -189,6 +198,7 @@
<feature name="jms" description="JMS service and commands" version="${project.version}" resolver="(obr)">
<details>JMS support provinding service, commands, and MBean.</details>
<feature>transaction</feature>
+ <feature>aries-blueprint</feature>
<bundle>mvn:org.apache.geronimo.specs/geronimo-jms_1.1_spec/${geronimo.jms-spec.version}</bundle>
<bundle>mvn:org.apache.karaf.jms/org.apache.karaf.jms.core/${project.version}</bundle>
<bundle>mvn:org.apache.karaf.jms/org.apache.karaf.jms.command/${project.version}</bundle>
@@ -212,6 +222,7 @@
<details>Support of the Aries EBA archives</details>
<!-- pre-requisites-->
<feature version="${project.version}">obr</feature>
+ <feature>aries-blueprint</feature>
<!-- common -->
<bundle start-level="30">mvn:org.apache.aries.application/org.apache.aries.application.resolver.obr/${aries.application.version}</bundle>
<bundle start-level="30">mvn:org.apache.aries.application/org.apache.aries.application.install/${aries.application.version}</bundle>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/assemblies/features/standard/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml
index 3fae4aa..49028d2 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -16,7 +16,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<features name="standard-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+<features name="standard-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.3.0 http://karaf.apache.org/xmlns/features/v1.3.0">
<repository>mvn:org.ops4j.pax.web/pax-web-features/${pax.web.version}/xml/features</repository>
@@ -35,6 +35,9 @@
<bundle dependency="true" start-level="20">mvn:org.apache.aries/org.apache.aries.util/${aries.util.version}</bundle>
<bundle start-level="20">mvn:org.apache.aries.proxy/org.apache.aries.proxy.api/${aries.proxy.api.version}</bundle>
<bundle start-level="20">mvn:org.apache.aries.proxy/org.apache.aries.proxy.impl/${aries.proxy.version}</bundle>
+ <capability>
+ service-reference;effective:=active;objectClass=org.apache.aries.proxy.ProxyManager
+ </capability>
</feature>
<feature name="aries-blueprint" description="Aries Blueprint" version="${project.version}">
@@ -47,6 +50,9 @@
<condition>bundle</condition>
<bundle start-level="30">mvn:org.apache.karaf.bundle/org.apache.karaf.bundle.blueprintstate/${project.version}</bundle>
</conditional>
+ <capability>
+ service-reference;effective:=active;objectClass=org.apache.aries.blueprint.services.ParserService
+ </capability>
</feature>
<feature name="aries-annotation" description="Aries Annotations" version="${project.version}">
@@ -202,6 +208,7 @@
realm=karaf
</config>
<feature>http</feature>
+ <feature>aries-blueprint</feature>
<bundle start-level="30">mvn:org.apache.felix/org.apache.felix.metatype/${felix.metatype.version}</bundle>
<bundle start-level="30">mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.branding/${project.version}</bundle>
<bundle start-level="30">mvn:org.apache.karaf.webconsole/org.apache.karaf.webconsole.console/${project.version}</bundle>
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/pom.xml
----------------------------------------------------------------------
diff --git a/features/core/pom.xml b/features/core/pom.xml
index 8459b3d..fc9f9ce 100644
--- a/features/core/pom.xml
+++ b/features/core/pom.xml
@@ -65,6 +65,11 @@
<artifactId>org.apache.karaf.util</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.resolver</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
@@ -107,15 +112,19 @@
org.apache.karaf.features.management.codec;
-noimport:=true
</Export-Package>
+ <Provide-Capability>
+ service-reference;effective:=active;objectClass=org.apache.karaf.features.FeaturesService
+ </Provide-Capability>
<Private-Package>
- org.apache.karaf.features.internal,
- org.apache.karaf.features.internal.model,
- org.apache.karaf.features.internal.osgi,
- org.apache.karaf.features.management.internal,
+ org.apache.karaf.features.internal.*,
+ org.apache.felix.resolver,
org.apache.felix.utils.version,
org.apache.felix.utils.manifest,
org.apache.karaf.util.collections,
- org.apache.karaf.util.tracker
+ org.apache.karaf.util.json,
+ org.apache.karaf.util.tracker,
+ org.osgi.service.resolver,
+ org.osgi.service.repository
</Private-Package>
<Bundle-Activator>
org.apache.karaf.features.internal.osgi.Activator
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/Capability.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Capability.java b/features/core/src/main/java/org/apache/karaf/features/Capability.java
new file mode 100644
index 0000000..d329708
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/Capability.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public interface Capability {
+
+ String getValue();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/Feature.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Feature.java b/features/core/src/main/java/org/apache/karaf/features/Feature.java
index 4dfb624..2f9f001 100644
--- a/features/core/src/main/java/org/apache/karaf/features/Feature.java
+++ b/features/core/src/main/java/org/apache/karaf/features/Feature.java
@@ -55,4 +55,9 @@ public interface Feature {
int getStartLevel();
String getRegion();
+
+ List<? extends Capability> getCapabilities();
+
+ List<? extends Requirement> getRequirements();
+
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java b/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java
index 8e09748..282ff71 100644
--- a/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java
+++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesNamespaces.java
@@ -26,14 +26,16 @@ public interface FeaturesNamespaces {
String URI_1_0_0 = "http://karaf.apache.org/xmlns/features/v1.0.0";
String URI_1_1_0 = "http://karaf.apache.org/xmlns/features/v1.1.0";
String URI_1_2_0 = "http://karaf.apache.org/xmlns/features/v1.2.0";
+ String URI_1_3_0 = "http://karaf.apache.org/xmlns/features/v1.3.0";
- String URI_CURRENT = URI_1_2_0;
+ String URI_CURRENT = URI_1_3_0;
QName FEATURES_0_0_0 = new QName("features");
QName FEATURES_1_0_0 = new QName(URI_1_0_0, "features");
QName FEATURES_1_1_0 = new QName(URI_1_1_0, "features");
QName FEATURES_1_2_0 = new QName(URI_1_2_0, "features");
+ QName FEATURES_1_3_0 = new QName(URI_1_3_0, "features");
- QName FEATURES_CURRENT = FEATURES_1_2_0;
+ QName FEATURES_CURRENT = FEATURES_1_3_0;
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/Requirement.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/Requirement.java b/features/core/src/main/java/org/apache/karaf/features/Requirement.java
new file mode 100644
index 0000000..4446335
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/Requirement.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public interface Requirement {
+
+ String getValue();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/Artifact.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/Artifact.java b/features/core/src/main/java/org/apache/karaf/features/internal/Artifact.java
deleted file mode 100644
index 94e72e3..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/Artifact.java
+++ /dev/null
@@ -1,56 +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.net.URI;
-
-/**
- * Simple abstraction of a maven artifact to avoid external deps
- */
-public class Artifact {
- String groupId;
- String artifactId;
- String version;
- String extension;
- String classifier;
-
- public Artifact(String coords) {
- String[] coordsAr = coords.split(":");
- if (coordsAr.length != 5) {
- throw new IllegalArgumentException("Maven URL " + coords + " is malformed or not complete");
- }
- this.groupId = coordsAr[0];
- this.artifactId = coordsAr[1];
- this.version = coordsAr[4];
- this.extension = coordsAr[2];
- this.classifier = coordsAr[3];
- }
-
- public Artifact(String coords, String version) {
- this(coords);
- this.version = version;
- }
-
- public URI getPaxUrlForArtifact(String version) {
- String uriSt = "mvn:" + this.groupId + "/" + this.artifactId + "/" + version + "/" + this.extension + "/" + this.classifier;
- try {
- return new URI(uriSt);
- } catch (Exception e) {
- return null;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/BootFeaturesInstaller.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/BootFeaturesInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/BootFeaturesInstaller.java
deleted file mode 100644
index 1b4fa9d..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/BootFeaturesInstaller.java
+++ /dev/null
@@ -1,171 +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.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.karaf.features.BootFinished;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeaturesService;
-import org.apache.karaf.features.FeaturesService.Option;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Manages installation of the boot features in the background
- */
-public class BootFeaturesInstaller {
- private static final Logger LOGGER = LoggerFactory.getLogger(BootFeaturesInstaller.class);
-
- public static String VERSION_PREFIX = "version=";
-
- private final BundleContext bundleContext;
- private final FeaturesService featuresService;
- private final String boot;
- private final boolean bootAsynchronous;
-
- /**
- *
- * @param featuresService
- * @param boot list of boot features separated by comma. Optionally contains ;version=x.x.x to specify a specific feature version
- */
- public BootFeaturesInstaller(BundleContext bundleContext, FeaturesService featuresService, String boot, boolean bootAsynchronous) {
- this.bundleContext = bundleContext;
- this.featuresService = featuresService;
- this.boot = boot;
- this.bootAsynchronous = bootAsynchronous;
- }
-
- /**
- * Install boot features
- * @throws Exception
- */
- public void start() {
- if (boot != null) {
- if (bootAsynchronous) {
- new Thread() {
- public void run() {
- installBootFeatures();
- publishBootFinished();
- }
- }.start();
- } else {
- installBootFeatures();
- publishBootFinished();
- }
- } else {
- publishBootFinished();
- }
- }
-
- void installBootFeatures() {
- try {
- List<Feature> installedFeatures = Arrays.asList(featuresService.listInstalledFeatures());
- List<Set<String>> stagedFeatureNames = parseBootFeatures(boot);
- List<Set<Feature>> stagedFeatures = toFeatureSetList(stagedFeatureNames);
-
- for (Set<Feature> features : stagedFeatures) {
- features.removeAll(installedFeatures);
- featuresService.installFeatures(features, EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
- }
- } catch (Exception e) {
- LOGGER.error("Error installing boot features", e);
- }
- }
-
- private List<Set<Feature>> toFeatureSetList(List<Set<String>> stagedFeatures) {
- ArrayList<Set<Feature>> result = new ArrayList<Set<Feature>>();
- for (Set<String> features : stagedFeatures) {
- HashSet<Feature> featureSet = new HashSet<Feature>();
- for (String featureName : features) {
- try {
- Feature feature = getFeature(featureName);
- if (feature == null) {
- LOGGER.error("Error Boot feature " + featureName + " not found");
- } else {
- featureSet.add(feature);
- }
- } catch (Exception e) {
- LOGGER.error("Error getting feature for feature string " + featureName, e);
- }
- }
- result.add(featureSet);
- }
- return result;
- }
-
- /**
- *
- * @param featureSt either feature name or <featurename>;version=<version>
- * @return feature matching the feature string
- * @throws Exception
- */
- private Feature getFeature(String featureSt) throws Exception {
- String[] parts = featureSt.trim().split(";");
- String featureName = parts[0];
- String featureVersion = null;
- for (String part : parts) {
- // if the part starts with "version=" it contains the version info
- if (part.startsWith(VERSION_PREFIX)) {
- featureVersion = part.substring(VERSION_PREFIX.length());
- }
- }
- if (featureVersion == null) {
- // no version specified - use default version
- featureVersion = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION;
- }
- return featuresService.getFeature(featureName, featureVersion);
- }
-
- protected List<Set<String>> parseBootFeatures(String bootFeatures) {
- Pattern pattern = Pattern.compile("(\\((.+))\\),|.+");
- Matcher matcher = pattern.matcher(bootFeatures);
- List<Set<String>> result = new ArrayList<Set<String>>();
- while (matcher.find()) {
- String group = matcher.group(2) != null ? matcher.group(2) : matcher.group();
- result.add(parseFeatureList(group));
- }
- return result;
- }
-
- private Set<String> parseFeatureList(String group) {
- HashSet<String> features = new HashSet<String>();
- for (String feature : Arrays.asList(group.trim().split("\\s*,\\s*"))) {
- if (feature.length() > 0) {
- features.add(feature);
- }
- }
- return features;
- }
-
- private void publishBootFinished() {
- if (bundleContext != null) {
- BootFinished bootFinished = new BootFinished() {};
- bundleContext.registerService(BootFinished.class, bootFinished, new Hashtable<String, String>());
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/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
deleted file mode 100644
index be4fca7..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/BundleManager.java
+++ /dev/null
@@ -1,498 +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.File;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.jar.JarInputStream;
-import java.util.jar.Manifest;
-
-import org.apache.felix.utils.manifest.Clause;
-import org.apache.felix.utils.manifest.Parser;
-import org.apache.felix.utils.version.VersionRange;
-import org.apache.karaf.features.FeaturesService.Option;
-import org.apache.karaf.features.RegionsPersistence;
-import org.apache.karaf.features.Resolver;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.FrameworkListener;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.Version;
-import org.osgi.framework.startlevel.BundleStartLevel;
-import org.osgi.framework.wiring.FrameworkWiring;
-import org.osgi.service.url.URLStreamHandlerService;
-import org.osgi.util.tracker.ServiceTracker;
-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 long refreshTimeout;
- private RegionsPersistence regionsPersistence;
-
- public BundleManager(BundleContext bundleContext) {
- this(bundleContext, 5000);
- }
-
- public BundleManager(BundleContext bundleContext, long refreshTimeout) {
- this.bundleContext = bundleContext;
- this.refreshTimeout = refreshTimeout;
- }
-
- public void setRegionsPersistence(RegionsPersistence regionsPersistence) {
- this.regionsPersistence = regionsPersistence;
- }
-
- public BundleInstallerResult installBundleIfNeeded(String bundleLocation, int startLevel, String regionName) throws IOException, BundleException {
- BundleInstallerResult result = doInstallBundleIfNeeded(bundleLocation, startLevel, regionName);
- installToRegion(regionName, result.bundle, result.isNew);
- return result;
- }
-
- private void installToRegion(String region, Bundle bundle, boolean isNew) throws BundleException {
- if (region != null && isNew) {
- if (regionsPersistence != null) {
- regionsPersistence.install(bundle, region);
- }
- }
- }
-
- private BundleInstallerResult doInstallBundleIfNeeded(String bundleLocation, int startLevel, String regionName) 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;
- }
-
- /**
- * 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) {
- 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.osgi.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/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/EventAdminListener.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/EventAdminListener.java b/features/core/src/main/java/org/apache/karaf/features/internal/EventAdminListener.java
deleted file mode 100644
index d463282..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/EventAdminListener.java
+++ /dev/null
@@ -1,92 +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.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.karaf.features.EventConstants;
-import org.apache.karaf.features.FeatureEvent;
-import org.apache.karaf.features.FeaturesListener;
-import org.apache.karaf.features.RepositoryEvent;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.util.tracker.ServiceTracker;
-
-/**
- * A listener to publish events to EventAdmin
- */
-public class EventAdminListener implements FeaturesListener {
-
- private final ServiceTracker<EventAdmin, EventAdmin> tracker;
-
- public EventAdminListener(BundleContext context) {
- tracker = new ServiceTracker<EventAdmin, EventAdmin>(context, EventAdmin.class.getName(), null);
- tracker.open();
- }
-
- public void featureEvent(FeatureEvent event) {
- EventAdmin eventAdmin = tracker.getService();
- if (eventAdmin == null) {
- return;
- }
- Dictionary<String, Object> props = new Hashtable<String, Object>();
- props.put(EventConstants.TYPE, event.getType());
- props.put(EventConstants.EVENT, event);
- props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
- props.put(EventConstants.FEATURE_NAME, event.getFeature().getName());
- props.put(EventConstants.FEATURE_VERSION, event.getFeature().getVersion());
- String topic;
- switch (event.getType()) {
- case FeatureInstalled:
- topic = EventConstants.TOPIC_FEATURES_INSTALLED;
- break;
- case FeatureUninstalled:
- topic = EventConstants.TOPIC_FEATURES_UNINSTALLED;
- break;
- default:
- throw new IllegalStateException("Unknown features event type: " + event.getType());
- }
- eventAdmin.postEvent(new Event(topic, props));
- }
-
- public void repositoryEvent(RepositoryEvent event) {
- EventAdmin eventAdmin = tracker.getService();
- if (eventAdmin == null) {
- return;
- }
- Dictionary<String, Object> props = new Hashtable<String, Object>();
- props.put(EventConstants.TYPE, event.getType());
- props.put(EventConstants.EVENT, event);
- props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
- props.put(EventConstants.REPOSITORY_NAME, event.getRepository().getName());
- props.put(EventConstants.REPOSITORY_URI, event.getRepository().getURI().toString());
- String topic;
- switch (event.getType()) {
- case RepositoryAdded:
- topic = EventConstants.TOPIC_REPOSITORY_ADDED;
- break;
- case RepositoryRemoved:
- topic = EventConstants.TOPIC_REPOSITORY_REMOVED;
- break;
- default:
- throw new IllegalStateException("Unknown repository event type: " + event.getType());
- }
- eventAdmin.postEvent(new Event(topic, props));
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
deleted file mode 100644
index 5946a3a..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureConfigInstaller.java
+++ /dev/null
@@ -1,166 +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.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.karaf.features.ConfigFileInfo;
-import org.apache.karaf.features.Feature;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FeatureConfigInstaller {
- private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
- private static final String CONFIG_KEY = "org.apache.karaf.features.configKey";
-
- private final ConfigurationAdmin configAdmin;
-
- public FeatureConfigInstaller(ConfigurationAdmin configAdmin) {
- this.configAdmin = configAdmin;
- }
-
- private String[] parsePid(String pid) {
- int n = pid.indexOf('-');
- if (n > 0) {
- String factoryPid = pid.substring(n + 1);
- pid = pid.substring(0, n);
- return new String[]{pid, factoryPid};
- } else {
- return new String[]{pid, null};
- }
- }
-
- private Configuration createConfiguration(ConfigurationAdmin configurationAdmin,
- String pid, String factoryPid) throws IOException, InvalidSyntaxException {
- if (factoryPid != null) {
- return configurationAdmin.createFactoryConfiguration(factoryPid, null);
- } else {
- return configurationAdmin.getConfiguration(pid, null);
- }
- }
-
- private Configuration findExistingConfiguration(ConfigurationAdmin configurationAdmin,
- String pid, String factoryPid) throws IOException, InvalidSyntaxException {
- String filter;
- if (factoryPid == null) {
- filter = "(" + Constants.SERVICE_PID + "=" + pid + ")";
- } else {
- String key = createConfigurationKey(pid, factoryPid);
- filter = "(" + CONFIG_KEY + "=" + key + ")";
- }
- Configuration[] configurations = configurationAdmin.listConfigurations(filter);
- if (configurations != null && configurations.length > 0) {
- return configurations[0];
- }
- return null;
- }
-
- void installFeatureConfigs(Feature feature, boolean verbose) throws IOException, InvalidSyntaxException {
- for (String config : feature.getConfigurations().keySet()) {
- Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));
- String[] pid = parsePid(config);
- Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]);
- if (cfg == null) {
- cfg = createConfiguration(configAdmin, pid[0], pid[1]);
- String key = createConfigurationKey(pid[0], pid[1]);
- props.put(CONFIG_KEY, key);
- if (cfg.getBundleLocation() != null) {
- cfg.setBundleLocation(null);
- }
- cfg.update(props);
- }
- }
- for (ConfigFileInfo configFile : feature.getConfigurationFiles()) {
- installConfigurationFile(configFile.getLocation(), configFile.getFinalname(), configFile.isOverride(), verbose);
- }
- }
-
- private String createConfigurationKey(String pid, String factoryPid) {
- return factoryPid == null ? pid : pid + "-" + factoryPid;
- }
-
- private void installConfigurationFile(String fileLocation, String finalname, boolean override, boolean verbose) throws IOException {
- LOGGER.debug("Checking configuration file " + fileLocation);
- if (verbose) {
- System.out.println("Checking configuration file " + fileLocation);
- }
-
- String basePath = System.getProperty("karaf.base");
-
- if (finalname.indexOf("${") != -1) {
- //remove any placeholder or variable part, this is not valid.
- int marker = finalname.indexOf("}");
- finalname = finalname.substring(marker+1);
- }
-
- finalname = basePath + File.separator + finalname;
-
- File file = new File(finalname);
- if (file.exists() && !override) {
- LOGGER.debug("configFile already exist, don't override it");
- return;
- }
-
- InputStream is = null;
- FileOutputStream fop = null;
- try {
- is = new BufferedInputStream(new URL(fileLocation).openStream());
-
- if (!file.exists()) {
- File parentFile = file.getParentFile();
- if (parentFile != null)
- parentFile.mkdirs();
- file.createNewFile();
- }
-
- fop = new FileOutputStream(file);
-
- int bytesRead;
- byte[] buffer = new byte[1024];
-
- while ((bytesRead = is.read(buffer)) != -1) {
- fop.write(buffer, 0, bytesRead);
- }
- } catch (RuntimeException e) {
- LOGGER.error(e.getMessage());
- throw e;
- } catch (MalformedURLException e) {
- LOGGER.error(e.getMessage());
- throw e;
- } finally {
- if (is != null)
- is.close();
- if (fop != null) {
- fop.flush();
- fop.close();
- }
- }
-
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/FeatureFinder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureFinder.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeatureFinder.java
deleted file mode 100644
index d3215fa..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureFinder.java
+++ /dev/null
@@ -1,59 +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.net.URI;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
-
-public class FeatureFinder implements ManagedService {
-
- Map<String, String> nameToArtifactMap = new HashMap<String, String>();
-
- public String[] getNames() {
- return nameToArtifactMap.keySet().toArray(new String[] {});
- }
-
- public URI getUriFor(String name, String version) {
- String coords = nameToArtifactMap.get(name);
- if (coords == null) {
- return null;
- }
- Artifact artifact = new Artifact(coords);
- return artifact.getPaxUrlForArtifact(version);
- }
-
- @SuppressWarnings("rawtypes")
- public void updated(Dictionary properties) throws ConfigurationException {
- if (properties != null) {
- nameToArtifactMap.clear();
- Enumeration keys = properties.keys();
- while (keys.hasMoreElements()) {
- String key = (String)keys.nextElement();
- if (!"felix.fileinstall.filename".equals(key) && !"service.pid".equals(key)) {
- nameToArtifactMap.put(key, (String)properties.get(key));
- }
- }
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java b/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java
deleted file mode 100644
index dfb7a7d..0000000
--- a/features/core/src/main/java/org/apache/karaf/features/internal/FeatureValidationUtil.java
+++ /dev/null
@@ -1,110 +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.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URLConnection;
-import javax.xml.XMLConstants;
-import javax.xml.namespace.QName;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-
-import org.apache.karaf.features.FeaturesNamespaces;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-/**
- * Utility class which fires XML Schema validation.
- */
-public class FeatureValidationUtil {
-
- public static final QName FEATURES_0_0 = new QName("features");
- public static final QName FEATURES_1_0 = new QName("http://karaf.apache.org/xmlns/features/v1.0.0", "features");
- public static final QName FEATURES_1_1 = new QName("http://karaf.apache.org/xmlns/features/v1.1.0", "features");
- public static final QName FEATURES_1_2 = new QName("http://karaf.apache.org/xmlns/features/v1.2.0", "features");
- private static final Logger LOGGER = LoggerFactory.getLogger(FeatureValidationUtil.class);
-
- /**
- * Runs schema validation.
- *
- * @param uri Uri to validate.
- * @throws Exception When validation fails.
- */
- public static void validate(URI uri) throws Exception {
- Document doc = load(uri);
-
- QName name = new QName(doc.getDocumentElement().getNamespaceURI(), doc.getDocumentElement().getLocalName());
-
- if (FeaturesNamespaces.FEATURES_0_0_0.equals(name)) {
- LOGGER.warn("Old style feature file without namespace found (URI: {}). This format is deprecated and support for it will soon be removed", uri);
- return;
- } else if (FeaturesNamespaces.FEATURES_1_0_0.equals(name)) {
- validate(doc, "/org/apache/karaf/features/karaf-features-1.0.0.xsd");
- } else if (FeaturesNamespaces.FEATURES_1_1_0.equals(name)) {
- validate(doc, "/org/apache/karaf/features/karaf-features-1.1.0.xsd");
- } else if (FeaturesNamespaces.FEATURES_1_2_0.equals(name)) {
- validate(doc, "/org/apache/karaf/features/karaf-features-1.2.0.xsd");
- }
- else {
- throw new IllegalArgumentException("Unrecognized root element: " + name);
- }
- }
-
- private static Document load(URI uri) throws IOException, SAXException, ParserConfigurationException {
- InputStream stream = null;
- try {
- URLConnection conn;
- try {
- conn = uri.toURL().openConnection();
- } catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("invalid URI: " + uri, e);
- }
- conn.setDefaultUseCaches(false);
- stream = conn.getInputStream();
- // load document and check the root element for namespace declaration
- DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
- dFactory.setNamespaceAware(true);
- return dFactory.newDocumentBuilder().parse(stream);
- } finally {
- if (stream != null) {
- stream.close();
- }
- }
- }
-
- private static void validate(Document doc, String schemaLocation) throws SAXException {
- SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
- // root element has namespace - we can use schema validation
- Schema schema = factory.newSchema(new StreamSource(FeatureValidationUtil.class.getResourceAsStream(schemaLocation)));
- // create schema by reading it from an XSD file:
- Validator validator = schema.newValidator();
- try {
- validator.validate(new DOMSource(doc));
- } catch (Exception e) {
- throw new IllegalArgumentException("Unable to validate " + doc.getDocumentURI(), e);
- }
- }
-
-}
[06/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/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 bce3735..db7a4fc 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
@@ -30,28 +30,25 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URI;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
-import org.apache.karaf.features.internal.BundleManager;
-import org.apache.karaf.features.internal.BundleManager.BundleInstallerResult;
-import org.apache.karaf.features.internal.FeaturesServiceImpl;
-import org.apache.karaf.features.internal.TestBase;
+import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
+import org.apache.karaf.features.internal.service.StateStorage;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
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'>"
@@ -74,6 +71,9 @@ public class FeaturesServiceTest extends TestBase {
return tmp.toURI();
}
+ /*
+ TODO: migrate those tests
+
@Test
public void testInstallFeature() throws Exception {
URI uri = createTempRepo(
@@ -322,27 +322,20 @@ public class FeaturesServiceTest extends TestBase {
+ " <feature name='f2' version='0.2'><bundle>%s</bundle></feature>"
+ "</features>", bundleVer01Uri, bundleVer02Uri);
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
- Bundle bundleVer01 = createDummyBundle(12345L, "bundleVer01", headers());
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
- expect(bundleManager.installBundleIfNeeded(bundleVer01Uri, 0, null)).andReturn(new BundleInstallerResult(bundleVer01, true));
- expect(bundleManager.installBundleIfNeeded(bundleVer01Uri, 0, null)).andReturn(new BundleInstallerResult(bundleVer01, false));
- expect(bundleManager.getBundleContext()).andReturn(bundleContext);
- ignoreRefreshes(bundleManager);
- bundleManager.uninstall(Collections.EMPTY_LIST, true);
-
- EasyMock.expectLastCall().times(2);
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ replay(bundleContext);
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
+ FeaturesServiceImpl svc = new FeaturesServiceImpl(null, bundleContext, new Storage(), null, null, null, null);
svc.addRepository(uri);
svc.installFeature("f2", "0.1");
svc.installFeature("f1", "0.1");
svc.uninstallFeature("f1", "0.1");
svc.uninstallFeature("f2", "0.1");
- verify(bundleManager);
+
+ verify(bundleContext);
}
+ */
@Test
public void testGetFeaturesShouldHandleDifferentVersionPatterns() throws Exception {
@@ -351,14 +344,9 @@ public class FeaturesServiceTest extends TestBase {
+ " <feature name='f2' version='0.1'><bundle>bundle1</bundle></feature>"
+ " <feature name='f2' version='0.2'><bundle>bundle2</bundle></feature>"
+ "</features>");
-
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
-
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, null);
svc.addRepository(uri);
- verify(bundleManager);
assertEquals(feature("f2", "0.2"), svc.getFeature("f2", "[0.1,0.3)"));
assertEquals(feature("f2", "0.2"), svc.getFeature("f2", "0.0.0"));
@@ -367,104 +355,17 @@ public class FeaturesServiceTest extends TestBase {
}
@Test
- public void testInstallBatchFeatureWithContinueOnFailureNoClean() throws Exception {
- String bundle1Uri = "bundle1";
- String bundle2Uri = "bundle2";
-
- URI uri = createTempRepo(FEATURE_WITH_INVALID_BUNDLE, bundle1Uri, bundle2Uri);
-
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- Bundle installedBundle1 = createDummyBundle(12345L, "bundle1", headers());
- Bundle installedBundle2 = createDummyBundle(54321L, "bundle2", headers());
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
- expect(bundleManager.installBundleIfNeeded(bundle1Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle1, true));
- expect(bundleManager.installBundleIfNeeded(bundle2Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle2, true));
- expect(bundleManager.installBundleIfNeeded("zfs:unknown", 0, null)).andThrow(new MalformedURLException());
- EasyMock.expectLastCall();
- ignoreRefreshes(bundleManager);
-
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
- svc.addRepository(uri);
- svc.installFeatures(new CopyOnWriteArraySet<Feature>(Arrays.asList(svc.listFeatures())),
- EnumSet.of(FeaturesService.Option.ContinueBatchOnFailure, FeaturesService.Option.NoCleanIfFailure));
- verify(bundleManager);
- }
-
- @Test
- public void testInstallBatchFeatureWithContinueOnFailureClean() throws Exception {
- String bundle1Uri = "file:bundle1";
- String bundle2Uri = "file:bundle2";
-
- URI uri = createTempRepo(FEATURE_WITH_INVALID_BUNDLE, bundle1Uri, bundle2Uri);
-
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- Bundle installedBundle1 = createDummyBundle(12345L, "bundle1", headers());
- Bundle installedBundle2 = createDummyBundle(54321L, "bundle2", headers());
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
- expect(bundleManager.installBundleIfNeeded(bundle1Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle1, true));
- expect(bundleManager.installBundleIfNeeded(bundle2Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle2, true));
- expect(bundleManager.installBundleIfNeeded("zfs:unknown", 0, null)).andThrow(new MalformedURLException());
- bundleManager.uninstall(setOf(installedBundle1));
- EasyMock.expectLastCall();
- ignoreRefreshes(bundleManager);
-
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
- svc.addRepository(uri);
- svc.installFeatures(new CopyOnWriteArraySet<Feature>(Arrays.asList(svc.listFeatures())),
- EnumSet.of(FeaturesService.Option.ContinueBatchOnFailure));
- verify(bundleManager);
- }
-
- @Test
- public void testInstallBatchFeatureWithoutContinueOnFailureNoClean() throws Exception {
- String bundle1Uri = "file:bundle1";
- String bundle2Uri = "file:bundle2";
-
- URI uri = createTempRepo(FEATURE_WITH_INVALID_BUNDLE, bundle1Uri, bundle2Uri);
-
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- Bundle installedBundle1 = createDummyBundle(12345L, bundle1Uri, headers());
- Bundle installedBundle2 = createDummyBundle(54321L, bundle2Uri, headers());
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
- expect(bundleManager.installBundleIfNeeded(bundle1Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle1, true));
- expect(bundleManager.installBundleIfNeeded(bundle2Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle2, true));
- expect(bundleManager.installBundleIfNeeded("zfs:unknown", 0, null)).andThrow(new MalformedURLException());
-
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
- svc.addRepository(uri);
- try {
- List<Feature> features = Arrays.asList(svc.listFeatures());
- Collections.reverse(features);
- svc.installFeatures(new CopyOnWriteArraySet<Feature>(features),
- EnumSet.of(FeaturesService.Option.NoCleanIfFailure));
- fail("Call should have thrown an exception");
- } catch (MalformedURLException e) {
- }
- verify(bundleManager);
- }
-
- @Test
- public void testInstallBatchFeatureWithoutContinueOnFailureClean() throws Exception {
+ public void testInstallBatchFeatureWithFailure() throws Exception {
String bundle1Uri = "file:bundle1";
String bundle2Uri = "file:bundle2";
URI uri = createTempRepo(FEATURE_WITH_INVALID_BUNDLE, bundle1Uri, bundle2Uri);
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- Bundle installedBundle1 = createDummyBundle(12345L, "bundle1", headers());
- Bundle installedBundle2 = createDummyBundle(54321L, "bundle2", headers());
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
- expect(bundleManager.installBundleIfNeeded(bundle1Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle1, true));
- expect(bundleManager.installBundleIfNeeded(bundle2Uri, 0, null)).andReturn(new BundleInstallerResult(installedBundle2, true));
- expect(bundleManager.installBundleIfNeeded("zfs:unknown", 0, null)).andThrow(new MalformedURLException());
- bundleManager.uninstall(setOf(installedBundle1, installedBundle2));
- EasyMock.expectLastCall();
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ replay(bundleContext);
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
+ FeaturesServiceImpl svc = new FeaturesServiceImpl(null, bundleContext, new Storage(), null, null, null, null);
svc.addRepository(uri);
try {
List<Feature> features = Arrays.asList(svc.listFeatures());
@@ -474,7 +375,7 @@ public class FeaturesServiceTest extends TestBase {
fail("Call should have thrown an exception");
} catch (MalformedURLException e) {
}
- verify(bundleManager);
+ verify(bundleContext);
}
/**
@@ -485,18 +386,13 @@ public class FeaturesServiceTest extends TestBase {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <featur><bundle>somebundle</bundle></featur></features>");
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
-
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
+ FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, null);
try {
svc.addRepository(uri);
fail("exception expected");
} catch (Exception e) {
assertTrue(e.getMessage().contains("Unable to validate"));
}
- verify(bundleManager);
}
/**
@@ -508,11 +404,7 @@ public class FeaturesServiceTest extends TestBase {
+ " <feature name='f1'><bundle>file:bundle1</bundle><bundle>file:bundle2</bundle></feature>"
+ "</features>");
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
-
- replay(bundleManager);
- FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
+ FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, null);
svc.addRepository(uri);
Feature feature = svc.getFeature("f1");
Assert.assertNotNull("No feature named fi found", feature);
@@ -520,4 +412,15 @@ public class FeaturesServiceTest extends TestBase {
Assert.assertEquals(2, bundles.size());
}
+ static class Storage extends StateStorage {
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ return null;
+ }
+ @Override
+ protected OutputStream getOutputStream() throws IOException {
+ return null;
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java b/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
index cf97a20..164dc79 100644
--- a/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/RepositoryTest.java
@@ -19,7 +19,9 @@ package org.apache.karaf.features;
import java.net.URI;
import junit.framework.TestCase;
-import org.apache.karaf.features.internal.RepositoryImpl;
+import org.apache.karaf.features.internal.resolver.FeatureResource;
+import org.apache.karaf.features.internal.service.RepositoryImpl;
+import org.osgi.resource.Resource;
public class RepositoryTest extends TestCase {
@@ -107,7 +109,25 @@ public class RepositoryTest extends TestCase {
assertEquals(true, features[2].getConfigurationFiles().get(0).isOverride());
assertEquals("cfloc", features[2].getConfigurationFiles().get(0).getLocation());
}
-
+
+ public void testLoadRepoWithCapabilitiesAndRequirement() throws Exception {
+ RepositoryImpl r = new RepositoryImpl(getClass().getResource("repo3.xml").toURI());
+ // Check features
+ Feature[] features = r.getFeatures();
+ assertNotNull(features);
+ assertEquals(1, features.length);
+ assertNotNull(features[0]);
+ assertEquals("f1", features[0].getName());
+ assertEquals(1, features[0].getCapabilities().size());
+ assertEquals("cap", features[0].getCapabilities().get(0).getValue().trim());
+ assertEquals(1, features[0].getRequirements().size());
+ assertEquals("req", features[0].getRequirements().get(0).getValue().trim());
+
+ Resource res = FeatureResource.build(features[0], null, null);
+ assertEquals(1, res.getCapabilities("cap").size());
+ assertEquals(1, res.getRequirements("req").size());
+ }
+
public void testShowWrongUriInException() throws Exception {
String uri = "src/test/resources/org/apache/karaf/shell/features/repo1.xml";
RepositoryImpl r = new RepositoryImpl(new URI(uri));
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/TestBase.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/TestBase.java b/features/core/src/test/java/org/apache/karaf/features/TestBase.java
new file mode 100644
index 0000000..ac8f3d9
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/TestBase.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.easymock.EasyMock;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.startlevel.BundleStartLevel;
+
+import static java.util.Arrays.asList;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+public class TestBase {
+ public Bundle createDummyBundle(long id, final String symbolicName, Dictionary<String,String> headers) {
+ Bundle bundle = EasyMock.createNiceMock(Bundle.class);
+
+ // Be aware that this means all bundles are treated as different
+ expect(bundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(1).anyTimes();
+
+ expect(bundle.getBundleId()).andReturn(id).anyTimes();
+ expect(bundle.getSymbolicName()).andReturn(symbolicName).anyTimes();
+ expect(bundle.getHeaders()).andReturn(headers).anyTimes();
+ BundleStartLevel sl = EasyMock.createMock(BundleStartLevel.class);
+ expect(sl.isPersistentlyStarted()).andReturn(true).anyTimes();
+ expect(bundle.adapt(BundleStartLevel.class)).andReturn(sl).anyTimes();
+ replay(bundle, sl);
+ return bundle;
+ }
+
+ public Dictionary<String, String> headers(String ... keyAndHeader) {
+ Hashtable<String, String> headersTable = new Hashtable<String, String>();
+ int c=0;
+ while (c < keyAndHeader.length) {
+ String key = keyAndHeader[c++];
+ String value = keyAndHeader[c++];
+ headersTable.put(key, value);
+ }
+ return headersTable;
+ }
+
+ public Map<String, Map<String, Feature>> features(Feature ... features) {
+ final Map<String, Map<String, Feature>> featuresMap = new HashMap<String, Map<String,Feature>>();
+ for (Feature feature : features) {
+ Map<String, Feature> featureVersion = getOrCreate(featuresMap, feature);
+ featureVersion.put(feature.getVersion(), feature);
+ }
+ return featuresMap;
+ }
+
+ private Map<String, Feature> getOrCreate(final Map<String, Map<String, Feature>> featuresMap, Feature feature) {
+ Map<String, Feature> featureVersion = featuresMap.get(feature.getName());
+ if (featureVersion == null) {
+ featureVersion = new HashMap<String, Feature>();
+ featuresMap.put(feature.getName(), featureVersion);
+ }
+ return featureVersion;
+ }
+
+ public Feature feature(String name) {
+ return feature(name, null);
+ }
+
+ public Feature feature(String name, String version) {
+ return new org.apache.karaf.features.internal.model.Feature(name, version);
+ }
+
+ public Set<Bundle> setOf(Bundle ... elements) {
+ return new HashSet<Bundle>(Arrays.asList(elements));
+ }
+
+ public Set<Long> setOf(Long ... elements) {
+ return new HashSet<Long>(Arrays.asList(elements));
+ }
+
+ public Set<String> setOf(String ... elements) {
+ return new HashSet<String>(asList(elements));
+ }
+
+ public Set<Feature> setOf(Feature ... elements) {
+ return new HashSet<Feature>(Arrays.asList(elements));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/BootFeaturesInstallerTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/BootFeaturesInstallerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/BootFeaturesInstallerTest.java
deleted file mode 100644
index b59806d..0000000
--- a/features/core/src/test/java/org/apache/karaf/features/internal/BootFeaturesInstallerTest.java
+++ /dev/null
@@ -1,103 +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 static java.util.Arrays.asList;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-
-import java.util.EnumSet;
-
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeaturesService.Option;
-import org.easymock.EasyMock;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class BootFeaturesInstallerTest extends TestBase {
-
- @Test
- @SuppressWarnings("unchecked")
- public void testParser() {
- BootFeaturesInstaller installer = new BootFeaturesInstaller(null, null, "", false);
- Assert.assertEquals(asList(setOf("test1", "test2"),setOf("test3")), installer.parseBootFeatures("(test1, test2), test3"));
- Assert.assertEquals(asList(setOf("test1", "test2", "test3")), installer.parseBootFeatures("test1, test2, test3"));
- }
-
- @Test
- public void testDefaultBootFeatures() throws Exception {
- FeaturesServiceImpl impl = EasyMock.createMock(FeaturesServiceImpl.class);
- Feature configFeature = feature("config", "1.0.0");
- Feature standardFeature = feature("standard", "1.0.0");
- Feature regionFeature = feature("region", "1.0.0");
- expect(impl.listInstalledFeatures()).andStubReturn(new Feature[]{});
- expect(impl.getFeature("config", "0.0.0")).andReturn(configFeature);
- expect(impl.getFeature("standard", "0.0.0")).andReturn(standardFeature);
- expect(impl.getFeature("region", "0.0.0")).andReturn(regionFeature);
-
- impl.installFeatures(setOf(configFeature, standardFeature, regionFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
- EasyMock.expectLastCall();
-
- replay(impl);
- BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl, "config,standard,region", false);
- bootFeatures.installBootFeatures();
- EasyMock.verify(impl);
- }
-
- /**
- * This test checks KARAF-388 which allows you to specify version of boot feature.
- * @throws Exception
- */
- @Test
- public void testStartDoesNotFailWithNonExistentVersion() throws Exception {
- FeaturesServiceImpl impl = EasyMock.createMock(FeaturesServiceImpl.class);
- expect(impl.listInstalledFeatures()).andReturn(new Feature[]{});
- EasyMock.expectLastCall();
- Feature sshFeature = feature("ssh", "1.0.0");
- expect(impl.getFeature("ssh", "1.0.0")).andReturn(sshFeature);
- expect(impl.getFeature("transaction", "1.2")).andReturn(null);
-
- // Only the ssh feature should get installed
- impl.installFeatures(setOf(sshFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
- EasyMock.expectLastCall();
-
- replay(impl);
- BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl , "transaction;version=1.2,ssh;version=1.0.0", false);
- bootFeatures.installBootFeatures();
- EasyMock.verify(impl);
- }
-
- @Test
- public void testStagedBoot() throws Exception {
- FeaturesServiceImpl impl = EasyMock.createStrictMock(FeaturesServiceImpl.class);
- Feature sshFeature = feature("ssh", "1.0.0");
- Feature transactionFeature = feature("transaction", "2.0.0");
- expect(impl.listInstalledFeatures()).andStubReturn(new Feature[]{});
- expect(impl.getFeature("transaction", "0.0.0")).andStubReturn(transactionFeature);
- expect(impl.getFeature("ssh", "0.0.0")).andStubReturn(sshFeature);
-
- impl.installFeatures(setOf(transactionFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
- EasyMock.expectLastCall();
- impl.installFeatures(setOf(sshFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
- EasyMock.expectLastCall();
-
- replay(impl);
- BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl , "(transaction), ssh", false);
- bootFeatures.installBootFeatures();
- EasyMock.verify(impl);
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/BundleManagerTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/BundleManagerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/BundleManagerTest.java
deleted file mode 100644
index 0a7c6fe..0000000
--- a/features/core/src/test/java/org/apache/karaf/features/internal/BundleManagerTest.java
+++ /dev/null
@@ -1,74 +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 static org.easymock.EasyMock.replay;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import junit.framework.Assert;
-
-import org.easymock.EasyMock;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-
-public class BundleManagerTest extends TestBase {
-
- @Test
- public void testfindBundlestoRefreshWithHostToRefresh() throws Exception {
- Bundle hostBundle = createDummyBundle(12345l, "Host", headers());
- Bundle fragmentBundle = createDummyBundle(54321l, "fragment", headers(Constants.FRAGMENT_HOST, "Host"));
-
- BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
- BundleManager bundleManager = new BundleManager(bundleContext);
-
- // Host was already installed, fragment is new
- Set<Bundle> existing = new HashSet<Bundle>(Arrays.asList(hostBundle, fragmentBundle));
- Set<Bundle> installed = new HashSet<Bundle>(Arrays.asList(fragmentBundle));
-
- replay(bundleContext);
- Set<Bundle> bundles = bundleManager.findBundlesWithFragmentsToRefresh(existing, installed);
- EasyMock.verify(bundleContext);
-
- Assert.assertEquals(1, bundles.size());
- Assert.assertEquals(hostBundle, bundles.iterator().next());
- }
-
- @Test
- public void testfindBundlestoRefreshWithOptionalPackages() throws Exception {
- Bundle exporterBundle = createDummyBundle(12345l, "exporter", headers(Constants.EXPORT_PACKAGE, "org.my.package"));
- Bundle importerBundle = createDummyBundle(54321l, "importer", headers(Constants.IMPORT_PACKAGE, "org.my.package;resolution:=optional"));
-
- BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
- BundleManager bundleManager = new BundleManager(bundleContext);
-
- // Importer was already installed, exporter is new
- Set<Bundle> existing = new HashSet<Bundle>(Arrays.asList(importerBundle, exporterBundle));
- Set<Bundle> installed = new HashSet<Bundle>(Arrays.asList(exporterBundle));
-
- replay(bundleContext);
- Set<Bundle> bundles = bundleManager.findBundlesWithOptionalPackagesToRefresh(existing, installed);
- EasyMock.verify(bundleContext);
-
- Assert.assertEquals(1, bundles.size());
- Assert.assertEquals(importerBundle, bundles.iterator().next());
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java
deleted file mode 100644
index 4776bf4..0000000
--- a/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java
+++ /dev/null
@@ -1,178 +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 static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.felix.utils.manifest.Clause;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.internal.BundleManager.BundleInstallerResult;
-import org.easymock.EasyMock;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Test cases for {@link FeaturesServiceImpl}
- */
-public class FeaturesServiceImplTest extends TestBase {
-
- File dataFile;
-
- @Before
- public void setUp() throws IOException {
- dataFile = File.createTempFile("features", null, null);
- }
-
- @Test
- public void testGetFeature() throws Exception {
- Feature transactionFeature = feature("transaction", "1.0.0");
- final Map<String, Map<String, Feature>> features = features(transactionFeature);
- final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null) {
- protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
- return features;
- };
- };
- assertNotNull(impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
- assertSame(transactionFeature, impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
- }
-
- @Test
- public void testGetFeatureStripVersion() throws Exception {
- final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null) {
- protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
- return features(feature("transaction", "1.0.0"));
- };
- };
- Feature feature = impl.getFeature("transaction", " 1.0.0 ");
- assertNotNull(feature);
- assertSame("transaction", feature.getName());
- }
-
- @Test
- public void testGetFeatureNotAvailable() throws Exception {
- final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null) {
- protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
- return features(feature("transaction", "1.0.0"));
- };
- };
- assertNull(impl.getFeature("activemq", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
- }
-
- @Test
- public void testGetFeatureHighestAvailable() throws Exception {
- final Map<String, Map<String, Feature>> features = features(
- feature("transaction", "1.0.0"),
- feature("transaction", "2.0.0")
- );
- final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null) {
- protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
- return features;
- };
- };
- assertNotNull(impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
- assertSame("2.0.0", impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION).getVersion());
- }
-
- @Test
- public void testStartDoesNotFailWithOneInvalidUri() {
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
- expect(bundleManager.createAndRegisterEventAdminListener()).andReturn(null);
- replay(bundleManager);
- FeaturesServiceImpl service = new FeaturesServiceImpl(bundleManager, null);
- try {
- service.setUrls("mvn:inexistent/features/1.0/xml/features");
- service.start();
- } catch (Exception e) {
- fail(String.format("Service should not throw start-up exception but log the error instead: %s", e));
- }
- }
-
-
-
- /**
- * This test ensures that every feature get installed only once, even if it appears multiple times in the list
- * of transitive feature dependencies (KARAF-1600)
- */
- @Test
- @SuppressWarnings("unchecked")
- public void testNoDuplicateFeaturesInstallation() throws Exception {
- final List<Feature> installed = new LinkedList<Feature>();
- BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
- expect(bundleManager.installBundleIfNeeded(EasyMock.anyObject(String.class), EasyMock.anyInt(), EasyMock.anyObject(String.class)))
- .andReturn(new BundleInstallerResult(createDummyBundle(1l, "", headers()), true)).anyTimes();
- bundleManager.refreshBundles(EasyMock.anyObject(Set.class), EasyMock.anyObject(Set.class), EasyMock.anyObject(EnumSet.class));
- EasyMock.expectLastCall();
- final FeaturesServiceImpl impl = new FeaturesServiceImpl(bundleManager, null) {
- // override methods which refers to bundle context to avoid mocking everything
- @Override
- protected boolean loadState() {
- return true;
- }
-
- @Override
- protected void saveState() {
-
- }
-
- @Override
- protected void doInstallFeature(InstallationState state, Feature feature, boolean verbose) throws Exception {
- installed.add(feature);
-
- super.doInstallFeature(state, feature, verbose);
- }
-
- };
- replay(bundleManager);
- impl.addRepository(getClass().getResource("repo2.xml").toURI());
- impl.installFeature("all");
-
- // copying the features to a set to filter out the duplicates
- Set<Feature> noduplicates = new HashSet<Feature>();
- noduplicates.addAll(installed);
-
- assertEquals("Every feature should only have been installed once", installed.size(), noduplicates.size());
- }
-
- @Test
- public void testGetOptionalImportsOnly() {
- BundleManager bundleManager = new BundleManager(null, 0l);
-
- List<Clause> result = bundleManager.getOptionalImports("org.apache.karaf,org.apache.karaf.optional;resolution:=optional");
- assertEquals("One optional import expected", 1, result.size());
- assertEquals("org.apache.karaf.optional", result.get(0).getName());
-
- result = bundleManager.getOptionalImports(null);
- assertNotNull(result);
- assertEquals("No optional imports expected", 0, result.size());
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java
deleted file mode 100644
index 09f9c62..0000000
--- a/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesValidationTest.java
+++ /dev/null
@@ -1,60 +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.junit.Test;
-
-import static org.junit.Assert.fail;
-
-public class FeaturesValidationTest {
-
- @Test
- public void testNoNs() throws Exception {
- FeatureValidationUtil.validate(getClass().getResource("f01.xml").toURI());
- }
-
- @Test
- public void testNs10() throws Exception {
- FeatureValidationUtil.validate(getClass().getResource("f02.xml").toURI());
- }
-
- @Test
- public void testNs10NoName() throws Exception {
- FeatureValidationUtil.validate(getClass().getResource("f03.xml").toURI());
- }
-
- @Test
- public void testNs11() throws Exception {
- FeatureValidationUtil.validate(getClass().getResource("f04.xml").toURI());
- }
-
- @Test
- public void testNs12() throws Exception {
- FeatureValidationUtil.validate(getClass().getResource("f06.xml").toURI());
- }
-
- @Test
- public void testNs13() throws Exception {
- try {
- FeatureValidationUtil.validate(getClass().getResource("f05.xml").toURI());
- fail("Validation should have failed");
- } catch (Exception e) {
- // ok
- }
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/OverridesTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/OverridesTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/OverridesTest.java
deleted file mode 100644
index 99feb32..0000000
--- a/features/core/src/test/java/org/apache/karaf/features/internal/OverridesTest.java
+++ /dev/null
@@ -1,243 +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.File;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Writer;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.felix.utils.manifest.Clause;
-import org.apache.karaf.features.BundleInfo;
-import org.apache.karaf.features.internal.model.Bundle;
-import org.junit.Before;
-import org.junit.Test;
-import org.ops4j.pax.tinybundles.core.TinyBundles;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-public class OverridesTest {
-
- private String bsn = "bsn";
- private File b100;
- private File b101;
- private File b102;
- private File b110;
- private File c100;
- private File c101;
- private File c110;
-
- @Before
- public void setUp() throws IOException {
- b100 = File.createTempFile("karaf", "-100.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.0.0")
- .build(),
- new FileOutputStream(b100));
-
- b101 = File.createTempFile("karaf", "-101.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.0.1")
- .build(),
- new FileOutputStream(b101));
-
- b102 = File.createTempFile("karaf", "-102.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.0.2")
- .build(),
- new FileOutputStream(b102));
-
- b110 = File.createTempFile("karaf", "-110.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.1.0")
- .build(),
- new FileOutputStream(b110));
-
- c100 = File.createTempFile("karafc", "-100.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.0.0")
- .set("Bundle-Vendor", "Apache")
- .build(),
- new FileOutputStream(c100));
-
- c101 = File.createTempFile("karafc", "-101.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.0.1")
- .set("Bundle-Vendor", "NotApache")
- .build(),
- new FileOutputStream(c101));
-
- c110 = File.createTempFile("karafc", "-110.jar");
- copy(TinyBundles.bundle()
- .set("Bundle-SymbolicName", bsn)
- .set("Bundle-Version", "1.1.0")
- .set("Bundle-Vendor", "NotApache")
- .build(),
- new FileOutputStream(c110));
- }
-
- @Test
- public void testDifferentVendors() throws IOException {
- File props = File.createTempFile("karaf", "properties");
- Writer w = new FileWriter(props);
- w.write(c101.toURI().toString());
- w.write("\n");
- w.write(c110.toURI().toString());
- w.write("\n");
- w.close();
-
- List<BundleInfo> res = Overrides.override(
- Arrays.<BundleInfo>asList(new Bundle(c100.toURI().toString())),
- props.toURI().toString());
- assertNotNull(res);
- assertEquals(1, res.size());
- BundleInfo out = res.get(0);
- assertNotNull(out);
- assertEquals(c101.toURI().toString(), out.getLocation());
- }
-
- @Test
- public void testMatching101() throws IOException {
- File props = File.createTempFile("karaf", "properties");
- Writer w = new FileWriter(props);
- w.write(b101.toURI().toString());
- w.write("\n");
- w.write(b110.toURI().toString());
- w.write("\n");
- w.close();
-
- List<BundleInfo> res = Overrides.override(
- Arrays.<BundleInfo>asList(new Bundle(b100.toURI().toString())),
- props.toURI().toString());
- assertNotNull(res);
- assertEquals(1, res.size());
- BundleInfo out = res.get(0);
- assertNotNull(out);
- assertEquals(b101.toURI().toString(), out.getLocation());
- }
-
- @Test
- public void testMatching102() throws IOException {
- File props = File.createTempFile("karaf", "properties");
- Writer w = new FileWriter(props);
- w.write(b101.toURI().toString());
- w.write("\n");
- w.write(b102.toURI().toString());
- w.write("\n");
- w.write(b110.toURI().toString());
- w.write("\n");
- w.close();
-
- List<BundleInfo> res = Overrides.override(
- Arrays.<BundleInfo>asList(new Bundle(b100.toURI().toString())),
- props.toURI().toString());
- assertNotNull(res);
- assertEquals(1, res.size());
- BundleInfo out = res.get(0);
- assertNotNull(out);
- assertEquals(b102.toURI().toString(), out.getLocation());
- }
-
- @Test
- public void testMatchingRange() throws IOException {
- File props = File.createTempFile("karaf", "properties");
- Writer w = new FileWriter(props);
- w.write(b101.toURI().toString());
- w.write("\n");
- w.write(b110.toURI().toString());
- w.write(";range=\"[1.0, 2.0)\"\n");
- w.close();
-
- List<BundleInfo> res = Overrides.override(
- Arrays.<BundleInfo>asList(new Bundle(b100.toURI().toString())),
- props.toURI().toString());
- assertNotNull(res);
- assertEquals(1, res.size());
- BundleInfo out = res.get(0);
- assertNotNull(out);
- assertEquals(b110.toURI().toString(), out.getLocation());
- }
-
- @Test
- public void testNotMatching() throws IOException {
- File props = File.createTempFile("karaf", "properties");
- Writer w = new FileWriter(props);
- w.write(b110.toURI().toString());
- w.write("\n");
- w.close();
-
- List<BundleInfo> res = Overrides.override(
- Arrays.<BundleInfo>asList(new Bundle(b100.toURI().toString())),
- props.toURI().toString());
- assertNotNull(res);
- assertEquals(1, res.size());
- BundleInfo out = res.get(0);
- assertNotNull(out);
- assertEquals(b100.toURI().toString(), out.getLocation());
- }
-
- @Test
- public void testLoadOverrides() {
- List<Clause> overrides = Overrides.loadOverrides(getClass().getResource("overrides.properties").toExternalForm());
- assertEquals(2, overrides.size());
-
- Clause karafAdminCommand = null;
- Clause karafAdminCore = null;
- for (Clause clause : overrides) {
- if (clause.getName().equals("mvn:org.apache.karaf.admin/org.apache.karaf.admin.command/2.3.0.redhat-61033X")) {
- karafAdminCommand = clause;
- }
- if (clause.getName().equals("mvn:org.apache.karaf.admin/org.apache.karaf.admin.core/2.3.0.redhat-61033X")) {
- karafAdminCore = clause;
- }
- }
- assertNotNull("Missing admin.command bundle override", karafAdminCommand);
- assertNotNull("Missing admin.core bundle override", karafAdminCore);
- assertNotNull("Missing range on admin.core override", karafAdminCore.getAttribute(Overrides.OVERRIDE_RANGE));
- }
-
- /**
- * Copies the content of {@link java.io.InputStream} to {@link java.io.OutputStream}.
- *
- * @param input
- * @param output
- * @throws java.io.IOException
- */
- private void copy(final InputStream input, final OutputStream output) throws IOException {
- byte[] buffer = new byte[1024 * 16];
- int n;
- while (-1 != (n = input.read(buffer))) {
- output.write(buffer, 0, n);
- output.flush();
- }
- input.close();
- output.close();
- }
-
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/TestBase.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/TestBase.java b/features/core/src/test/java/org/apache/karaf/features/internal/TestBase.java
deleted file mode 100644
index e651a6c..0000000
--- a/features/core/src/test/java/org/apache/karaf/features/internal/TestBase.java
+++ /dev/null
@@ -1,108 +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 static java.util.Arrays.asList;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.karaf.features.Feature;
-import org.easymock.EasyMock;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.startlevel.BundleStartLevel;
-
-public class TestBase {
- public Bundle createDummyBundle(long id, final String symbolicName, Dictionary<String,String> headers) {
- Bundle bundle = EasyMock.createNiceMock(Bundle.class);
-
- // Be aware that this means all bundles are treated as different
- expect(bundle.compareTo(EasyMock.<Bundle>anyObject())).andReturn(1).anyTimes();
-
- expect(bundle.getBundleId()).andReturn(id).anyTimes();
- expect(bundle.getSymbolicName()).andReturn(symbolicName).anyTimes();
- expect(bundle.getHeaders()).andReturn(headers).anyTimes();
- BundleStartLevel sl = EasyMock.createMock(BundleStartLevel.class);
- expect(sl.isPersistentlyStarted()).andReturn(true).anyTimes();
- expect(bundle.adapt(BundleStartLevel.class)).andReturn(sl).anyTimes();
- replay(bundle, sl);
- return bundle;
- }
-
- public Dictionary<String, String> headers(String ... keyAndHeader) {
- Hashtable<String, String> headersTable = new Hashtable<String, String>();
- int c=0;
- while (c < keyAndHeader.length) {
- String key = keyAndHeader[c++];
- String value = keyAndHeader[c++];
- headersTable.put(key, value);
- }
- return headersTable;
- }
-
- public Map<String, Map<String, Feature>> features(Feature ... features) {
- final Map<String, Map<String, Feature>> featuresMap = new HashMap<String, Map<String,Feature>>();
- for (Feature feature : features) {
- Map<String, Feature> featureVersion = getOrCreate(featuresMap, feature);
- featureVersion.put(feature.getVersion(), feature);
- }
- return featuresMap;
- }
-
- private Map<String, Feature> getOrCreate(final Map<String, Map<String, Feature>> featuresMap, Feature feature) {
- Map<String, Feature> featureVersion = featuresMap.get(feature.getName());
- if (featureVersion == null) {
- featureVersion = new HashMap<String, Feature>();
- featuresMap.put(feature.getName(), featureVersion);
- }
- return featureVersion;
- }
-
- public Feature feature(String name, String version) {
- return new org.apache.karaf.features.internal.model.Feature(name, version);
- }
-
- public Set<Bundle> setOf(Bundle ... elements) {
- return new HashSet<Bundle>(Arrays.asList(elements));
- }
-
- public Set<Long> setOf(Long ... elements) {
- return new HashSet<Long>(Arrays.asList(elements));
- }
-
- public Set<String> setOf(String ... elements) {
- return new HashSet<String>(asList(elements));
- }
-
- public Set<Feature> setOf(Feature ... elements) {
- return new HashSet<Feature>(Arrays.asList(elements));
- }
-
- @SuppressWarnings("unchecked")
- public void ignoreRefreshes(BundleManager bundleManager) {
- bundleManager.refreshBundles(EasyMock.anyObject(Set.class), EasyMock.anyObject(Set.class), EasyMock.anyObject(EnumSet.class));
- EasyMock.expectLastCall().anyTimes();
- }
-}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/service/BootFeaturesInstallerTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/BootFeaturesInstallerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/BootFeaturesInstallerTest.java
new file mode 100644
index 0000000..ab11b6b
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/BootFeaturesInstallerTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.service;
+
+import static java.util.Arrays.asList;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.util.EnumSet;
+
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.FeaturesService.Option;
+import org.apache.karaf.features.TestBase;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BootFeaturesInstallerTest extends TestBase {
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testParser() {
+ BootFeaturesInstaller installer = new BootFeaturesInstaller(null, null, "", "", false);
+ Assert.assertEquals(asList(setOf("test1", "test2"),setOf("test3")), installer.parseBootFeatures("(test1, test2), test3"));
+ Assert.assertEquals(asList(setOf("test1", "test2", "test3")), installer.parseBootFeatures("test1, test2, test3"));
+ }
+
+ @Test
+ public void testDefaultBootFeatures() throws Exception {
+ FeaturesServiceImpl impl = EasyMock.createMock(FeaturesServiceImpl.class);
+ Feature configFeature = feature("config", "1.0.0");
+ Feature standardFeature = feature("standard", "1.0.0");
+ Feature regionFeature = feature("region", "1.0.0");
+ expect(impl.listInstalledFeatures()).andStubReturn(new Feature[]{});
+ expect(impl.getFeature("config", "0.0.0")).andReturn(configFeature);
+ expect(impl.getFeature("standard", "0.0.0")).andReturn(standardFeature);
+ expect(impl.getFeature("region", "0.0.0")).andReturn(regionFeature);
+
+ impl.installFeatures(setOf(configFeature, standardFeature, regionFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
+ EasyMock.expectLastCall();
+
+ impl.bootDone();
+ EasyMock.expectLastCall();
+
+ replay(impl);
+ BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl, "", "config,standard,region", false);
+ bootFeatures.installBootFeatures();
+ EasyMock.verify(impl);
+ }
+
+ /**
+ * This test checks KARAF-388 which allows you to specify version of boot feature.
+ * @throws Exception
+ */
+ @Test
+ public void testStartDoesNotFailWithNonExistentVersion() throws Exception {
+ FeaturesServiceImpl impl = EasyMock.createMock(FeaturesServiceImpl.class);
+ Feature sshFeature = feature("ssh", "1.0.0");
+ expect(impl.getFeature("ssh", "1.0.0")).andReturn(sshFeature);
+ expect(impl.getFeature("transaction", "1.2")).andReturn(null);
+
+ // Only the ssh feature should get installed
+ impl.installFeatures(setOf(sshFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
+ EasyMock.expectLastCall();
+
+ impl.bootDone();
+ EasyMock.expectLastCall();
+
+ replay(impl);
+ BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl , "", "transaction;version=1.2,ssh;version=1.0.0", false);
+ bootFeatures.installBootFeatures();
+ EasyMock.verify(impl);
+ }
+
+ @Test
+ public void testStagedBoot() throws Exception {
+ FeaturesServiceImpl impl = EasyMock.createStrictMock(FeaturesServiceImpl.class);
+ Feature sshFeature = feature("ssh", "1.0.0");
+ Feature transactionFeature = feature("transaction", "2.0.0");
+ expect(impl.getFeature("transaction", "0.0.0")).andStubReturn(transactionFeature);
+ expect(impl.getFeature("ssh", "0.0.0")).andStubReturn(sshFeature);
+
+ impl.installFeatures(setOf(transactionFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
+ EasyMock.expectLastCall();
+ impl.installFeatures(setOf(sshFeature), EnumSet.of(Option.NoCleanIfFailure, Option.ContinueBatchOnFailure));
+ EasyMock.expectLastCall();
+
+ impl.bootDone();
+ EasyMock.expectLastCall();
+
+ replay(impl);
+ BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl , "", "(transaction), ssh", false);
+ bootFeatures.installBootFeatures();
+ EasyMock.verify(impl);
+ }
+
+ @Test
+ public void testStartDoesNotFailWithOneInvalidUri() throws Exception {
+ FeaturesServiceImpl impl = EasyMock.createStrictMock(FeaturesServiceImpl.class);
+ impl.addRepository(URI.create("mvn:inexistent/features/1.0/xml/features"));
+ EasyMock.expectLastCall().andThrow(new IllegalArgumentException());
+
+ impl.bootDone();
+ EasyMock.expectLastCall();
+
+ replay(impl);
+ BootFeaturesInstaller bootFeatures = new BootFeaturesInstaller(null, impl, "mvn:inexistent/features/1.0/xml/features", "", false);
+ bootFeatures.installBootFeatures();
+ EasyMock.verify(impl);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/service/BundleManagerTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/BundleManagerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/BundleManagerTest.java
new file mode 100644
index 0000000..bed8104
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/BundleManagerTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.service;
+
+import org.apache.karaf.features.TestBase;
+
+public class BundleManagerTest extends TestBase {
+
+ /*
+ @Test
+ public void testfindBundlestoRefreshWithHostToRefresh() throws Exception {
+ Bundle hostBundle = createDummyBundle(12345l, "Host", headers());
+ Bundle fragmentBundle = createDummyBundle(54321l, "fragment", headers(Constants.FRAGMENT_HOST, "Host"));
+
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ BundleManager bundleManager = new BundleManager(bundleContext);
+
+ // Host was already installed, fragment is new
+ Set<Bundle> existing = new HashSet<Bundle>(Arrays.asList(hostBundle, fragmentBundle));
+ Set<Bundle> installed = new HashSet<Bundle>(Arrays.asList(fragmentBundle));
+
+ replay(bundleContext);
+ Set<Bundle> bundles = bundleManager.findBundlesWithFragmentsToRefresh(existing, installed);
+ EasyMock.verify(bundleContext);
+
+ Assert.assertEquals(1, bundles.size());
+ Assert.assertEquals(hostBundle, bundles.iterator().next());
+ }
+
+ @Test
+ public void testfindBundlestoRefreshWithOptionalPackages() throws Exception {
+ Bundle exporterBundle = createDummyBundle(12345l, "exporter", headers(Constants.EXPORT_PACKAGE, "org.my.package"));
+ Bundle importerBundle = createDummyBundle(54321l, "importer", headers(Constants.IMPORT_PACKAGE, "org.my.package;resolution:=optional"));
+
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ BundleManager bundleManager = new BundleManager(bundleContext);
+
+ // Importer was already installed, exporter is new
+ Set<Bundle> existing = new HashSet<Bundle>(Arrays.asList(importerBundle, exporterBundle));
+ Set<Bundle> installed = new HashSet<Bundle>(Arrays.asList(exporterBundle));
+
+ replay(bundleContext);
+ Set<Bundle> bundles = bundleManager.findBundlesWithOptionalPackagesToRefresh(existing, installed);
+ EasyMock.verify(bundleContext);
+
+ Assert.assertEquals(1, bundles.size());
+ Assert.assertEquals(importerBundle, bundles.iterator().next());
+ }
+ */
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
new file mode 100644
index 0000000..8339151
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesServiceImplTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.TestBase;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for {@link org.apache.karaf.features.internal.service.FeaturesServiceImpl}
+ */
+public class FeaturesServiceImplTest extends TestBase {
+
+ File dataFile;
+
+ @Before
+ public void setUp() throws IOException {
+ dataFile = File.createTempFile("features", null, null);
+ }
+
+ @Test
+ public void testGetFeature() throws Exception {
+ Feature transactionFeature = feature("transaction", "1.0.0");
+ final Map<String, Map<String, Feature>> features = features(transactionFeature);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, "") {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ }
+ };
+ assertNotNull(impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
+ assertSame(transactionFeature, impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
+ }
+
+ @Test
+ public void testGetFeatureStripVersion() throws Exception {
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, "") {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features(feature("transaction", "1.0.0"));
+ }
+ };
+ Feature feature = impl.getFeature("transaction", " 1.0.0 ");
+ assertNotNull(feature);
+ assertSame("transaction", feature.getName());
+ }
+
+ @Test
+ public void testGetFeatureNotAvailable() throws Exception {
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, "") {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features(feature("transaction", "1.0.0"));
+ }
+ };
+ assertNull(impl.getFeature("activemq", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
+ }
+
+ @Test
+ public void testGetFeatureHighestAvailable() throws Exception {
+ final Map<String, Map<String, Feature>> features = features(
+ feature("transaction", "1.0.0"),
+ feature("transaction", "2.0.0")
+ );
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, "") {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ }
+ };
+ assertNotNull(impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION));
+ assertSame("2.0.0", impl.getFeature("transaction", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION).getVersion());
+ }
+
+ /**
+ * This test ensures that every feature get installed only once, even if it appears multiple times in the list
+ * of transitive feature dependencies (KARAF-1600)
+ */
+ /*
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testNoDuplicateFeaturesInstallation() throws Exception {
+ final List<Feature> installed = new LinkedList<Feature>();
+ BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
+ expect(bundleManager.installBundleIfNeeded(EasyMock.anyObject(String.class), EasyMock.anyInt(), EasyMock.anyObject(String.class)))
+ .andReturn(new BundleInstallerResult(createDummyBundle(1l, "", headers()), true)).anyTimes();
+ bundleManager.refreshBundles(EasyMock.anyObject(Set.class), EasyMock.anyObject(Set.class), EasyMock.anyObject(EnumSet.class));
+ EasyMock.expectLastCall();
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl(bundleManager, null) {
+ // override methods which refers to bundle context to avoid mocking everything
+ @Override
+ protected boolean loadState() {
+ return true;
+ }
+
+ @Override
+ protected void saveState() {
+
+ }
+
+ @Override
+ protected void doInstallFeature(InstallationState state, Feature feature, boolean verbose) throws Exception {
+ installed.add(feature);
+
+ super.doInstallFeature(state, feature, verbose);
+ }
+
+ };
+ replay(bundleManager);
+ impl.addRepository(getClass().getResource("repo2.xml").toURI());
+ impl.installFeature("all");
+
+ // copying the features to a set to filter out the duplicates
+ Set<Feature> noduplicates = new HashSet<Feature>();
+ noduplicates.addAll(installed);
+
+ assertEquals("Every feature should only have been installed once", installed.size(), noduplicates.size());
+ }
+
+ @Test
+ public void testGetOptionalImportsOnly() {
+ BundleManager bundleManager = new BundleManager(null, 0l);
+
+ List<Clause> result = bundleManager.getOptionalImports("org.apache.karaf,org.apache.karaf.optional;resolution:=optional");
+ assertEquals("One optional import expected", 1, result.size());
+ assertEquals("org.apache.karaf.optional", result.get(0).getName());
+
+ result = bundleManager.getOptionalImports(null);
+ assertNotNull(result);
+ assertEquals("No optional imports expected", 0, result.size());
+ }
+ */
+
+ static class Storage extends StateStorage {
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ return null;
+ }
+ @Override
+ protected OutputStream getOutputStream() throws IOException {
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesValidationTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesValidationTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesValidationTest.java
new file mode 100644
index 0000000..f3ca2e6
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/FeaturesValidationTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.service;
+
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+
+public class FeaturesValidationTest {
+
+ @Test
+ public void testNoNs() throws Exception {
+ FeatureValidationUtil.validate(getClass().getResource("f01.xml").toURI());
+ }
+
+ @Test
+ public void testNs10() throws Exception {
+ FeatureValidationUtil.validate(getClass().getResource("f02.xml").toURI());
+ }
+
+ @Test
+ public void testNs10NoName() throws Exception {
+ FeatureValidationUtil.validate(getClass().getResource("f03.xml").toURI());
+ }
+
+ @Test
+ public void testNs11() throws Exception {
+ FeatureValidationUtil.validate(getClass().getResource("f04.xml").toURI());
+ }
+
+ @Test
+ public void testNs12() throws Exception {
+ FeatureValidationUtil.validate(getClass().getResource("f06.xml").toURI());
+ }
+
+ @Test
+ public void testNs11NoName() throws Exception {
+ try {
+ FeatureValidationUtil.validate(getClass().getResource("f05.xml").toURI());
+ fail("Validation should have failed");
+ } catch (Exception e) {
+ // ok
+ }
+ }
+
+ @Test
+ public void testNs13() throws Exception {
+ FeatureValidationUtil.validate(getClass().getResource("f07.xml").toURI());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/test/java/org/apache/karaf/features/internal/service/OverridesTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/OverridesTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/OverridesTest.java
new file mode 100644
index 0000000..c4976cf
--- /dev/null
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/OverridesTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Parser;
+import org.apache.karaf.features.BundleInfo;
+import org.apache.karaf.features.internal.model.Bundle;
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.resolver.UriNamespace;
+import org.junit.Before;
+import org.junit.Test;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleException;
+import org.osgi.resource.Resource;
+
+import static org.apache.karaf.features.internal.resolver.UriNamespace.getUri;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class OverridesTest {
+
+ private String bsn = "bsn";
+ private Resource b100;
+ private Resource b101;
+ private Resource b102;
+ private Resource b110;
+ private Resource c100;
+ private Resource c101;
+ private Resource c110;
+
+ @Before
+ public void setUp() throws BundleException {
+ b100 = resource("karaf-100.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.0.0")
+ .build();
+
+ b101 = resource("karaf-101.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.0.1")
+ .build();
+
+ b102 = resource("karaf-102.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.0.2")
+ .build();
+
+ b110 = resource("karaf-110.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.1.0")
+ .build();
+
+ c100 = resource("karafc-100.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.0.0")
+ .set("Bundle-Vendor", "Apache")
+ .build();
+
+ c101 = resource("karafc-101.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.0.1")
+ .set("Bundle-Vendor", "NotApache")
+ .build();
+
+ c110 = resource("karafc-110.jar")
+ .set("Bundle-SymbolicName", bsn)
+ .set("Bundle-Version", "1.1.0")
+ .set("Bundle-Vendor", "NotApache")
+ .build();
+ }
+
+ @Test
+ public void testDifferentVendors() throws IOException {
+ Map<String, Resource> map = asResourceMap(c100, c101, c110);
+ assertEquals(c100, map.get(getUri(c100)));
+ Overrides.override(map, Arrays.asList(getUri(c101), getUri(c110)));
+ assertEquals(c101, map.get(getUri(c100)));
+ }
+
+ @Test
+ public void testMatching101() throws IOException {
+ Map<String, Resource> map = asResourceMap(b100, b101, b110);
+ assertEquals(b100, map.get(getUri(b100)));
+ Overrides.override(map, Arrays.asList(getUri(b101), getUri(b110)));
+ assertEquals(b101, map.get(getUri(b100)));
+ }
+
+ @Test
+ public void testMatching102() throws IOException {
+ Map<String, Resource> map = asResourceMap(b100, b101, b102, b110);
+ assertEquals(b100, map.get(getUri(b100)));
+ Overrides.override(map, Arrays.asList(getUri(b101), getUri(b102), getUri(b110)));
+ assertEquals(b102, map.get(getUri(b100)));
+ }
+
+ @Test
+ public void testMatchingRange() throws IOException {
+ Map<String, Resource> map = asResourceMap(b100, b101, b110);
+ assertEquals(b100, map.get(getUri(b100)));
+ Overrides.override(map, Arrays.asList(getUri(b101), getUri(b110) + ";range=\"[1.0, 2.0)\""));
+ assertEquals(b110, map.get(getUri(b100)));
+ }
+
+ @Test
+ public void testNotMatching() throws IOException {
+ Map<String, Resource> map = asResourceMap(b100, b110);
+ assertEquals(b100, map.get(getUri(b100)));
+ Overrides.override(map, Arrays.asList(getUri(b110)));
+ assertEquals(b100, map.get(getUri(b100)));
+ }
+
+ @Test
+ public void testLoadOverrides() {
+ Set<String> overrides = Overrides.loadOverrides(getClass().getResource("overrides.properties").toExternalForm());
+ assertEquals(2, overrides.size());
+
+ Clause karafAdminCommand = null;
+ Clause karafAdminCore = null;
+ for (Clause clause : Parser.parseClauses(overrides.toArray(new String[overrides.size()]))) {
+ if (clause.getName().equals("mvn:org.apache.karaf.admin/org.apache.karaf.admin.command/2.3.0.redhat-61033X")) {
+ karafAdminCommand = clause;
+ }
+ if (clause.getName().equals("mvn:org.apache.karaf.admin/org.apache.karaf.admin.core/2.3.0.redhat-61033X")) {
+ karafAdminCore = clause;
+ }
+ }
+ assertNotNull("Missing admin.command bundle override", karafAdminCommand);
+ assertNotNull("Missing admin.core bundle override", karafAdminCore);
+ assertNotNull("Missing range on admin.core override", karafAdminCore.getAttribute(Overrides.OVERRIDE_RANGE));
+ }
+
+ /**
+ * Copies the content of {@link java.io.InputStream} to {@link java.io.OutputStream}.
+ *
+ * @param input
+ * @param output
+ * @throws java.io.IOException
+ */
+ private void copy(final InputStream input, final OutputStream output) throws IOException {
+ byte[] buffer = new byte[1024 * 16];
+ int n;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ output.flush();
+ }
+ input.close();
+ output.close();
+ }
+
+ static Builder resource(String uri) {
+ return new Builder(uri);
+ }
+
+ static Map<String, Resource> asResourceMap(Resource... resources) {
+ Map<String, Resource> map = new HashMap<String, Resource>();
+ for (Resource resource : resources) {
+ map.put(getUri(resource), resource);
+ }
+ return map;
+ }
+
+ static class Builder {
+ String uri;
+ Map<String,String> headers = new HashMap<String,String>();
+ Builder(String uri) {
+ this.uri = uri;
+ this.headers.put("Bundle-ManifestVersion", "2");
+ }
+ Builder set(String key, String value) {
+ this.headers.put(key, value);
+ return this;
+ }
+ Resource build() throws BundleException {
+ return ResourceBuilder.build(uri, headers);
+ }
+ }
+
+}
[11/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java
new file mode 100644
index 0000000..350679f
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed 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.management;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.openmbean.TabularData;
+
+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;
+import org.apache.karaf.features.management.codec.JmxRepository;
+import org.apache.karaf.features.management.codec.JmxRepositoryEvent;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Implementation of {@link FeaturesServiceMBean}.
+ */
+public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
+ MBeanRegistration, FeaturesServiceMBean {
+
+ private ServiceRegistration<FeaturesListener> registration;
+
+ private BundleContext bundleContext;
+
+ private ObjectName objectName;
+
+ private volatile long sequenceNumber = 0;
+
+ private org.apache.karaf.features.FeaturesService featuresService;
+
+ public FeaturesServiceMBeanImpl() throws NotCompliantMBeanException {
+ super(FeaturesServiceMBean.class);
+ }
+
+ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
+ objectName = name;
+ return name;
+ }
+
+ public void postRegister(Boolean registrationDone) {
+ registration = bundleContext.registerService(FeaturesListener.class,
+ getFeaturesListener(), new Hashtable<String, String>());
+ }
+
+ public void preDeregister() throws Exception {
+ registration.unregister();
+ }
+
+ public void postDeregister() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TabularData getFeatures() throws Exception {
+ try {
+ List<Feature> allFeatures = Arrays.asList(featuresService.listFeatures());
+ List<Feature> insFeatures = Arrays.asList(featuresService.listInstalledFeatures());
+ ArrayList<JmxFeature> features = new ArrayList<JmxFeature>();
+ for (Feature feature : allFeatures) {
+ try {
+ features.add(new JmxFeature(feature, insFeatures.contains(feature)));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ TabularData table = JmxFeature.tableFrom(features);
+ return table;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TabularData getRepositories() throws Exception {
+ try {
+ List<Repository> allRepositories = Arrays.asList(featuresService.listRepositories());
+ ArrayList<JmxRepository> repositories = new ArrayList<JmxRepository>();
+ for (Repository repository : allRepositories) {
+ try {
+ repositories.add(new JmxRepository(repository));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ TabularData table = JmxRepository.tableFrom(repositories);
+ return table;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ public void addRepository(String uri) throws Exception {
+ featuresService.addRepository(new URI(uri));
+ }
+
+ public void addRepository(String uri, boolean install) throws Exception {
+ featuresService.addRepository(new URI(uri), install);
+ }
+
+ public void removeRepository(String uri) throws Exception {
+ featuresService.removeRepository(new URI(uri));
+ }
+
+ public void removeRepository(String uri, boolean uninstall) throws Exception {
+ featuresService.removeRepository(new URI(uri), uninstall);
+ }
+
+ public void installFeature(String name) throws Exception {
+ featuresService.installFeature(name);
+ }
+
+ public void installFeature(String name, boolean noClean, boolean noRefresh) throws Exception {
+ EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
+ if (noClean) {
+ options.add(org.apache.karaf.features.FeaturesService.Option.NoCleanIfFailure);
+ }
+ if (noRefresh) {
+ options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
+ }
+ featuresService.installFeature(name, options);
+ }
+
+ public void installFeature(String name, String version) throws Exception {
+ featuresService.installFeature(name, version);
+ }
+
+ public void installFeature(String name, String version, boolean noClean, boolean noRefresh) throws Exception {
+ EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
+ if (noClean) {
+ options.add(org.apache.karaf.features.FeaturesService.Option.NoCleanIfFailure);
+ }
+ if (noRefresh) {
+ options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
+ }
+ featuresService.installFeature(name, version, options);
+ }
+
+ public TabularData infoFeature(String name) throws Exception {
+ try {
+ Feature feature = featuresService.getFeature(name);
+ return infoFeature(feature);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ public TabularData infoFeature(String name, String version) throws Exception {
+ try {
+ Feature feature = featuresService.getFeature(name, version);
+ return infoFeature(feature);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+ private TabularData infoFeature(Feature feature) throws Exception {
+ JmxFeature jmxFeature = null;
+ if (featuresService.isInstalled(feature)) {
+ jmxFeature = new JmxFeature(feature, true);
+ } else {
+ jmxFeature = new JmxFeature(feature, false);
+ }
+ ArrayList<JmxFeature> features = new ArrayList<JmxFeature>();
+ features.add(jmxFeature);
+ TabularData table = JmxFeature.tableFrom(features);
+ return table;
+ }
+
+ public void uninstallFeature(String name) throws Exception {
+ 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;
+ }
+
+ public void setFeaturesService(org.apache.karaf.features.FeaturesService featuresService) {
+ this.featuresService = featuresService;
+ }
+
+ public FeaturesListener getFeaturesListener() {
+ return new FeaturesListener() {
+ public void featureEvent(FeatureEvent event) {
+ if (!event.isReplay()) {
+ Notification notification = new Notification(FEATURE_EVENT_TYPE, objectName, sequenceNumber++);
+ notification.setUserData(new JmxFeatureEvent(event).asCompositeData());
+ sendNotification(notification);
+ }
+ }
+
+ public void repositoryEvent(RepositoryEvent event) {
+ if (!event.isReplay()) {
+ Notification notification = new Notification(REPOSITORY_EVENT_TYPE, objectName, sequenceNumber++);
+ notification.setUserData(new JmxRepositoryEvent(event).asCompositeData());
+ sendNotification(notification);
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ return o.equals(this);
+ }
+
+ };
+ }
+
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ return getBroadcastInfo();
+ }
+
+ private static MBeanNotificationInfo[] getBroadcastInfo() {
+ String type = Notification.class.getCanonicalName();
+ MBeanNotificationInfo info1 = new MBeanNotificationInfo(new String[]{FEATURE_EVENT_EVENT_TYPE},
+ type, "Some features notification");
+ MBeanNotificationInfo info2 = new MBeanNotificationInfo(new String[]{REPOSITORY_EVENT_EVENT_TYPE},
+ type, "Some repository notification");
+ return new MBeanNotificationInfo[]{info1, info2};
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/management/StandardEmitterMBean.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/management/StandardEmitterMBean.java b/features/core/src/main/java/org/apache/karaf/features/internal/management/StandardEmitterMBean.java
new file mode 100644
index 0000000..13a4b6c
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/management/StandardEmitterMBean.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed 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.management;
+
+import javax.management.*;
+
+public class StandardEmitterMBean extends StandardMBean implements NotificationEmitter {
+
+ private final NotificationBroadcasterSupport emitter;
+
+ @SuppressWarnings("rawtypes")
+ public StandardEmitterMBean(Class mbeanInterface) throws NotCompliantMBeanException {
+ super(mbeanInterface);
+ this.emitter = new NotificationBroadcasterSupport() {
+ @Override
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ return StandardEmitterMBean.this.getNotificationInfo();
+ }
+ };
+ }
+
+ public void sendNotification(Notification notification) {
+ emitter.sendNotification(notification);
+ }
+
+
+ public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException {
+ emitter.removeNotificationListener(listener, filter, handback);
+ }
+
+ public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException {
+ emitter.addNotificationListener(listener, filter, handback);
+ }
+
+ public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
+ emitter.removeNotificationListener(listener);
+ }
+
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ return new MBeanNotificationInfo[0];
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ MBeanInfo mbeanInfo = super.getMBeanInfo();
+ if (mbeanInfo != null) {
+ MBeanNotificationInfo[] notificationInfo = getNotificationInfo();
+ mbeanInfo = new MBeanInfo(mbeanInfo.getClassName(), mbeanInfo.getDescription(), mbeanInfo.getAttributes(),
+ mbeanInfo.getConstructors(), mbeanInfo.getOperations(), notificationInfo);
+ }
+ return mbeanInfo;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/model/Capability.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Capability.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Capability.java
new file mode 100644
index 0000000..b866151
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Capability.java
@@ -0,0 +1,91 @@
+/*
+ * 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.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+import org.apache.karaf.features.BundleInfo;
+
+
+/**
+ *
+ * Additional capability for a feature.
+ *
+ *
+ * <p>Java class for bundle complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="capability">
+ * <simpleContent>
+ * <extension base="<http://www.w3.org/2001/XMLSchema>string">
+ * </extension>
+ * </simpleContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "capability", propOrder = {
+ "value"
+})
+public class Capability implements org.apache.karaf.features.Capability {
+
+ @XmlValue
+ protected String value;
+
+
+ public Capability() {
+ }
+
+ public Capability(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Capability bundle = (Capability) o;
+
+ if (value != null ? !value.equals(bundle.value) : bundle.value != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = value != null ? value.hashCode() : 0;
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
index 866c4f7..46580da 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Feature.java
@@ -51,6 +51,9 @@ import javax.xml.bind.annotation.XmlType;
* <element name="configfile" type="{http://karaf.apache.org/xmlns/features/v1.0.0}configFile" maxOccurs="unbounded" minOccurs="0"/>
* <element name="feature" type="{http://karaf.apache.org/xmlns/features/v1.0.0}dependency" maxOccurs="unbounded" minOccurs="0"/>
* <element name="bundle" type="{http://karaf.apache.org/xmlns/features/v1.0.0}bundle" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="conditional" type="{http://karaf.apache.org/xmlns/features/v1.0.0}conditional" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="capability" type="{http://karaf.apache.org/xmlns/features/v1.0.0}capability" maxOccurs="unbounded" minOccurs="0"/>
+ * <element name="requirement" type="{http://karaf.apache.org/xmlns/features/v1.0.0}requirement" maxOccurs="unbounded" minOccurs="0"/>
* </sequence>
* <attribute name="name" use="required" type="{http://karaf.apache.org/xmlns/features/v1.0.0}featureName" />
* <attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" default="0.0.0" />
@@ -70,10 +73,12 @@ import javax.xml.bind.annotation.XmlType;
"configfile",
"feature",
"bundle",
- "conditional"
+ "conditional",
+ "capability",
+ "requirement"
})
public class Feature extends Content implements org.apache.karaf.features.Feature {
- public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_";
+ public static String SPLIT_FOR_NAME_AND_VERSION = "/";
public static String DEFAULT_VERSION = "0.0.0";
@@ -93,6 +98,8 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
@XmlAttribute
protected String region;
protected List<Conditional> conditional;
+ protected List<Capability> capability;
+ protected List<Requirement> requirement;
public Feature() {
}
@@ -108,7 +115,7 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
public static org.apache.karaf.features.Feature valueOf(String str) {
- if (str.indexOf(SPLIT_FOR_NAME_AND_VERSION) >= 0) {
+ if (str.contains(SPLIT_FOR_NAME_AND_VERSION)) {
String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION)
+ SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
@@ -122,7 +129,7 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
public String getId() {
- return getName() + "-" + getVersion();
+ return getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
}
/**
@@ -159,7 +166,7 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
*/
public String getVersion() {
if (version == null) {
- return "0.0.0";
+ return DEFAULT_VERSION;
} else {
return version;
}
@@ -300,7 +307,7 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
* <p/>
* <p/>
* Objects of the following type(s) are allowed in the list
- * {@link Dependency }
+ * {@link Conditional }
*/
public List<Conditional> getConditional() {
if (conditional == null) {
@@ -309,9 +316,22 @@ public class Feature extends Content implements org.apache.karaf.features.Featur
return this.conditional;
}
+ public List<Capability> getCapabilities() {
+ if (capability == null) {
+ capability = new ArrayList<Capability>();
+ }
+ return this.capability;
+ }
+
+ public List<Requirement> getRequirements() {
+ if (requirement == null) {
+ requirement = new ArrayList<Requirement>();
+ }
+ return this.requirement;
+ }
+
public String toString() {
- String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
- return ret;
+ return getId();
}
@Override
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java
new file mode 100644
index 0000000..f7b5775
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Requirement.java
@@ -0,0 +1,87 @@
+/*
+ * 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.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+
+/**
+ *
+ * Additional requirement for a feature.
+ *
+ *
+ * <p>Java class for bundle complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * <complexType name="capability">
+ * <simpleContent>
+ * <extension base="<http://www.w3.org/2001/XMLSchema>string">
+ * </extension>
+ * </simpleContent>
+ * </complexType>
+ * </pre>
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "requirement", propOrder = {
+ "value"
+})
+public class Requirement implements org.apache.karaf.features.Requirement {
+
+ @XmlValue
+ protected String value;
+
+
+ public Requirement() {
+ }
+
+ public Requirement(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Requirement bundle = (Requirement) o;
+
+ if (value != null ? !value.equals(bundle.value) : bundle.value != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = value != null ? value.hashCode() : 0;
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
index 138366e..d51e5d5 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/osgi/Activator.java
@@ -17,22 +17,25 @@
package org.apache.karaf.features.internal.osgi;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;
-import javax.management.NotCompliantMBeanException;
-
import org.apache.karaf.features.FeaturesListener;
import org.apache.karaf.features.FeaturesService;
-import org.apache.karaf.features.internal.BootFeaturesInstaller;
-import org.apache.karaf.features.internal.BundleManager;
-import org.apache.karaf.features.internal.FeatureConfigInstaller;
-import org.apache.karaf.features.internal.FeatureFinder;
-import org.apache.karaf.features.internal.FeaturesServiceImpl;
-import org.apache.karaf.features.management.internal.FeaturesServiceMBeanImpl;
+import org.apache.karaf.features.internal.service.EventAdminListener;
+import org.apache.karaf.features.internal.service.FeatureConfigInstaller;
+import org.apache.karaf.features.internal.service.FeatureFinder;
+import org.apache.karaf.features.internal.service.BootFeaturesInstaller;
+import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
+import org.apache.karaf.features.internal.service.StateStorage;
+import org.apache.karaf.features.internal.management.FeaturesServiceMBeanImpl;
import org.apache.karaf.features.RegionsPersistence;
import org.apache.karaf.util.tracker.BaseActivator;
import org.apache.karaf.util.tracker.SingleServiceTracker;
@@ -50,6 +53,12 @@ public class Activator extends BaseActivator {
private FeaturesServiceImpl featuresService;
private SingleServiceTracker<RegionsPersistence> regionsTracker;
+ public Activator() {
+ // Special case here, as we don't want the activator to wait for current job to finish,
+ // else it would forbid the features service to refresh itself
+ setSchedulerStopTimeout(0);
+ }
+
@Override
protected void doOpen() throws Exception {
trackService(URLStreamHandlerService.class, "(url.handler.protocol=mvn)");
@@ -67,7 +76,7 @@ public class Activator extends BaseActivator {
updated((Dictionary) configuration);
}
- protected void doStart() throws NotCompliantMBeanException {
+ protected void doStart() throws Exception {
ConfigurationAdmin configurationAdmin = getTrackedService(ConfigurationAdmin.class);
URLStreamHandlerService mvnUrlHandler = getTrackedService(URLStreamHandlerService.class);
@@ -80,39 +89,62 @@ public class Activator extends BaseActivator {
props.put(Constants.SERVICE_PID, "org.apache.karaf.features.repos");
register(ManagedService.class, featureFinder, props);
- final BundleManager bundleManager = new BundleManager(bundleContext);
- regionsTracker = new SingleServiceTracker<RegionsPersistence>(bundleContext, RegionsPersistence.class,
- new SingleServiceTracker.SingleServiceListener() {
- @Override
- public void serviceFound() {
- bundleManager.setRegionsPersistence(regionsTracker.getService());
- }
- @Override
- public void serviceLost() {
- serviceFound();
- }
- @Override
- public void serviceReplaced() {
- serviceFound();
- }
- });
- regionsTracker.open();
+ // TODO: region support
+// final BundleManager bundleManager = new BundleManager(bundleContext);
+// regionsTracker = new SingleServiceTracker<RegionsPersistence>(bundleContext, RegionsPersistence.class,
+// new SingleServiceTracker.SingleServiceListener() {
+// @Override
+// public void serviceFound() {
+// bundleManager.setRegionsPersistence(regionsTracker.getService());
+// }
+// @Override
+// public void serviceLost() {
+// serviceFound();
+// }
+// @Override
+// public void serviceReplaced() {
+// serviceFound();
+// }
+// });
+// regionsTracker.open();
FeatureConfigInstaller configInstaller = new FeatureConfigInstaller(configurationAdmin);
- String featuresRepositories = getString("featuresRepositories", "");
- boolean respectStartLvlDuringFeatureStartup = getBoolean("respectStartLvlDuringFeatureStartup", true);
- boolean respectStartLvlDuringFeatureUninstall = getBoolean("respectStartLvlDuringFeatureUninstall", true);
- long resolverTimeout = getLong("resolverTimeout", 5000);
+ // TODO: honor respectStartLvlDuringFeatureStartup and respectStartLvlDuringFeatureUninstall
+// boolean respectStartLvlDuringFeatureStartup = getBoolean("respectStartLvlDuringFeatureStartup", true);
+// boolean respectStartLvlDuringFeatureUninstall = getBoolean("respectStartLvlDuringFeatureUninstall", true);
String overrides = getString("overrides", new File(System.getProperty("karaf.etc"), "overrides.properties").toString());
- featuresService = new FeaturesServiceImpl(bundleManager, configInstaller);
- featuresService.setUrls(featuresRepositories);
- featuresService.setRespectStartLvlDuringFeatureStartup(respectStartLvlDuringFeatureStartup);
- featuresService.setRespectStartLvlDuringFeatureUninstall(respectStartLvlDuringFeatureUninstall);
- featuresService.setResolverTimeout(resolverTimeout);
- featuresService.setOverrides(overrides);
- featuresService.setFeatureFinder(featureFinder);
- featuresService.start();
+ StateStorage stateStorage = new StateStorage() {
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ File file = bundleContext.getDataFile("FeaturesServiceState.properties");
+ if (file.exists()) {
+ return new FileInputStream(file);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected OutputStream getOutputStream() throws IOException {
+ File file = bundleContext.getDataFile("FeaturesServiceState.properties");
+ return new FileOutputStream(file);
+ }
+ };
+ EventAdminListener eventAdminListener;
+ try {
+ eventAdminListener = new EventAdminListener(bundleContext);
+ } catch (Throwable t) {
+ eventAdminListener = null;
+ }
+ featuresService = new FeaturesServiceImpl(
+ bundleContext.getBundle(),
+ bundleContext.getBundle(0).getBundleContext(),
+ stateStorage,
+ featureFinder,
+ eventAdminListener,
+ configInstaller,
+ overrides);
register(FeaturesService.class, featuresService);
featuresListenerTracker = new ServiceTracker<FeaturesListener, FeaturesListener>(
@@ -135,9 +167,12 @@ public class Activator extends BaseActivator {
);
featuresListenerTracker.open();
+ String featuresRepositories = getString("featuresRepositories", "");
String featuresBoot = getString("featuresBoot", "");
boolean featuresBootAsynchronous = getBoolean("featuresBootAsynchronous", false);
- BootFeaturesInstaller bootFeaturesInstaller = new BootFeaturesInstaller(bundleContext, featuresService, featuresBoot, featuresBootAsynchronous);
+ BootFeaturesInstaller bootFeaturesInstaller = new BootFeaturesInstaller(
+ bundleContext, featuresService,
+ featuresRepositories, featuresBoot, featuresBootAsynchronous);
bootFeaturesInstaller.start();
FeaturesServiceMBeanImpl featuresServiceMBean = new FeaturesServiceMBeanImpl();
@@ -157,7 +192,6 @@ public class Activator extends BaseActivator {
}
super.doStop();
if (featuresService != null) {
- featuresService.stop();
featuresService = null;
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java
new file mode 100644
index 0000000..0d5e83d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/AggregateRepository.java
@@ -0,0 +1,55 @@
+/*
+ * 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.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.service.repository.Repository;
+
+public class AggregateRepository implements Repository {
+
+ private final Collection<Repository> repositories;
+
+ public AggregateRepository(Collection<Repository> repositories) {
+ this.repositories = repositories;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ List<Capability> caps = new ArrayList<Capability>();
+ for (Repository repository : repositories) {
+ Map<Requirement, Collection<Capability>> resMap =
+ repository.findProviders(Collections.singleton(requirement));
+ Collection<Capability> res = resMap != null ? resMap.get(requirement) : null;
+ if (res != null) {
+ caps.addAll(res);
+ }
+ }
+ result.put(requirement, caps);
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
new file mode 100644
index 0000000..c4c0d16
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/BaseRepository.java
@@ -0,0 +1,86 @@
+/*
+ * 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.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.karaf.features.internal.resolver.CapabilitySet;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.repository.Repository;
+
+/**
+ */
+public class BaseRepository implements Repository {
+
+ protected final List<Resource> resources;
+ protected final Map<String, CapabilitySet> capSets;
+
+ public BaseRepository() {
+ this.resources = new ArrayList<Resource>();
+ this.capSets = new HashMap<String, CapabilitySet>();
+ }
+
+ protected void addResource(Resource resource) {
+ for (Capability cap : resource.getCapabilities(null)) {
+ String ns = cap.getNamespace();
+ CapabilitySet set = capSets.get(ns);
+ if (set == null) {
+ set = new CapabilitySet(Collections.singletonList(ns));
+ capSets.put(ns, set);
+ }
+ set.addCapability(cap);
+ }
+ resources.add(resource);
+ }
+
+ public List<Resource> getResources() {
+ return resources;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ CapabilitySet set = capSets.get(requirement.getNamespace());
+ if (set != null) {
+ SimpleFilter sf;
+ if (requirement instanceof RequirementImpl) {
+ sf = ((RequirementImpl) requirement).getFilter();
+ } else {
+ String filter = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
+ sf = (filter != null)
+ ? SimpleFilter.parse(filter)
+ : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ }
+ result.put(requirement, set.match(sf, true));
+ } else {
+ result.put(requirement, Collections.<Capability>emptyList());
+ }
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
new file mode 100644
index 0000000..7916821
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/CacheRepository.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.features.internal.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.service.repository.Repository;
+
+public class CacheRepository implements Repository {
+
+ private final Repository repository;
+ private final Map<Requirement, Collection<Capability>> cache =
+ new ConcurrentHashMap<Requirement, Collection<Capability>>();
+
+ public CacheRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public Map<Requirement, Collection<Capability>> findProviders(Collection<? extends Requirement> requirements) {
+ List<Requirement> missing = new ArrayList<Requirement>();
+ Map<Requirement, Collection<Capability>> result = new HashMap<Requirement, Collection<Capability>>();
+ for (Requirement requirement : requirements) {
+ Collection<Capability> caps = cache.get(requirement);
+ if (caps == null) {
+ missing.add(requirement);
+ } else {
+ result.put(requirement, caps);
+ }
+ }
+ Map<Requirement, Collection<Capability>> newCache = repository.findProviders(missing);
+ for (Requirement requirement : newCache.keySet()) {
+ cache.put(requirement, newCache.get(requirement));
+ result.put(requirement, newCache.get(requirement));
+ }
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
new file mode 100644
index 0000000..1aecef1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/HttpMetadataProvider.java
@@ -0,0 +1,88 @@
+/*
+ * 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.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.karaf.features.internal.util.JsonReader;
+
+/**
+ */
+public class HttpMetadataProvider implements MetadataProvider {
+
+ public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+ public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
+ public static final String GZIP = "gzip";
+
+ private final String url;
+ private long lastModified;
+ private Map<String, Map<String, String>> metadatas;
+
+ public HttpMetadataProvider(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ @Override
+ public Map<String, Map<String, String>> getMetadatas() {
+ try {
+ HttpURLConnection.setFollowRedirects(false);
+ HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
+ if (lastModified > 0) {
+ con.setIfModifiedSince(lastModified);
+ }
+ con.setRequestProperty(HEADER_ACCEPT_ENCODING, GZIP);
+ if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ lastModified = con.getLastModified();
+ InputStream is = con.getInputStream();
+ if (GZIP.equals(con.getHeaderField(HEADER_CONTENT_ENCODING))) {
+ is = new GZIPInputStream(is);
+ }
+ metadatas = verify(JsonReader.read(is));
+ } else if (con.getResponseCode() != HttpURLConnection.HTTP_NOT_MODIFIED) {
+ throw new IOException("Unexpected http response: "
+ + con.getResponseCode() + " " + con.getResponseMessage());
+ }
+ return metadatas;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Map<String, Map<String, String>> verify(Object value) {
+ Map<?,?> obj = Map.class.cast(value);
+ for (Map.Entry<?,?> entry : obj.entrySet()) {
+ String.class.cast(entry.getKey());
+ Map<?,?> child = Map.class.cast(entry.getValue());
+ for (Map.Entry<?,?> ce : child.entrySet()) {
+ String.class.cast(ce.getKey());
+ String.class.cast(ce.getValue());
+ }
+ }
+ return (Map<String, Map<String, String>>) obj;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
new file mode 100644
index 0000000..9ac54a1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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.repository;
+
+import java.util.Map;
+
+/**
+ */
+public interface MetadataProvider {
+
+ long getLastModified();
+
+ Map<String, Map<String, String>> getMetadatas();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
new file mode 100644
index 0000000..2d4fbba
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/MetadataRepository.java
@@ -0,0 +1,43 @@
+/*
+ * 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.repository;
+
+import java.util.Map;
+
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.osgi.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ */
+public class MetadataRepository extends BaseRepository {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MetadataRepository.class);
+
+ public MetadataRepository(MetadataProvider provider) {
+ Map<String, Map<String, String>> metadatas = provider.getMetadatas();
+ for (Map.Entry<String, Map<String, String>> metadata : metadatas.entrySet()) {
+ try {
+ Resource resource = ResourceBuilder.build(metadata.getKey(), metadata.getValue());
+ addResource(resource);
+ } catch (Exception e) {
+ LOGGER.info("Unable to build resource for " + metadata.getKey(), e);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
new file mode 100644
index 0000000..f289c8d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/repository/StaticRepository.java
@@ -0,0 +1,33 @@
+/*
+ * 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.repository;
+
+import java.util.Collection;
+
+import org.osgi.resource.Resource;
+
+/**
+ */
+public class StaticRepository extends BaseRepository {
+
+ public StaticRepository(Collection<Resource> resources) {
+ for (Resource resource : resources) {
+ addResource(resource);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java
new file mode 100644
index 0000000..0653398
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/BaseClause.java
@@ -0,0 +1,114 @@
+/*
+ * 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.resolver;
+
+import java.util.Map;
+
+import org.osgi.framework.Version;
+import org.osgi.resource.Resource;
+
+/**
+ */
+public abstract class BaseClause {
+
+ public abstract Resource getResource();
+
+ public abstract String getNamespace();
+
+ public abstract Map<String, String> getDirectives();
+
+ public abstract Map<String, Object> getAttributes();
+
+ @Override
+ public String toString() {
+ return toString(getResource(), getNamespace(), getAttributes(), getDirectives());
+ }
+
+ public static String toString(Resource res, String namespace, Map<String, Object> attrs, Map<String, String> dirs) {
+ StringBuilder sb = new StringBuilder();
+ if (res != null) {
+ sb.append("[").append(res).append("] ");
+ }
+ sb.append(namespace);
+ for (String key : attrs.keySet()) {
+ sb.append("; ");
+ append(sb, key, attrs.get(key), true);
+ }
+ for (String key : dirs.keySet()) {
+ sb.append("; ");
+ append(sb, key, dirs.get(key), false);
+ }
+ return sb.toString();
+ }
+
+ private static void append(StringBuilder sb, String key, Object val, boolean attribute) {
+ sb.append(key);
+ if (val instanceof Version) {
+ sb.append(":Version=");
+ sb.append(val);
+ } else if (val instanceof Long) {
+ sb.append(":Long=");
+ sb.append(val);
+ } else if (val instanceof Double) {
+ sb.append(":Double=");
+ sb.append(val);
+ } else if (val instanceof Iterable) {
+ Iterable it = (Iterable) val;
+ String scalar = null;
+ for (Object o : it) {
+ String ts;
+ if (o instanceof String) {
+ ts = "String";
+ } else if (o instanceof Long) {
+ ts = "Long";
+ } else if (o instanceof Double) {
+ ts = "Double";
+ } else if (o instanceof Version) {
+ ts = "Version";
+ } else {
+ throw new IllegalArgumentException("Unsupported scalar type: " + o);
+ }
+ if (scalar == null) {
+ scalar = ts;
+ } else if (!scalar.equals(ts)) {
+ throw new IllegalArgumentException("Unconsistent list type for attribute " + key);
+ }
+ }
+ sb.append(":List<").append(scalar).append(">=");
+ sb.append("\"");
+ boolean first = true;
+ for (Object o : it) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(o.toString().replace("\"", "\\\"").replace(",", "\\,"));
+ }
+ sb.append("\"");
+ } else {
+ sb.append(attribute ? "=" : ":=");
+ String s = val.toString();
+ if (s.matches("[0-9a-zA-Z_\\-.]*")) {
+ sb.append(s);
+ } else {
+ sb.append("\"").append(s.replace("\"", "\\\\")).append("\"");
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java
new file mode 100644
index 0000000..ad4cc85
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CandidateComparator.java
@@ -0,0 +1,129 @@
+/*
+ * 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.resolver;
+
+import java.util.Comparator;
+
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.resource.Capability;
+
+public class CandidateComparator implements Comparator<Capability>
+{
+ public int compare(Capability cap1, Capability cap2)
+ {
+ int c = 0;
+ // Always prefer system bundle
+ if (cap1 instanceof BundleCapability && !(cap2 instanceof BundleCapability)) {
+ c = -1;
+ } else if (!(cap1 instanceof BundleCapability) && cap2 instanceof BundleCapability) {
+ c = 1;
+ }
+ // Compare revision capabilities.
+ if ((c == 0) && cap1.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
+ {
+ c = ((Comparable) cap1.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE))
+ .compareTo(cap2.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
+ if (c == 0)
+ {
+ Version v1 = (!cap1.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap1.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+ Version v2 = (!cap2.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap2.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = compareVersions(v2, v1);
+ }
+ }
+ // Compare package capabilities.
+ else if ((c == 0) && cap1.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+ {
+ c = ((Comparable) cap1.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))
+ .compareTo(cap2.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
+ if (c == 0)
+ {
+ Version v1 = (!cap1.getAttributes().containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap1.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ Version v2 = (!cap2.getAttributes().containsKey(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap2.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = compareVersions(v2, v1);
+ // if same version, rather compare on the bundle version
+ if (c == 0)
+ {
+ v1 = (!cap1.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap1.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+ v2 = (!cap2.getAttributes().containsKey(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap2.getAttributes().get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = compareVersions(v2, v1);
+ }
+ }
+ }
+ // Compare feature capabilities
+ else if ((c == 0) && cap1.getNamespace().equals(FeatureNamespace.FEATURE_NAMESPACE))
+ {
+ c = ((Comparable) cap1.getAttributes().get(FeatureNamespace.FEATURE_NAMESPACE))
+ .compareTo(cap2.getAttributes().get(FeatureNamespace.FEATURE_NAMESPACE));
+ if (c == 0)
+ {
+ Version v1 = (!cap1.getAttributes().containsKey(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap1.getAttributes().get(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ Version v2 = (!cap2.getAttributes().containsKey(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE))
+ ? Version.emptyVersion
+ : (Version) cap2.getAttributes().get(FeatureNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = compareVersions(v2, v1);
+ }
+ }
+ return c;
+ }
+
+ private int compareVersions(Version v1, Version v2) {
+ int c = v1.getMajor() - v2.getMajor();
+ if (c != 0) {
+ return c;
+ }
+ c = v1.getMinor() - v2.getMinor();
+ if (c != 0) {
+ return c;
+ }
+ c = v1.getMicro() - v2.getMicro();
+ if (c != 0) {
+ return c;
+ }
+ String q1 = cleanQualifierForComparison(v1.getQualifier());
+ String q2 = cleanQualifierForComparison(v2.getQualifier());
+ return q1.compareTo(q2);
+ }
+
+ private String cleanQualifierForComparison(String qualifier) {
+ return qualifier.replaceAll("(redhat-[0-9]{3})([0-9]{3})", "$1-$2");
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
new file mode 100644
index 0000000..bfe9b40
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
@@ -0,0 +1,165 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.osgi.framework.Constants;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+public class CapabilityImpl extends BaseClause implements Capability {
+
+ private final Resource m_resource;
+ private final String m_namespace;
+ private final Map<String, String> m_dirs;
+ private final Map<String, Object> m_attrs;
+ private final List<String> m_uses;
+ private final List<List<String>> m_includeFilter;
+ private final List<List<String>> m_excludeFilter;
+ private final Set<String> m_mandatory;
+
+ public CapabilityImpl(Capability capability) {
+ this(null, capability.getNamespace(), capability.getDirectives(), capability.getAttributes());
+ }
+
+ public CapabilityImpl(Resource resource, String namespace,
+ Map<String, String> dirs, Map<String, Object> attrs) {
+ m_namespace = namespace;
+ m_resource = resource;
+ m_dirs = dirs;
+ m_attrs = attrs;
+
+ // Find all export directives: uses, mandatory, include, and exclude.
+
+ List<String> uses = Collections.emptyList();
+ String value = m_dirs.get(Constants.USES_DIRECTIVE);
+ if (value != null) {
+ // Parse these uses directive.
+ StringTokenizer tok = new StringTokenizer(value, ",");
+ uses = new ArrayList<String>(tok.countTokens());
+ while (tok.hasMoreTokens()) {
+ uses.add(tok.nextToken().trim());
+ }
+ }
+ m_uses = uses;
+
+ value = m_dirs.get(Constants.INCLUDE_DIRECTIVE);
+ if (value != null) {
+ List<String> filters = ResourceBuilder.parseDelimitedString(value, ",");
+ m_includeFilter = new ArrayList<List<String>>(filters.size());
+ for (String filter : filters) {
+ List<String> substrings = SimpleFilter.parseSubstring(filter);
+ m_includeFilter.add(substrings);
+ }
+ } else {
+ m_includeFilter = null;
+ }
+
+ value = m_dirs.get(Constants.EXCLUDE_DIRECTIVE);
+ if (value != null) {
+ List<String> filters = ResourceBuilder.parseDelimitedString(value, ",");
+ m_excludeFilter = new ArrayList<List<String>>(filters.size());
+ for (String filter : filters) {
+ List<String> substrings = SimpleFilter.parseSubstring(filter);
+ m_excludeFilter.add(substrings);
+ }
+ } else {
+ m_excludeFilter = null;
+ }
+
+ Set<String> mandatory = Collections.emptySet();
+ value = m_dirs.get(Constants.MANDATORY_DIRECTIVE);
+ if (value != null) {
+ List<String> names = ResourceBuilder.parseDelimitedString(value, ",");
+ mandatory = new HashSet<String>(names.size());
+ for (String name : names) {
+ // If attribute exists, then record it as mandatory.
+ if (m_attrs.containsKey(name)) {
+ mandatory.add(name);
+ }
+ // Otherwise, report an error.
+ else {
+ throw new IllegalArgumentException("Mandatory attribute '" + name + "' does not exist.");
+ }
+ }
+ }
+ m_mandatory = mandatory;
+ }
+
+ public Resource getResource() {
+ return m_resource;
+ }
+
+ public String getNamespace() {
+ return m_namespace;
+ }
+
+ public Map<String, String> getDirectives() {
+ return m_dirs;
+ }
+
+ public Map<String, Object> getAttributes() {
+ return m_attrs;
+ }
+
+ public boolean isAttributeMandatory(String name) {
+ return !m_mandatory.isEmpty() && m_mandatory.contains(name);
+ }
+
+ public List<String> getUses() {
+ return m_uses;
+ }
+
+ public boolean isIncluded(String name) {
+ if ((m_includeFilter == null) && (m_excludeFilter == null)) {
+ return true;
+ }
+
+ // Get the class name portion of the target class.
+ String className = getClassName(name);
+
+ // If there are no include filters then all classes are included
+ // by default, otherwise try to find one match.
+ boolean included = (m_includeFilter == null);
+ for (int i = 0; !included && m_includeFilter != null && i < m_includeFilter.size(); i++) {
+ included = SimpleFilter.compareSubstring(m_includeFilter.get(i), className);
+ }
+
+ // If there are no exclude filters then no classes are excluded
+ // by default, otherwise try to find one match.
+ boolean excluded = false;
+ for (int i = 0; (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.size()); i++) {
+ excluded = SimpleFilter.compareSubstring(m_excludeFilter.get(i), className);
+ }
+ return included && !excluded;
+ }
+
+ private static String getClassName(String className) {
+ if (className == null) {
+ className = "";
+ }
+ return (className.lastIndexOf('.') < 0) ? "" : className.substring(className.lastIndexOf('.') + 1);
+ }
+
+}
[08/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
new file mode 100644
index 0000000..a3ed213
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/BootFeaturesInstaller.java
@@ -0,0 +1,193 @@
+/*
+ * 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.service;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.karaf.features.BootFinished;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.FeaturesService;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BootFeaturesInstaller {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BootFeaturesInstaller.class);
+
+ public static String VERSION_PREFIX = "version=";
+
+ private final FeaturesServiceImpl featuresService;
+ private final BundleContext bundleContext;
+ private final String repositories;
+ private final String features;
+ private final boolean asynchronous;
+
+ /**
+ *
+ * @param features list of boot features separated by comma. Optionally contains ;version=x.x.x to specify a specific feature version
+ */
+ public BootFeaturesInstaller(BundleContext bundleContext,
+ FeaturesServiceImpl featuresService,
+ String repositories,
+ String features,
+ boolean asynchronous) {
+ this.bundleContext = bundleContext;
+ this.featuresService = featuresService;
+ this.repositories = repositories;
+ this.features = features;
+ this.asynchronous = asynchronous;
+ }
+
+ /**
+ * Install boot features
+ */
+ public void start() {
+ if (featuresService.isBootDone()) {
+ publishBootFinished();
+ return;
+ }
+ if (asynchronous) {
+ new Thread("Initial Features Provisioning") {
+ public void run() {
+ installBootFeatures();
+ }
+ }.start();
+ } else {
+ installBootFeatures();
+ }
+ }
+
+ protected void installBootFeatures() {
+ try {
+ for (String repo : repositories.split(",")) {
+ repo = repo.trim();
+ if (!repo.isEmpty()) {
+ try {
+ featuresService.addRepository(URI.create(repo));
+ } catch (Exception e) {
+ LOGGER.error("Error installing boot feature repository " + repo);
+ }
+ }
+ }
+
+ List<Set<String>> stagedFeatureNames = parseBootFeatures(features);
+ List<Set<Feature>> stagedFeatures = toFeatureSetList(stagedFeatureNames);
+ for (Set<Feature> features : stagedFeatures) {
+ featuresService.installFeatures(features, EnumSet.of(FeaturesService.Option.NoCleanIfFailure, FeaturesService.Option.ContinueBatchOnFailure));
+ }
+ featuresService.bootDone();
+ publishBootFinished();
+ } catch (Exception e) {
+ // Special handling in case the bundle has been refreshed.
+ // In such a case, simply exits without logging any exception
+ // as the restart should cause the feature service to finish
+ // the work.
+ if (e instanceof IllegalStateException) {
+ try {
+ bundleContext.getBundle();
+ } catch (IllegalStateException ies) {
+ return;
+ }
+ }
+ LOGGER.error("Error installing boot features", e);
+ }
+ }
+
+ private List<Set<Feature>> toFeatureSetList(List<Set<String>> stagedFeatures) {
+ ArrayList<Set<Feature>> result = new ArrayList<Set<Feature>>();
+ for (Set<String> features : stagedFeatures) {
+ HashSet<Feature> featureSet = new HashSet<Feature>();
+ for (String featureName : features) {
+ try {
+ Feature feature = getFeature(featureName);
+ if (feature == null) {
+ LOGGER.error("Error Boot feature " + featureName + " not found");
+ } else {
+ featureSet.add(feature);
+ }
+ } catch (Exception e) {
+ LOGGER.error("Error getting feature for feature string " + featureName, e);
+ }
+ }
+ result.add(featureSet);
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param featureSt either feature name or <featurename>;version=<version>
+ * @return feature matching the feature string
+ * @throws Exception
+ */
+ private Feature getFeature(String featureSt) throws Exception {
+ String[] parts = featureSt.trim().split(";");
+ String featureName = parts[0];
+ String featureVersion = null;
+ for (String part : parts) {
+ // if the part starts with "version=" it contains the version info
+ if (part.startsWith(VERSION_PREFIX)) {
+ featureVersion = part.substring(VERSION_PREFIX.length());
+ }
+ }
+ if (featureVersion == null) {
+ // no version specified - use default version
+ featureVersion = org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION;
+ }
+ return featuresService.getFeature(featureName, featureVersion);
+ }
+
+ protected List<Set<String>> parseBootFeatures(String bootFeatures) {
+ Pattern pattern = Pattern.compile("(\\((.+))\\),|.+");
+ Matcher matcher = pattern.matcher(bootFeatures);
+ List<Set<String>> result = new ArrayList<Set<String>>();
+ while (matcher.find()) {
+ String group = matcher.group(2) != null ? matcher.group(2) : matcher.group();
+ result.add(parseFeatureList(group));
+ }
+ return result;
+ }
+
+ protected Set<String> parseFeatureList(String group) {
+ HashSet<String> features = new HashSet<String>();
+ for (String feature : Arrays.asList(group.trim().split("\\s*,\\s*"))) {
+ if (feature.length() > 0) {
+ features.add(feature);
+ }
+ }
+ return features;
+ }
+
+ private void publishBootFinished() {
+ if (bundleContext != null) {
+ BootFinished bootFinished = new BootFinished() {};
+ bundleContext.registerService(BootFinished.class, bootFinished, new Hashtable<String, String>());
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java
new file mode 100644
index 0000000..b6eaae5
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/EventAdminListener.java
@@ -0,0 +1,91 @@
+/*
+ * 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.service;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.karaf.features.EventConstants;
+import org.apache.karaf.features.FeatureEvent;
+import org.apache.karaf.features.FeaturesListener;
+import org.apache.karaf.features.RepositoryEvent;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * A listener to publish events to EventAdmin
+ */
+public class EventAdminListener implements FeaturesListener {
+
+ private final ServiceTracker<EventAdmin, EventAdmin> tracker;
+
+ public EventAdminListener(BundleContext context) {
+ tracker = new ServiceTracker<EventAdmin, EventAdmin>(context, EventAdmin.class.getName(), null);
+ tracker.open();
+ }
+
+ public void featureEvent(FeatureEvent event) {
+ EventAdmin eventAdmin = tracker.getService();
+ if (eventAdmin == null) {
+ return;
+ }
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(EventConstants.TYPE, event.getType());
+ props.put(EventConstants.EVENT, event);
+ props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
+ props.put(EventConstants.FEATURE_NAME, event.getFeature().getName());
+ props.put(EventConstants.FEATURE_VERSION, event.getFeature().getVersion());
+ String topic;
+ switch (event.getType()) {
+ case FeatureInstalled:
+ topic = EventConstants.TOPIC_FEATURES_INSTALLED;
+ break;
+ case FeatureUninstalled:
+ topic = EventConstants.TOPIC_FEATURES_UNINSTALLED;
+ break;
+ default:
+ throw new IllegalStateException("Unknown features event type: " + event.getType());
+ }
+ eventAdmin.postEvent(new Event(topic, props));
+ }
+
+ public void repositoryEvent(RepositoryEvent event) {
+ EventAdmin eventAdmin = tracker.getService();
+ if (eventAdmin == null) {
+ return;
+ }
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(EventConstants.TYPE, event.getType());
+ props.put(EventConstants.EVENT, event);
+ props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
+ props.put(EventConstants.REPOSITORY_URI, event.getRepository().getURI().toString());
+ String topic;
+ switch (event.getType()) {
+ case RepositoryAdded:
+ topic = EventConstants.TOPIC_REPOSITORY_ADDED;
+ break;
+ case RepositoryRemoved:
+ topic = EventConstants.TOPIC_REPOSITORY_REMOVED;
+ break;
+ default:
+ throw new IllegalStateException("Unknown repository event type: " + event.getType());
+ }
+ eventAdmin.postEvent(new Event(topic, props));
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
new file mode 100644
index 0000000..0e9038d
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
@@ -0,0 +1,167 @@
+/*
+ * 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.service;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.karaf.features.ConfigFileInfo;
+import org.apache.karaf.features.Feature;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FeatureConfigInstaller {
+ private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
+ private static final String CONFIG_KEY = "org.apache.karaf.features.configKey";
+
+ private final ConfigurationAdmin configAdmin;
+
+ public FeatureConfigInstaller(ConfigurationAdmin configAdmin) {
+ this.configAdmin = configAdmin;
+ }
+
+ private String[] parsePid(String pid) {
+ int n = pid.indexOf('-');
+ if (n > 0) {
+ String factoryPid = pid.substring(n + 1);
+ pid = pid.substring(0, n);
+ return new String[]{pid, factoryPid};
+ } else {
+ return new String[]{pid, null};
+ }
+ }
+
+ private Configuration createConfiguration(ConfigurationAdmin configurationAdmin,
+ String pid, String factoryPid) throws IOException, InvalidSyntaxException {
+ if (factoryPid != null) {
+ return configurationAdmin.createFactoryConfiguration(factoryPid, null);
+ } else {
+ return configurationAdmin.getConfiguration(pid, null);
+ }
+ }
+
+ private Configuration findExistingConfiguration(ConfigurationAdmin configurationAdmin,
+ String pid, String factoryPid) throws IOException, InvalidSyntaxException {
+ String filter;
+ if (factoryPid == null) {
+ filter = "(" + Constants.SERVICE_PID + "=" + pid + ")";
+ } else {
+ String key = createConfigurationKey(pid, factoryPid);
+ filter = "(" + CONFIG_KEY + "=" + key + ")";
+ }
+ Configuration[] configurations = configurationAdmin.listConfigurations(filter);
+ if (configurations != null && configurations.length > 0) {
+ return configurations[0];
+ }
+ return null;
+ }
+
+ void installFeatureConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+ for (String config : feature.getConfigurations().keySet()) {
+ Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));
+ String[] pid = parsePid(config);
+ Configuration cfg = findExistingConfiguration(configAdmin, pid[0], pid[1]);
+ if (cfg == null) {
+ cfg = createConfiguration(configAdmin, pid[0], pid[1]);
+ String key = createConfigurationKey(pid[0], pid[1]);
+ props.put(CONFIG_KEY, key);
+ if (cfg.getBundleLocation() != null) {
+ cfg.setBundleLocation(null);
+ }
+ cfg.update(props);
+ }
+ }
+ for (ConfigFileInfo configFile : feature.getConfigurationFiles()) {
+ installConfigurationFile(configFile.getLocation(), configFile.getFinalname(), configFile.isOverride());
+ }
+ }
+
+ private String createConfigurationKey(String pid, String factoryPid) {
+ return factoryPid == null ? pid : pid + "-" + factoryPid;
+ }
+
+ private void installConfigurationFile(String fileLocation, String finalname, boolean override) throws IOException {
+ String basePath = System.getProperty("karaf.base");
+
+ if (finalname.contains("${")) {
+ //remove any placeholder or variable part, this is not valid.
+ int marker = finalname.indexOf("}");
+ finalname = finalname.substring(marker+1);
+ }
+
+ finalname = basePath + File.separator + finalname;
+
+ File file = new File(finalname);
+ if (file.exists()) {
+ if (!override) {
+ LOGGER.debug("Configuration file {} already exist, don't override it", finalname);
+ return;
+ } else {
+ LOGGER.info("Configuration file {} already exist, overriding it", finalname);
+ }
+ } else {
+ LOGGER.info("Creating configuration file {}", finalname);
+ }
+
+ InputStream is = null;
+ FileOutputStream fop = null;
+ try {
+ is = new BufferedInputStream(new URL(fileLocation).openStream());
+
+ if (!file.exists()) {
+ File parentFile = file.getParentFile();
+ if (parentFile != null)
+ parentFile.mkdirs();
+ file.createNewFile();
+ }
+
+ fop = new FileOutputStream(file);
+
+ int bytesRead;
+ byte[] buffer = new byte[1024];
+
+ while ((bytesRead = is.read(buffer)) != -1) {
+ fop.write(buffer, 0, bytesRead);
+ }
+ } catch (RuntimeException e) {
+ LOGGER.error(e.getMessage());
+ throw e;
+ } catch (MalformedURLException e) {
+ LOGGER.error(e.getMessage());
+ throw e;
+ } finally {
+ if (is != null)
+ is.close();
+ if (fop != null) {
+ fop.flush();
+ fop.close();
+ }
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java
new file mode 100644
index 0000000..d6defe0
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureFinder.java
@@ -0,0 +1,68 @@
+/*
+ * 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.service;
+
+import java.net.URI;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+public class FeatureFinder implements ManagedService {
+
+ final Map<String, String> nameToArtifactMap = new HashMap<String, String>();
+
+ public String[] getNames() {
+ synchronized (nameToArtifactMap) {
+ Set<String> strings = nameToArtifactMap.keySet();
+ return strings.toArray(new String[strings.size()]);
+ }
+ }
+
+ public URI getUriFor(String name, String version) {
+ String coords;
+ synchronized (nameToArtifactMap) {
+ coords = nameToArtifactMap.get(name);
+ }
+ if (coords == null) {
+ return null;
+ }
+ Artifact artifact = new Artifact(coords);
+ return artifact.getMavenUrl(version);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void updated(Dictionary properties) throws ConfigurationException {
+ synchronized (nameToArtifactMap) {
+ if (properties != null) {
+ nameToArtifactMap.clear();
+ Enumeration keys = properties.keys();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (!"felix.fileinstall.filename".equals(key) && !"service.pid".equals(key)) {
+ nameToArtifactMap.put(key, (String) properties.get(key));
+ }
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java
new file mode 100644
index 0000000..8fb161e
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureValidationUtil.java
@@ -0,0 +1,113 @@
+/*
+ * 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.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URLConnection;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+
+import org.apache.karaf.features.FeaturesNamespaces;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * Utility class which fires XML Schema validation.
+ */
+public class FeatureValidationUtil {
+
+ public static final QName FEATURES_0_0 = new QName("features");
+ public static final QName FEATURES_1_0 = new QName("http://karaf.apache.org/xmlns/features/v1.0.0", "features");
+ public static final QName FEATURES_1_1 = new QName("http://karaf.apache.org/xmlns/features/v1.1.0", "features");
+ public static final QName FEATURES_1_2 = new QName("http://karaf.apache.org/xmlns/features/v1.2.0", "features");
+ private static final Logger LOGGER = LoggerFactory.getLogger(FeatureValidationUtil.class);
+
+ /**
+ * Runs schema validation.
+ *
+ * @param uri Uri to validate.
+ * @throws Exception When validation fails.
+ */
+ public static void validate(URI uri) throws Exception {
+ Document doc = load(uri);
+
+ QName name = new QName(doc.getDocumentElement().getNamespaceURI(), doc.getDocumentElement().getLocalName());
+
+ if (FeaturesNamespaces.FEATURES_0_0_0.equals(name)) {
+ LOGGER.warn("Old style feature file without namespace found (URI: {}). This format is deprecated and support for it will soon be removed", uri);
+ return;
+ } else if (FeaturesNamespaces.FEATURES_1_0_0.equals(name)) {
+ validate(doc, "/org/apache/karaf/features/karaf-features-1.0.0.xsd");
+ } else if (FeaturesNamespaces.FEATURES_1_1_0.equals(name)) {
+ validate(doc, "/org/apache/karaf/features/karaf-features-1.1.0.xsd");
+ } else if (FeaturesNamespaces.FEATURES_1_2_0.equals(name)) {
+ validate(doc, "/org/apache/karaf/features/karaf-features-1.2.0.xsd");
+ } else if (FeaturesNamespaces.FEATURES_1_3_0.equals(name)) {
+ validate(doc, "/org/apache/karaf/features/karaf-features-1.3.0.xsd");
+ }
+ else {
+ throw new IllegalArgumentException("Unrecognized root element: " + name);
+ }
+ }
+
+ private static Document load(URI uri) throws IOException, SAXException, ParserConfigurationException {
+ InputStream stream = null;
+ try {
+ URLConnection conn;
+ try {
+ conn = uri.toURL().openConnection();
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("invalid URI: " + uri, e);
+ }
+ conn.setDefaultUseCaches(false);
+ stream = conn.getInputStream();
+ // load document and check the root element for namespace declaration
+ DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
+ dFactory.setNamespaceAware(true);
+ return dFactory.newDocumentBuilder().parse(stream);
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ private static void validate(Document doc, String schemaLocation) throws SAXException {
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ // root element has namespace - we can use schema validation
+ Schema schema = factory.newSchema(new StreamSource(FeatureValidationUtil.class.getResourceAsStream(schemaLocation)));
+ // create schema by reading it from an XSD file:
+ Validator validator = schema.newValidator();
+ try {
+ validator.validate(new DOMSource(doc));
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Unable to validate " + doc.getDocumentURI(), e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/38502e41/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
new file mode 100644
index 0000000..457b5e1
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
@@ -0,0 +1,1378 @@
+/*
+ * 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.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+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.internal.deployment.DeploymentBuilder;
+import org.apache.karaf.features.internal.deployment.StreamProvider;
+import org.apache.karaf.features.internal.resolver.FeatureNamespace;
+import org.apache.karaf.features.internal.resolver.UriNamespace;
+import org.apache.karaf.features.internal.util.ChecksumUtils;
+import org.apache.karaf.features.internal.util.Macro;
+import org.apache.karaf.features.internal.util.MultiException;
+import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.felix.resolver.Util.getSymbolicName;
+import static org.apache.felix.resolver.Util.getVersion;
+
+/**
+ *
+ */
+public class FeaturesServiceImpl implements FeaturesService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class);
+ private static final String SNAPSHOT = "SNAPSHOT";
+ private static final String MAVEN = "mvn:";
+
+ private final Bundle bundle;
+ private final BundleContext systemBundleContext;
+ private final StateStorage storage;
+ private final FeatureFinder featureFinder;
+ private final EventAdminListener eventAdminListener;
+ private final FeatureConfigInstaller configInstaller;
+ private final String overrides;
+
+ private final List<FeaturesListener> listeners = new CopyOnWriteArrayIdentityList<FeaturesListener>();
+
+ // Synchronized on lock
+ private final Object lock = new Object();
+ private final State state = new State();
+ private final Map<String, Repository> repositoryCache = new HashMap<String, Repository>();
+ private Map<String, Map<String, Feature>> featureCache;
+
+
+
+ public FeaturesServiceImpl(Bundle bundle,
+ BundleContext systemBundleContext,
+ StateStorage storage,
+ FeatureFinder featureFinder,
+ EventAdminListener eventAdminListener,
+ FeatureConfigInstaller configInstaller,
+ String overrides) {
+ this.bundle = bundle;
+ this.systemBundleContext = systemBundleContext;
+ this.storage = storage;
+ this.featureFinder = featureFinder;
+ this.eventAdminListener = eventAdminListener;
+ this.configInstaller = configInstaller;
+ this.overrides = overrides;
+ loadState();
+ }
+
+ //
+ // State support
+ //
+
+ protected void loadState() {
+ try {
+ synchronized (lock) {
+ storage.load(state);
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Error loading FeaturesService state", e);
+ }
+ }
+
+ protected void saveState() {
+ try {
+ synchronized (lock) {
+ storage.save(state);
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Error saving FeaturesService state", e);
+ }
+ }
+
+ boolean isBootDone() {
+ synchronized (lock) {
+ return state.bootDone.get();
+ }
+ }
+
+ void bootDone() {
+ synchronized (lock) {
+ state.bootDone.set(true);
+ saveState();
+ }
+ }
+
+ //
+ // Listeners support
+ //
+
+ public void registerListener(FeaturesListener listener) {
+ listeners.add(listener);
+ try {
+ Set<String> repositories = new TreeSet<String>();
+ Set<String> installedFeatures = new TreeSet<String>();
+ synchronized (lock) {
+ repositories.addAll(state.repositories);
+ installedFeatures.addAll(state.installedFeatures);
+ }
+ for (String uri : repositories) {
+ Repository repository = new RepositoryImpl(URI.create(uri));
+ listener.repositoryEvent(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, true));
+ }
+ for (String id : installedFeatures) {
+ Feature feature = org.apache.karaf.features.internal.model.Feature.valueOf(id);
+ 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);
+ }
+
+ 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);
+ }
+ }
+
+ //
+ // Feature Finder support
+ //
+
+ @Override
+ public URI getRepositoryUriFor(String name, String version) {
+ return featureFinder.getUriFor(name, version);
+ }
+
+ @Override
+ public String[] getRepositoryNames() {
+ return featureFinder.getNames();
+ }
+
+
+ //
+ // Repositories support
+ //
+
+ public Repository loadRepository(URI uri) throws Exception {
+ // TODO: merge validation and loading by loading the DOM, validating, unmarshalling
+ FeatureValidationUtil.validate(uri);
+ RepositoryImpl repo = new RepositoryImpl(uri);
+ repo.load();
+ return repo;
+ }
+
+ @Override
+ public void validateRepository(URI uri) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addRepository(URI uri) throws Exception {
+ addRepository(uri, false);
+ }
+
+ @Override
+ public void addRepository(URI uri, boolean install) throws Exception {
+ if (install) {
+ // TODO: implement
+ throw new UnsupportedOperationException();
+ }
+ Repository repository = loadRepository(uri);
+ synchronized (lock) {
+ // Clean cache
+ repositoryCache.put(uri.toString(), repository);
+ featureCache = null;
+ // Add repo
+ if (!state.repositories.add(uri.toString())) {
+ return;
+ }
+ saveState();
+ }
+ callListeners(new RepositoryEvent(repository, RepositoryEvent.EventType.RepositoryAdded, false));
+ }
+
+ @Override
+ public void removeRepository(URI uri) throws Exception {
+ removeRepository(uri, true);
+ }
+
+ @Override
+ public void removeRepository(URI uri, boolean uninstall) throws Exception {
+ // TODO: check we don't have any feature installed from this repository
+ Repository repo;
+ synchronized (lock) {
+ // Remove repo
+ if (!state.repositories.remove(uri.toString())) {
+ return;
+ }
+ // Clean cache
+ featureCache = null;
+ repo = repositoryCache.get(uri.toString());
+ List<String> toRemove = new ArrayList<String>();
+ toRemove.add(uri.toString());
+ while (!toRemove.isEmpty()) {
+ Repository rep = repositoryCache.remove(toRemove.remove(0));
+ if (rep != null) {
+ for (URI u : rep.getRepositories()) {
+ toRemove.add(u.toString());
+ }
+ }
+ }
+ saveState();
+ }
+ if (repo == null) {
+ repo = new RepositoryImpl(uri);
+ }
+ callListeners(new RepositoryEvent(repo, RepositoryEvent.EventType.RepositoryRemoved, false));
+ }
+
+ @Override
+ public void restoreRepository(URI uri) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refreshRepository(URI uri) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Repository[] listRepositories() {
+ // TODO: catching this exception is ugly: refactor the api
+ try {
+ getFeatures();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ synchronized (lock) {
+ return repositoryCache.values().toArray(new Repository[repositoryCache.size()]);
+ }
+ }
+
+ @Override
+ public Repository getRepository(String name) {
+ synchronized (lock) {
+ for (Repository repo : this.repositoryCache.values()) {
+ if (name.equals(repo.getName())) {
+ return repo;
+ }
+ }
+ return null;
+ }
+ }
+
+ //
+ // Features support
+ //
+
+ public Feature getFeature(String name) throws Exception {
+ return getFeature(name, null);
+ }
+
+ public Feature getFeature(String name, String version) throws Exception {
+ Map<String, Feature> versions = getFeatures().get(name);
+ return getFeatureMatching(versions, version);
+ }
+
+ protected Feature getFeatureMatching(Map<String, Feature> versions, String version) {
+ if (version != null) {
+ version = version.trim();
+ if (version.equals(org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION)) {
+ version = "";
+ }
+ } else {
+ version = "";
+ }
+ if (versions == null || versions.isEmpty()) {
+ return null;
+ } else {
+ Feature feature = version.isEmpty() ? null : versions.get(version);
+ if (feature == null) {
+ // Compute version range. If an version has been given, assume exact range
+ VersionRange versionRange = version.isEmpty() ?
+ new VersionRange(Version.emptyVersion) :
+ new VersionRange(version, true, true);
+ Version latest = Version.emptyVersion;
+ for (String available : versions.keySet()) {
+ Version availableVersion = VersionTable.getVersion(available);
+ if (availableVersion.compareTo(latest) >= 0 && versionRange.contains(availableVersion)) {
+ feature = versions.get(available);
+ latest = availableVersion;
+ }
+ }
+ }
+ return feature;
+ }
+ }
+
+ 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()]);
+ }
+
+ protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
+ List<String> uris;
+ synchronized (lock) {
+ if (featureCache != null) {
+ return featureCache;
+ }
+ uris = new ArrayList<String>(state.repositories);
+ }
+ //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
+ List<String> toLoad = new ArrayList<String>(uris);
+ while (!toLoad.isEmpty()) {
+ String uri = toLoad.remove(0);
+ Repository repo;
+ synchronized (lock) {
+ repo = repositoryCache.get(uri);
+ }
+ if (repo == null) {
+ RepositoryImpl rep = new RepositoryImpl(URI.create(uri));
+ rep.load();
+ repo = rep;
+ synchronized (lock) {
+ repositoryCache.put(uri, repo);
+ }
+ }
+ for (URI u : repo.getRepositories()) {
+ toLoad.add(u.toString());
+ }
+ }
+ List<Repository> repos;
+ synchronized (lock) {
+ repos = new ArrayList<Repository>(repositoryCache.values());
+ }
+ // * then load all features
+ for (Repository repo : repos) {
+ 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);
+ }
+ }
+ }
+ synchronized (lock) {
+ if (uris.size() == state.repositories.size() &&
+ state.repositories.containsAll(uris)) {
+ featureCache = map;
+ }
+ }
+ return map;
+ }
+
+ //
+ // Installed features
+ //
+
+ @Override
+ public Feature[] listInstalledFeatures() throws Exception {
+ Set<Feature> features = new HashSet<Feature>();
+ Map<String, Map<String, Feature>> allFeatures = getFeatures();
+ synchronized (lock) {
+ for (Map<String, Feature> featureWithDifferentVersion : allFeatures.values()) {
+ for (Feature f : featureWithDifferentVersion.values()) {
+ if (isInstalled(f)) {
+ features.add(f);
+ }
+ }
+ }
+ }
+ return features.toArray(new Feature[features.size()]);
+ }
+
+ @Override
+ public boolean isInstalled(Feature f) {
+ String id = normalize(f.getId());
+ synchronized (lock) {
+ return state.installedFeatures.contains(id);
+ }
+ }
+
+ //
+ // Installation and uninstallation of features
+ //
+
+ public void installFeature(String name) throws Exception {
+ installFeature(name, EnumSet.noneOf(Option.class));
+ }
+
+ public void installFeature(String name, String version) throws Exception {
+ installFeature(version != null ? name + "/" + version : name, EnumSet.noneOf(Option.class));
+ }
+
+ public void installFeature(String name, EnumSet<Option> options) throws Exception {
+ doAddFeatures(Collections.singleton(name), options);
+ }
+
+ public void installFeature(String name, String version, EnumSet<Option> options) throws Exception {
+ installFeature(version != null ? name + "/" + version : name, options);
+ }
+
+ public void installFeature(Feature feature, EnumSet<Option> options) throws Exception {
+ installFeature(feature.getId());
+ }
+
+ public void installFeatures(Set<Feature> features, EnumSet<Option> options) throws Exception {
+ Set<String> fs = new HashSet<String>();
+ for (Feature f : features) {
+ fs.add(f.getId());
+ }
+ doAddFeatures(fs, options);
+ }
+
+ @Override
+ public void uninstallFeature(String name, String version) throws Exception {
+ uninstallFeature(version != null ? name + "/" + version : name);
+ }
+
+ @Override
+ public void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception {
+ uninstallFeature(version != null ? name + "/" + version : name, options);
+ }
+
+ @Override
+ public void uninstallFeature(String name) throws Exception {
+ uninstallFeature(name, EnumSet.noneOf(Option.class));
+ }
+
+ @Override
+ public void uninstallFeature(String name, EnumSet<Option> options) throws Exception {
+ doRemoveFeatures(Collections.singleton(name), options);
+ }
+
+
+ //
+ //
+ //
+ // RESOLUTION
+ //
+ //
+ //
+
+
+
+
+
+
+ public void doAddFeatures(Set<String> features, EnumSet<Option> options) throws Exception {
+ Set<String> required;
+ Set<Long> managed;
+ synchronized (lock) {
+ required = new HashSet<String>(state.features);
+ managed = new HashSet<Long>(state.managedBundles);
+ }
+ List<String> featuresToAdd = new ArrayList<String>();
+ Map<String, Map<String, Feature>> featuresMap = getFeatures();
+ for (String feature : features) {
+ feature = normalize(feature);
+ String name = feature.substring(0, feature.indexOf("/"));
+ String version = feature.substring(feature.indexOf("/") + 1);
+ Feature f = getFeatureMatching(featuresMap.get(name), version);
+ if (f == null) {
+ throw new IllegalArgumentException("No matching features for " + feature);
+ }
+ featuresToAdd.add(normalize(f.getId()));
+ }
+ featuresToAdd = new ArrayList<String>(new LinkedHashSet<String>(featuresToAdd));
+ StringBuilder sb = new StringBuilder();
+ sb.append("Adding features: ");
+ for (int i = 0; i < featuresToAdd.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(featuresToAdd.get(i));
+ }
+ print(sb.toString(), options.contains(Option.Verbose));
+ required.addAll(featuresToAdd);
+ doInstallFeaturesInThread(required, managed, options);
+ }
+
+ public void doRemoveFeatures(Set<String> features, EnumSet<Option> options) throws Exception {
+ Set<String> required;
+ Set<Long> managed;
+ synchronized (lock) {
+ required = new HashSet<String>(state.features);
+ managed = new HashSet<Long>(state.managedBundles);
+ }
+ List<String> featuresToRemove = new ArrayList<String>();
+ for (String feature : new HashSet<String>(features)) {
+ List<String> toRemove = new ArrayList<String>();
+ feature = normalize(feature);
+ if (feature.endsWith("/0.0.0")) {
+ String nameSep = feature.substring(0, feature.indexOf("/") + 1);
+ for (String f : required) {
+ if (normalize(f).startsWith(nameSep)) {
+ toRemove.add(f);
+ }
+ }
+ } else {
+ toRemove.add(feature);
+ }
+ toRemove.retainAll(required);
+ if (toRemove.isEmpty()) {
+ throw new IllegalArgumentException("Feature named '" + feature + "' is not installed");
+ } else if (toRemove.size() > 1) {
+ String name = feature.substring(0, feature.indexOf("/"));
+ StringBuilder sb = new StringBuilder();
+ sb.append("Feature named '").append(name).append("' has multiple versions installed (");
+ for (int i = 0; i < toRemove.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(toRemove.get(i));
+ }
+ sb.append("). Please specify the version to uninstall.");
+ throw new IllegalArgumentException(sb.toString());
+ }
+ featuresToRemove.addAll(toRemove);
+ }
+ featuresToRemove = new ArrayList<String>(new LinkedHashSet<String>(featuresToRemove));
+ StringBuilder sb = new StringBuilder();
+ sb.append("Removing features: ");
+ for (int i = 0; i < featuresToRemove.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(featuresToRemove.get(i));
+ }
+ print(sb.toString(), options.contains(Option.Verbose));
+ required.removeAll(featuresToRemove);
+ doInstallFeaturesInThread(required, managed, options);
+ }
+
+ protected String normalize(String feature) {
+ if (!feature.contains("/")) {
+ feature += "/0.0.0";
+ }
+ int idx = feature.indexOf("/");
+ String name = feature.substring(0, idx);
+ String version = feature.substring(idx + 1);
+ return name + "/" + VersionTable.getVersion(version).toString();
+ }
+
+ /**
+ * Actual deployment needs to be done in a separate thread.
+ * The reason is that if the console is refreshed, the current thread which is running
+ * the command may be interrupted while waiting for the refresh to be done, leading
+ * to bundles not being started after the refresh.
+ */
+ public void doInstallFeaturesInThread(final Set<String> features,
+ final Set<Long> managed,
+ final EnumSet<Option> options) throws Exception {
+ ExecutorService executor = Executors.newCachedThreadPool();
+ try {
+ executor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ doInstallFeatures(features, managed, options);
+ return null;
+ }
+ }).get();
+ } catch (ExecutionException e) {
+ Throwable t = e.getCause();
+ if (t instanceof RuntimeException) {
+ throw ((RuntimeException) t);
+ } else if (t instanceof Error) {
+ throw ((Error) t);
+ } else if (t instanceof Exception) {
+ throw (Exception) t;
+ } else {
+ throw e;
+ }
+ } finally {
+ executor.shutdown();
+ }
+ }
+
+ public void doInstallFeatures(Set<String> features, Set<Long> managed, EnumSet<Option> options) throws Exception {
+ // TODO: make this configurable through ConfigAdmin
+ // TODO: this needs to be tested a bit
+ // TODO: note that this only applies to managed and updateable bundles
+ boolean updateSnaphots = true;
+
+ // TODO: make this configurable at runtime
+ // TODO: note that integration tests will fail if set to false
+ // TODO: but I think it should be the default anyway
+ boolean noRefreshUnmanaged = true;
+
+ // TODO: make this configurable at runtime
+ boolean noRefreshManaged = true;
+
+ // TODO: make this configurable at runtime
+ boolean noRefresh = false;
+
+ // TODO: make this configurable through ConfigAdmin
+ // TODO: though opening it as some important effects
+ String featureResolutionRange = "${range;[====,====]}";
+
+ // TODO: make this configurable through ConfigAdmin
+ String bundleUpdateRange = "${range;[==,=+)}";
+
+ boolean verbose = options.contains(Option.Verbose);
+
+ // Get a list of resolved and unmanaged bundles to use as capabilities during resolution
+ List<Resource> systemBundles = new ArrayList<Resource>();
+ Bundle[] bundles = systemBundleContext.getBundles();
+ for (Bundle bundle : bundles) {
+ if (bundle.getState() >= Bundle.RESOLVED && !managed.contains(bundle.getBundleId())) {
+ Resource res = bundle.adapt(BundleRevision.class);
+ systemBundles.add(res);
+ }
+ }
+ // Resolve
+ // TODO: requirements
+ // TODO: bundles
+ // TODO: regions: on isolated regions, we may need different resolution for each region
+ Set<String> overrides = Overrides.loadOverrides(this.overrides);
+ Repository[] repositories = listRepositories();
+ DeploymentBuilder builder = createDeploymentBuilder(repositories);
+ builder.setFeatureRange(featureResolutionRange);
+ builder.download(features,
+ Collections.<String>emptySet(),
+ Collections.<String>emptySet(),
+ overrides,
+ Collections.<String>emptySet());
+ Collection<Resource> allResources = builder.resolve(systemBundles, false);
+ Map<String, StreamProvider> providers = builder.getProviders();
+
+ // Install conditionals
+ List<String> installedFeatureIds = getFeatureIds(allResources);
+ List<Feature> installedFeatures = getFeatures(repositories, installedFeatureIds);
+
+ // TODO: is there are a way to use fragments or on-demand resources
+ // TODO: in the resolver to use a single resolution ?
+ boolean resolveAgain = false;
+ Set<String> featuresAndConditionals = new TreeSet<String>(features);
+ for (Feature feature : installedFeatures) {
+ for (Conditional cond : feature.getConditional()) {
+ boolean condSatisfied = true;
+ for (Dependency dep : cond.getCondition()) {
+ boolean depSatisfied = false;
+ String name = dep.getName();
+ VersionRange range = new VersionRange(dep.getVersion(), false, true);
+ for (Feature f : installedFeatures) {
+ if (f.getName().equals(name)) {
+ if (range.contains(VersionTable.getVersion(f.getVersion()))) {
+ depSatisfied = true;
+ break;
+ }
+ }
+ }
+ if (!depSatisfied) {
+ condSatisfied = false;
+ break;
+ }
+ }
+ if (condSatisfied) {
+ featuresAndConditionals.add(cond.asFeature(feature.getName(), feature.getVersion()).getId());
+ resolveAgain = true;
+ }
+ }
+ }
+ if (resolveAgain) {
+ builder.download(featuresAndConditionals,
+ Collections.<String>emptySet(),
+ Collections.<String>emptySet(),
+ overrides,
+ Collections.<String>emptySet());
+ allResources = builder.resolve(systemBundles, false);
+ providers = builder.getProviders();
+ }
+
+
+ //
+ // Compute list of installable resources (those with uris)
+ //
+ List<Resource> resources = getBundles(allResources);
+
+ // Compute information for each bundle
+ Map<String, BundleInfo> bundleInfos = new HashMap<String, BundleInfo>();
+ for (Feature feature : getFeatures(repositories, getFeatureIds(allResources))) {
+ for (BundleInfo bi : feature.getBundles()) {
+ BundleInfo oldBi = bundleInfos.get(bi.getLocation());
+ if (oldBi != null) {
+ bi = mergeBundleInfo(bi, oldBi);
+ }
+ bundleInfos.put(bi.getLocation(), bi);
+ }
+ }
+
+
+ //
+ // Compute deployment
+ //
+ Map<String, Long> bundleChecksums = new HashMap<String, Long>();
+ synchronized (lock) {
+ bundleChecksums.putAll(state.bundleChecksums);
+ }
+ Deployment deployment = computeDeployment(managed, updateSnaphots, bundles, providers, resources, bundleChecksums, bundleUpdateRange);
+
+ if (deployment.toDelete.isEmpty() &&
+ deployment.toUpdate.isEmpty() &&
+ deployment.toInstall.isEmpty()) {
+ print("No deployment change.", verbose);
+ return;
+ }
+ //
+ // Log deployment
+ //
+ logDeployment(deployment);
+
+
+ Set<Bundle> toRefresh = new HashSet<Bundle>();
+ Set<Bundle> toStart = new HashSet<Bundle>();
+
+ //
+ // Execute deployment
+ //
+
+ // TODO: handle update on the features service itself
+ if (deployment.toUpdate.containsKey(bundle) ||
+ deployment.toDelete.contains(bundle)) {
+
+ LOGGER.warn("Updating or uninstalling of the FeaturesService is not supported");
+ deployment.toUpdate.remove(bundle);
+ deployment.toDelete.remove(bundle);
+
+ }
+
+ //
+ // Perform bundle operations
+ //
+
+ // Stop bundles by chunks
+ Set<Bundle> toStop = new HashSet<Bundle>();
+ toStop.addAll(deployment.toUpdate.keySet());
+ toStop.addAll(deployment.toDelete);
+ removeFragmentsAndBundlesInState(toStop, Bundle.UNINSTALLED | Bundle.RESOLVED | Bundle.STOPPING);
+ if (!toStop.isEmpty()) {
+ print("Stopping bundles:", verbose);
+ while (!toStop.isEmpty()) {
+ List<Bundle> bs = getBundlesToStop(toStop);
+ for (Bundle bundle : bs) {
+ print(" " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
+ bundle.stop(Bundle.STOP_TRANSIENT);
+ toStop.remove(bundle);
+ }
+ }
+ }
+ if (!deployment.toDelete.isEmpty()) {
+ print("Uninstalling bundles:", verbose);
+ for (Bundle bundle : deployment.toDelete) {
+ print(" " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
+ bundle.uninstall();
+ managed.remove(bundle.getBundleId());
+ toRefresh.add(bundle);
+ }
+ }
+ if (!deployment.toUpdate.isEmpty()) {
+ print("Updating bundles:", verbose);
+ for (Map.Entry<Bundle, Resource> entry : deployment.toUpdate.entrySet()) {
+ Bundle bundle = entry.getKey();
+ Resource resource = entry.getValue();
+ String uri = UriNamespace.getUri(resource);
+ print(" " + uri, verbose);
+ InputStream is = getBundleInputStream(resource, providers);
+ bundle.update(is);
+ toRefresh.add(bundle);
+ toStart.add(bundle);
+ BundleInfo bi = bundleInfos.get(uri);
+ if (bi != null && bi.getStartLevel() > 0) {
+ bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel());
+ }
+ // TODO: handle region
+ }
+ }
+ if (!deployment.toInstall.isEmpty()) {
+ print("Installing bundles:", verbose);
+ for (Resource resource : deployment.toInstall) {
+ String uri = UriNamespace.getUri(resource);
+ print(" " + uri, verbose);
+ InputStream is = getBundleInputStream(resource, providers);
+ Bundle bundle = systemBundleContext.installBundle(uri, is);
+ managed.add(bundle.getBundleId());
+ toStart.add(bundle);
+ deployment.resToBnd.put(resource, bundle);
+ // save a checksum of installed snapshot bundle
+ if (isUpdateable(resource) && !deployment.newCheckums.containsKey(bundle.getLocation())) {
+ deployment.newCheckums.put(bundle.getLocation(), ChecksumUtils.checksum(getBundleInputStream(resource, providers)));
+ }
+ BundleInfo bi = bundleInfos.get(uri);
+ if (bi != null && bi.getStartLevel() > 0) {
+ bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel());
+ }
+ // TODO: handle region
+ }
+ }
+
+ //
+ // Update and save state
+ //
+ List<String> newFeatures = new ArrayList<String>();
+ synchronized (lock) {
+ List<String> allFeatures = new ArrayList<String>();
+ for (Resource resource : allResources) {
+ String name = FeatureNamespace.getName(resource);
+ if (name != null) {
+ Version version = FeatureNamespace.getVersion(resource);
+ String id = version != null ? name + "/" + version : name;
+ allFeatures.add(id);
+ if (!state.installedFeatures.contains(id)) {
+ newFeatures.add(id);
+ }
+ }
+ }
+ state.bundleChecksums.putAll(deployment.newCheckums);
+ state.features.clear();
+ state.features.addAll(features);
+ state.installedFeatures.clear();
+ state.installedFeatures.addAll(allFeatures);
+ state.managedBundles.clear();
+ state.managedBundles.addAll(managed);
+ saveState();
+ }
+
+ //
+ // Install configurations
+ //
+ if (configInstaller != null && !newFeatures.isEmpty()) {
+ for (Repository repository : repositories) {
+ for (Feature feature : repository.getFeatures()) {
+ if (newFeatures.contains(feature.getId())) {
+ configInstaller.installFeatureConfigs(feature);
+ }
+ }
+ }
+ }
+
+ if (!noRefreshManaged) {
+ findBundlesWithOptionalPackagesToRefresh(toRefresh);
+ findBundlesWithFragmentsToRefresh(toRefresh);
+ }
+
+ if (noRefreshUnmanaged) {
+ Set<Bundle> newSet = new HashSet<Bundle>();
+ for (Bundle bundle : toRefresh) {
+ if (managed.contains(bundle.getBundleId())) {
+ newSet.add(bundle);
+ }
+ }
+ toRefresh = newSet;
+ }
+
+ // TODO: remove this hack, but it avoids loading the class after the bundle is refreshed
+ RequirementSort sort = new RequirementSort();
+
+ if (!noRefresh) {
+ toStop = new HashSet<Bundle>();
+ toStop.addAll(toRefresh);
+ removeFragmentsAndBundlesInState(toStop, Bundle.UNINSTALLED | Bundle.RESOLVED | Bundle.STOPPING);
+ if (!toStop.isEmpty()) {
+ print("Stopping bundles:", verbose);
+ while (!toStop.isEmpty()) {
+ List<Bundle> bs = getBundlesToStop(toStop);
+ for (Bundle bundle : bs) {
+ print(" " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
+ bundle.stop(Bundle.STOP_TRANSIENT);
+ toStop.remove(bundle);
+ toStart.add(bundle);
+ }
+ }
+ }
+
+ if (!toRefresh.isEmpty()) {
+ print("Refreshing bundles:", verbose);
+ for (Bundle bundle : toRefresh) {
+ print(" " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose);
+ }
+ if (!toRefresh.isEmpty()) {
+ refreshPackages(toRefresh);
+ }
+ }
+ }
+
+ // Compute bundles to start
+ removeFragmentsAndBundlesInState(toStart, Bundle.UNINSTALLED | Bundle.ACTIVE | Bundle.STARTING);
+ if (!toStart.isEmpty()) {
+ // Compute correct start order
+ List<Exception> exceptions = new ArrayList<Exception>();
+ print("Starting bundles:", verbose);
+ while (!toStart.isEmpty()) {
+ List<Bundle> bs = getBundlesToStart(toStart);
+ for (Bundle bundle : bs) {
+ LOGGER.info(" " + bundle.getSymbolicName() + " / " + bundle.getVersion());
+ try {
+ bundle.start();
+ } catch (BundleException e) {
+ exceptions.add(e);
+ }
+ toStart.remove(bundle);
+ }
+ }
+ if (!exceptions.isEmpty()) {
+ throw new MultiException("Error restarting bundles", exceptions);
+ }
+ }
+
+ print("Done.", verbose);
+ }
+
+ protected BundleInfo mergeBundleInfo(BundleInfo bi, BundleInfo oldBi) {
+ // TODO: we need a proper merge strategy when a bundle
+ // TODO: comes from different features
+ return bi;
+ }
+
+ private void print(String message, boolean verbose) {
+ LOGGER.info(message);
+ if (verbose) {
+ System.out.println(message);
+ }
+ }
+
+ private void removeFragmentsAndBundlesInState(Collection<Bundle> bundles, int state) {
+ for (Bundle bundle : new ArrayList<Bundle>(bundles)) {
+ if ((bundle.getState() & state) != 0
+ || bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) {
+ bundles.remove(bundle);
+ }
+ }
+ }
+
+ protected void logDeployment(Deployment deployment) {
+ LOGGER.info("Changes to perform:");
+ if (!deployment.toDelete.isEmpty()) {
+ LOGGER.info(" Bundles to uninstall:");
+ for (Bundle bundle : deployment.toDelete) {
+ LOGGER.info(" " + bundle.getSymbolicName() + " / " + bundle.getVersion());
+ }
+ }
+ if (!deployment.toUpdate.isEmpty()) {
+ LOGGER.info(" Bundles to update:");
+ for (Map.Entry<Bundle, Resource> entry : deployment.toUpdate.entrySet()) {
+ LOGGER.info(" " + entry.getKey().getSymbolicName() + " / " + entry.getKey().getVersion() + " with " + UriNamespace.getUri(entry.getValue()));
+ }
+ }
+ if (!deployment.toInstall.isEmpty()) {
+ LOGGER.info(" Bundles to install:");
+ for (Resource resource : deployment.toInstall) {
+ LOGGER.info(" " + UriNamespace.getUri(resource));
+ }
+ }
+ }
+
+ protected Deployment computeDeployment(
+ Set<Long> managed,
+ boolean updateSnaphots,
+ Bundle[] bundles,
+ Map<String, StreamProvider> providers,
+ List<Resource> resources,
+ Map<String, Long> bundleChecksums,
+ String bundleUpdateRange) throws IOException {
+ Deployment deployment = new Deployment();
+
+ // TODO: regions
+ List<Resource> toDeploy = new ArrayList<Resource>(resources);
+
+ // First pass: go through all installed bundles and mark them
+ // as either to ignore or delete
+ for (Bundle bundle : bundles) {
+ if (bundle.getSymbolicName() != null && bundle.getBundleId() != 0) {
+ Resource resource = null;
+ for (Resource res : toDeploy) {
+ if (bundle.getSymbolicName().equals(getSymbolicName(res))) {
+ if (bundle.getVersion().equals(getVersion(res))) {
+ resource = res;
+ break;
+ }
+ }
+ }
+ // We found a matching bundle
+ if (resource != null) {
+ // In case of snapshots, check if the snapshot is out of date
+ // and flag it as to update
+ if (updateSnaphots && managed.contains(bundle.getBundleId()) && isUpdateable(resource)) {
+ // if the checksum are different
+ InputStream is = null;
+ try {
+ is = getBundleInputStream(resource, providers);
+ long newCrc = ChecksumUtils.checksum(is);
+ long oldCrc = bundleChecksums.containsKey(bundle.getLocation()) ? bundleChecksums.get(bundle.getLocation()) : 0l;
+ if (newCrc != oldCrc) {
+ LOGGER.debug("New snapshot available for " + bundle.getLocation());
+ deployment.toUpdate.put(bundle, resource);
+ deployment.newCheckums.put(bundle.getLocation(), newCrc);
+ }
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+ // We're done for this resource
+ toDeploy.remove(resource);
+ deployment.resToBnd.put(resource, bundle);
+ // There's no matching resource
+ // If the bundle is managed, we need to delete it
+ } else if (managed.contains(bundle.getBundleId())) {
+ deployment.toDelete.add(bundle);
+ }
+ }
+ }
+
+ // Second pass on remaining resources
+ for (Resource resource : toDeploy) {
+ TreeMap<Version, Bundle> matching = new TreeMap<Version, Bundle>();
+ VersionRange range = new VersionRange(Macro.transform(bundleUpdateRange, getVersion(resource).toString()));
+ for (Bundle bundle : deployment.toDelete) {
+ if (bundle.getSymbolicName().equals(getSymbolicName(resource)) && range.contains(bundle.getVersion())) {
+ matching.put(bundle.getVersion(), bundle);
+ }
+ }
+ if (!matching.isEmpty()) {
+ Bundle bundle = matching.lastEntry().getValue();
+ deployment.toUpdate.put(bundle, resource);
+ deployment.toDelete.remove(bundle);
+ deployment.resToBnd.put(resource, bundle);
+ } else {
+ deployment.toInstall.add(resource);
+ }
+ }
+ return deployment;
+ }
+
+ protected List<Resource> getBundles(Collection<Resource> allResources) {
+ Map<String, Resource> deploy = new TreeMap<String, Resource>();
+ for (Resource res : allResources) {
+ String uri = UriNamespace.getUri(res);
+ if (uri != null) {
+ deploy.put(uri, res);
+ }
+ }
+ return new ArrayList<Resource>(deploy.values());
+ }
+
+ protected List<Feature> getFeatures(Repository[] repositories, List<String> featureIds) throws Exception {
+ List<Feature> installedFeatures = new ArrayList<Feature>();
+ for (Repository repository : repositories) {
+ for (Feature feature : repository.getFeatures()) {
+ String id = feature.getName() + "/" + VersionTable.getVersion(feature.getVersion());
+ if (featureIds.contains(id)) {
+ installedFeatures.add(feature);
+ }
+ }
+ }
+ return installedFeatures;
+ }
+
+ protected List<String> getFeatureIds(Collection<Resource> allResources) {
+ List<String> installedFeatureIds = new ArrayList<String>();
+ for (Resource resource : allResources) {
+ String name = FeatureNamespace.getName(resource);
+ if (name != null) {
+ Version version = FeatureNamespace.getVersion(resource);
+ String id = version != null ? name + "/" + version : name;
+ installedFeatureIds.add(id);
+ }
+ }
+ return installedFeatureIds;
+ }
+
+ protected DeploymentBuilder createDeploymentBuilder(Repository[] repositories) {
+ return new DeploymentBuilder(new SimpleDownloader(), Arrays.asList(repositories));
+ }
+
+
+ protected boolean isUpdateable(Resource resource) {
+ return (getVersion(resource).getQualifier().endsWith(SNAPSHOT) ||
+ UriNamespace.getUri(resource).contains(SNAPSHOT) ||
+ !UriNamespace.getUri(resource).contains(MAVEN));
+ }
+
+ protected List<Bundle> getBundlesToStart(Collection<Bundle> bundles) {
+ // TODO: make this pluggable ?
+ // TODO: honor respectStartLvlDuringFeatureStartup
+
+ // We hit FELIX-2949 if we don't use the correct order as Felix resolver isn't greedy.
+ // In order to minimize that, we make sure we resolve the bundles in the order they
+ // are given back by the resolution, meaning that all root bundles (i.e. those that were
+ // not flagged as dependencies in features) are started before the others. This should
+ // make sure those important bundles are started first and minimize the problem.
+
+ // Restart the features service last, regardless of any other consideration
+ // so that we don't end up with the service trying to do stuff before we're done
+ boolean restart = bundles.remove(bundle);
+
+ List<BundleRevision> revs = new ArrayList<BundleRevision>();
+ for (Bundle bundle : bundles) {
+ revs.add(bundle.adapt(BundleRevision.class));
+ }
+ List<Bundle> sorted = new ArrayList<Bundle>();
+ for (BundleRevision rev : RequirementSort.sort(revs)) {
+ sorted.add(rev.getBundle());
+ }
+ if (restart) {
+ sorted.add(bundle);
+ }
+ return sorted;
+ }
+
+ protected List<Bundle> getBundlesToStop(Collection<Bundle> bundles) {
+ // TODO: make this pluggable ?
+ // TODO: honor respectStartLvlDuringFeatureUninstall
+
+ List<Bundle> bundlesToDestroy = new ArrayList<Bundle>();
+ for (Bundle bundle : bundles) {
+ ServiceReference[] references = bundle.getRegisteredServices();
+ int usage = 0;
+ if (references != null) {
+ for (ServiceReference reference : references) {
+ usage += getServiceUsage(reference, bundles);
+ }
+ }
+ LOGGER.debug("Usage for bundle {} is {}", bundle, usage);
+ if (usage == 0) {
+ bundlesToDestroy.add(bundle);
+ }
+ }
+ if (!bundlesToDestroy.isEmpty()) {
+ Collections.sort(bundlesToDestroy, new Comparator<Bundle>() {
+ public int compare(Bundle b1, Bundle b2) {
+ return (int) (b2.getLastModified() - b1.getLastModified());
+ }
+ });
+ LOGGER.debug("Selected bundles {} for destroy (no services in use)", bundlesToDestroy);
+ } else {
+ ServiceReference ref = null;
+ for (Bundle bundle : bundles) {
+ ServiceReference[] references = bundle.getRegisteredServices();
+ for (ServiceReference reference : references) {
+ if (getServiceUsage(reference, bundles) == 0) {
+ continue;
+ }
+ if (ref == null || reference.compareTo(ref) < 0) {
+ LOGGER.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference);
+ ref = reference;
+ }
+ }
+ }
+ if (ref != null) {
+ bundlesToDestroy.add(ref.getBundle());
+ }
+ LOGGER.debug("Selected bundle {} for destroy (lowest ranking service)", bundlesToDestroy);
+ }
+ return bundlesToDestroy;
+ }
+
+ private static int getServiceUsage(ServiceReference ref, Collection<Bundle> bundles) {
+ Bundle[] usingBundles = ref.getUsingBundles();
+ int nb = 0;
+ if (usingBundles != null) {
+ for (Bundle bundle : usingBundles) {
+ if (bundles.contains(bundle)) {
+ nb++;
+ }
+ }
+ }
+ return nb;
+ }
+
+ protected InputStream getBundleInputStream(Resource resource, Map<String, StreamProvider> providers) throws IOException {
+ String uri = UriNamespace.getUri(resource);
+ if (uri == null) {
+ throw new IllegalStateException("Resource has no uri");
+ }
+ StreamProvider provider = providers.get(uri);
+ if (provider == null) {
+ throw new IllegalStateException("Resource " + uri + " has no StreamProvider");
+ }
+ return provider.open();
+ }
+
+ protected void findBundlesWithOptionalPackagesToRefresh(Set<Bundle> toRefresh) {
+ // First pass: include all bundles contained in these features
+ if (toRefresh.isEmpty()) {
+ return;
+ }
+ Set<Bundle> bundles = new HashSet<Bundle>(Arrays.asList(systemBundleContext.getBundles()));
+ bundles.removeAll(toRefresh);
+ if (bundles.isEmpty()) {
+ return;
+ }
+ // Second pass: for each bundle, check if there is any unresolved optional package that could be resolved
+ for (Bundle bundle : bundles) {
+ BundleRevision rev = bundle.adapt(BundleRevision.class);
+ boolean matches = false;
+ if (rev != null) {
+ for (BundleRequirement req : rev.getDeclaredRequirements(null)) {
+ if (PackageNamespace.PACKAGE_NAMESPACE.equals(req.getNamespace())
+ && PackageNamespace.RESOLUTION_OPTIONAL.equals(req.getDirectives().get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
+ // This requirement is an optional import package
+ for (Bundle provider : toRefresh) {
+ BundleRevision providerRev = provider.adapt(BundleRevision.class);
+ if (providerRev != null) {
+ for (BundleCapability cap : providerRev.getDeclaredCapabilities(null)) {
+ if (req.matches(cap)) {
+ matches = true;
+ break;
+ }
+ }
+ }
+ if (matches) {
+ break;
+ }
+ }
+ }
+ if (matches) {
+ break;
+ }
+ }
+ }
+ if (matches) {
+ toRefresh.add(bundle);
+ }
+ }
+ }
+
+ protected void findBundlesWithFragmentsToRefresh(Set<Bundle> toRefresh) {
+ if (toRefresh.isEmpty()) {
+ return;
+ }
+ Set<Bundle> bundles = new HashSet<Bundle>(Arrays.asList(systemBundleContext.getBundles()));
+ bundles.removeAll(toRefresh);
+ if (bundles.isEmpty()) {
+ return;
+ }
+ for (Bundle bundle : new ArrayList<Bundle>(toRefresh)) {
+ BundleRevision rev = bundle.adapt(BundleRevision.class);
+ if (rev != null) {
+ for (BundleRequirement req : rev.getDeclaredRequirements(null)) {
+ if (BundleRevision.HOST_NAMESPACE.equals(req.getNamespace())) {
+ for (Bundle hostBundle : bundles) {
+ if (!toRefresh.contains(hostBundle)) {
+ BundleRevision hostRev = hostBundle.adapt(BundleRevision.class);
+ if (hostRev != null) {
+ for (BundleCapability cap : hostRev.getDeclaredCapabilities(null)) {
+ if (req.matches(cap)) {
+ toRefresh.add(hostBundle);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected void refreshPackages(Collection<Bundle> bundles) throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ FrameworkWiring fw = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
+ fw.refreshBundles(bundles, new FrameworkListener() {
+ @Override
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.ERROR) {
+ LOGGER.error("Framework error", event.getThrowable());
+ }
+ latch.countDown();
+ }
+ });
+ latch.await();
+ }
+
+
+ static class Deployment {
+ Map<String, Long> newCheckums = new HashMap<String, Long>();
+ Map<Resource, Bundle> resToBnd = new HashMap<Resource, Bundle>();
+ List<Resource> toInstall = new ArrayList<Resource>();
+ List<Bundle> toDelete = new ArrayList<Bundle>();
+ Map<Bundle, Resource> toUpdate = new HashMap<Bundle, Resource>();
+ }
+
+}
[12/13] [KARAF-2888] New FeaturesService based on the real OSGi
resolver
Posted by gn...@apache.org.
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;
+
+}