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 2015/06/11 08:31:40 UTC
[2/2] karaf git commit: [KARAF-3759] Provide tooling to store a
resolution attempt that failed so that it can be replayed offline for
analysis
[KARAF-3759] Provide tooling to store a resolution attempt that failed so that it can be replayed offline for analysis
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/9770185c
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/9770185c
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/9770185c
Branch: refs/heads/master
Commit: 9770185c85d0d38d78e3e9d0854582ef8529dd3b
Parents: 947d2eb
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Mon Jun 8 10:25:52 2015 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Jun 10 21:13:35 2015 +0200
----------------------------------------------------------------------
.../features/command/InstallFeatureCommand.java | 4 +
features/core/pom.xml | 6 +
.../apache/karaf/features/FeaturesService.java | 2 +
.../internal/region/OfflineResolver.java | 167 +++++++++++++++++++
.../region/SubsystemResolveContext.java | 8 +
.../internal/region/SubsystemResolver.java | 72 +++++++-
.../internal/resolver/ResourceBuilder.java | 83 ++++++++-
.../features/internal/service/Deployer.java | 4 +-
.../internal/service/FeaturesServiceImpl.java | 25 ++-
.../features/internal/util/JsonWriter.java | 15 +-
.../features/internal/region/SubsystemTest.java | 16 +-
11 files changed, 380 insertions(+), 22 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
----------------------------------------------------------------------
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
index 600310b..571a713 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
@@ -52,6 +52,9 @@ public class InstallFeatureCommand extends FeaturesCommandSupport {
@Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only", required = false, multiValued = false)
boolean simulate;
+ @Option(name = "--store", description = "Store the resolution into the given file and result for offline analysis")
+ String outputFile;
+
@Option(name = "-g", aliases = "--region", description = "Region to install to")
String region;
@@ -61,6 +64,7 @@ public class InstallFeatureCommand extends FeaturesCommandSupport {
addOption(FeaturesService.Option.NoAutoRefreshBundles, noRefresh);
addOption(FeaturesService.Option.NoAutoManageBundles, noManage);
addOption(FeaturesService.Option.Verbose, verbose);
+ admin.setResolutionOutputFile(outputFile);
admin.installFeatures(new HashSet<String>(features), region, options);
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/pom.xml
----------------------------------------------------------------------
diff --git a/features/core/pom.xml b/features/core/pom.xml
index d45feb5..767fe12 100644
--- a/features/core/pom.xml
+++ b/features/core/pom.xml
@@ -52,6 +52,11 @@
<dependency>
<groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.resolver</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.utils</artifactId>
<scope>provided</scope>
</dependency>
@@ -135,6 +140,7 @@
org.eclipse.equinox.region.*
</Export-Package>
<Import-Package>
+ !org.apache.felix.resolver,
!org.eclipse.osgi.service.resolver,
*
</Import-Package>
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
index 82d246d..57e7014 100644
--- a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
+++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
@@ -83,6 +83,8 @@ public interface FeaturesService {
String getRepositoryName(URI uri) throws Exception;
+ void setResolutionOutputFile(String outputFile);
+
void installFeature(String name) throws Exception;
void installFeature(String name, EnumSet<Option> options) throws Exception;
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/src/main/java/org/apache/karaf/features/internal/region/OfflineResolver.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/OfflineResolver.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/OfflineResolver.java
new file mode 100644
index 0000000..1c89dc2
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/OfflineResolver.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.region;
+
+import java.io.BufferedReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.resolver.Logger;
+import org.apache.felix.resolver.ResolverImpl;
+import org.apache.karaf.features.internal.repository.BaseRepository;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
+import org.apache.karaf.features.internal.resolver.ResourceBuilder;
+import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.apache.karaf.features.internal.util.JsonReader;
+import org.osgi.framework.BundleException;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.resource.Wiring;
+import org.osgi.service.repository.Repository;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolveContext;
+import org.osgi.service.resolver.Resolver;
+
+import static org.osgi.framework.Constants.RESOLUTION_DIRECTIVE;
+import static org.osgi.framework.Constants.RESOLUTION_OPTIONAL;
+import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
+
+public class OfflineResolver {
+
+ public static void main(String[] args) throws Exception {
+ if (args == null || args.length != 1) {
+ throw new IllegalArgumentException("File path expected");
+ }
+ resolve(args[0]);
+ }
+
+ public static void resolve(String resolutionFile) throws Exception {
+ Map<String, Object> resolution;
+ try (BufferedReader reader = Files.newBufferedReader(Paths.get(resolutionFile), StandardCharsets.UTF_8)) {
+ resolution = (Map<String, Object>) JsonReader.read(reader);
+ }
+
+ final Repository globalRepository;
+ if (resolution.containsKey("globalRepository")) {
+ globalRepository = readRepository(resolution.get("globalRepository"));
+ } else {
+ globalRepository = null;
+ }
+ final Repository repository = readRepository(resolution.get("repository"));
+
+ Resolver resolver = new ResolverImpl(new Logger(Logger.LOG_ERROR));
+ Map<Resource, List<Wire>> wiring = resolver.resolve(new ResolveContext() {
+ private final Set<Resource> mandatory = new HashSet<>();
+ private final CandidateComparator candidateComparator = new CandidateComparator(mandatory);
+
+ @Override
+ public Collection<Resource> getMandatoryResources() {
+ List<Resource> resources = new ArrayList<Resource>();
+ Requirement req = new RequirementImpl(
+ null,
+ IDENTITY_NAMESPACE,
+ Collections.<String, String>emptyMap(),
+ Collections.<String, Object>emptyMap(),
+ SimpleFilter.parse("(" + IDENTITY_NAMESPACE + "=root)"));
+ Collection<Capability> identities = repository.findProviders(Collections.singleton(req)).get(req);
+ for (Capability identity : identities) {
+ resources.add(identity.getResource());
+ }
+ return resources;
+ }
+
+ @Override
+ public List<Capability> findProviders(Requirement requirement) {
+ List<Capability> caps = new ArrayList<>();
+ Map<Requirement, Collection<Capability>> resMap =
+ repository.findProviders(Collections.singleton(requirement));
+ Collection<Capability> res = resMap != null ? resMap.get(requirement) : null;
+ if (res != null && !res.isEmpty()) {
+ caps.addAll(res);
+ } else if (globalRepository != null) {
+ // Only bring in external resources for non optional requirements
+ if (!RESOLUTION_OPTIONAL.equals(requirement.getDirectives().get(RESOLUTION_DIRECTIVE))) {
+ resMap = globalRepository.findProviders(Collections.singleton(requirement));
+ res = resMap != null ? resMap.get(requirement) : null;
+ if (res != null && !res.isEmpty()) {
+ caps.addAll(res);
+ }
+ }
+ }
+
+ // Sort caps
+ Collections.sort(caps, candidateComparator);
+ return caps;
+ }
+
+ @Override
+ public int insertHostedCapability(List<Capability> capabilities, HostedCapability hostedCapability) {
+ int idx = Collections.binarySearch(capabilities, hostedCapability, candidateComparator);
+ if (idx < 0) {
+ idx = Math.abs(idx + 1);
+ }
+ capabilities.add(idx, hostedCapability);
+ return idx;
+ }
+
+ @Override
+ public boolean isEffective(Requirement requirement) {
+ return true;
+ }
+
+ @Override
+ public Map<Resource, Wiring> getWirings() {
+ return Collections.emptyMap();
+ }
+ });
+ }
+
+ private static Repository readRepository(Object repository) throws BundleException {
+ List<Resource> resources = new ArrayList<>();
+ Collection<Map<String, List<String>>> metadatas;
+ if (repository instanceof Map) {
+ metadatas = ((Map<String, Map<String, List<String>>>) repository).values();
+ } else {
+ metadatas = (Collection<Map<String, List<String>>>) repository;
+ }
+ for (Map<String, List<String>> metadata : metadatas) {
+ ResourceImpl res = new ResourceImpl();
+ for (String cap : metadata.get("capabilities")) {
+ res.addCapabilities(ResourceBuilder.parseCapability(res, cap));
+ }
+ if (metadata.containsKey("requirements")) {
+ for (String req : metadata.get("requirements")) {
+ res.addRequirements(ResourceBuilder.parseRequirement(res, req));
+ }
+ }
+ resources.add(res);
+ }
+ return new BaseRepository(resources);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
index fdf9016..de990f4 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolveContext.java
@@ -87,6 +87,14 @@ public class SubsystemResolveContext extends ResolveContext {
findMandatory(root);
}
+ public Repository getRepository() {
+ return repository;
+ }
+
+ public Repository getGlobalRepository() {
+ return globalRepository;
+ }
+
void findMandatory(Resource res) {
if (mandatory.add(res)) {
for (Requirement req : res.getRequirements(null)) {
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
index 0402c12..d3701c7 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
@@ -16,6 +16,12 @@
*/
package org.apache.karaf.features.internal.region;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -30,12 +36,16 @@ import org.apache.karaf.features.Feature;
import org.apache.karaf.features.internal.download.DownloadManager;
import org.apache.karaf.features.internal.download.Downloader;
import org.apache.karaf.features.internal.download.StreamProvider;
+import org.apache.karaf.features.internal.resolver.BaseClause;
import org.apache.karaf.features.internal.resolver.CapabilityImpl;
import org.apache.karaf.features.internal.resolver.CapabilitySet;
+import org.apache.karaf.features.internal.resolver.RequirementImpl;
import org.apache.karaf.features.internal.resolver.ResolverUtil;
import org.apache.karaf.features.internal.resolver.ResourceBuilder;
import org.apache.karaf.features.internal.resolver.ResourceImpl;
+import org.apache.karaf.features.internal.resolver.ResourceUtils;
import org.apache.karaf.features.internal.resolver.SimpleFilter;
+import org.apache.karaf.features.internal.util.JsonWriter;
import org.eclipse.equinox.internal.region.StandardRegionDigraph;
import org.eclipse.equinox.region.Region;
import org.eclipse.equinox.region.RegionDigraph;
@@ -49,6 +59,7 @@ import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
+import org.osgi.service.repository.Repository;
import org.osgi.service.resolver.Resolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -168,8 +179,9 @@ public class SubsystemResolver {
public Map<Resource, List<Wire>> resolve(
Set<String> overrides,
String featureResolutionRange,
- final org.osgi.service.repository.Repository globalRepository
- ) throws Exception {
+ final Repository globalRepository,
+ String outputFile) throws Exception {
+
if (root == null) {
return Collections.emptyMap();
}
@@ -182,7 +194,30 @@ public class SubsystemResolver {
populateDigraph(digraph, root);
Downloader downloader = manager.createDownloader();
- wiring = resolver.resolve(new SubsystemResolveContext(root, digraph, globalRepository, downloader));
+ SubsystemResolveContext context = new SubsystemResolveContext(root, digraph, globalRepository, downloader);
+ if (outputFile != null) {
+ Map<String, Object> json = new HashMap<>();
+ if (globalRepository != null) {
+ json.put("globalRepository", toJson(globalRepository));
+ }
+ json.put("repository", toJson(context.getRepository()));
+ try {
+ wiring = resolver.resolve(context);
+ json.put("success", "true");
+ } catch (Exception e) {
+ json.put("exception", e.toString());
+ throw e;
+ } finally {
+ try (Writer writer = Files.newBufferedWriter(
+ Paths.get(outputFile),
+ StandardCharsets.UTF_8,
+ StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
+ JsonWriter.write(writer, json);
+ }
+ }
+ } else {
+ wiring = resolver.resolve(context);
+ }
downloader.await();
// Remove wiring to the fake environment resource
@@ -203,6 +238,37 @@ public class SubsystemResolver {
return wiring;
}
+ private Object toJson(Repository repository) {
+ Requirement req = new RequirementImpl(
+ null,
+ IDENTITY_NAMESPACE,
+ Collections.<String, String>emptyMap(),
+ Collections.<String, Object>emptyMap(),
+ new SimpleFilter(null, null, SimpleFilter.MATCH_ALL));
+ Collection<Capability> identities = repository.findProviders(Collections.singleton(req)).get(req);
+ List<Object> resources = new ArrayList<>();
+ for (Capability identity : identities) {
+ String id = BaseClause.toString(null, identity.getNamespace(), identity.getAttributes(), identity.getDirectives());
+ resources.add(toJson(identity.getResource()));
+ }
+ return resources;
+ }
+
+ private Object toJson(Resource resource) {
+ Map<String, Object> obj = new HashMap<>();
+ List<Object> caps = new ArrayList<>();
+ List<Object> reqs = new ArrayList<>();
+ for (Capability cap : resource.getCapabilities(null)) {
+ caps.add(BaseClause.toString(null, cap.getNamespace(), cap.getAttributes(), cap.getDirectives()));
+ }
+ for (Requirement req : resource.getRequirements(null)) {
+ reqs.add(BaseClause.toString(null, req.getNamespace(), req.getAttributes(), req.getDirectives()));
+ }
+ obj.put("capabilities", caps);
+ obj.put("requirements", reqs);
+ return obj;
+ }
+
public Map<String, Map<String, BundleInfo>> getBundleInfos() {
if (bundleInfos == null) {
bundleInfos = new HashMap<>();
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/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
index 47a0661..0dbf4e2 100644
--- 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
@@ -455,6 +455,87 @@ public final class ResourceBuilder {
private static List<ParsedHeaderClause> normalizeRequireCapabilityClauses(
List<ParsedHeaderClause> clauses) throws BundleException {
+ // Convert attributes into specified types.
+ for (ParsedHeaderClause clause : clauses) {
+ for (Map.Entry<String, Object> entry : clause.attrs.entrySet()) {
+ if (entry.getKey().equals("version")) {
+ clause.attrs.put(entry.getKey(), new VersionRange(entry.getValue().toString()));
+ }
+ }
+ 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<>(tokens.size());
+ for (String token : tokens) {
+ switch (listType) {
+ case "String":
+ values.add(token);
+ break;
+ case "Double":
+ values.add(new Double(token.trim()));
+ break;
+ case "Version":
+ values.add(new Version(token.trim()));
+ break;
+ case "Long":
+ values.add(new Long(token.trim()));
+ break;
+ default:
+ 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;
}
@@ -550,7 +631,7 @@ public final class ResourceBuilder {
String filterStr = clause.dirs.get(Constants.FILTER_DIRECTIVE);
SimpleFilter sf = (filterStr != null)
? SimpleFilter.parse(filterStr)
- : new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+ : SimpleFilter.convert(clause.attrs);
for (String path : clause.paths) {
// Create requirement and add to requirement list.
reqList.add(new RequirementImpl(
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
index e1d4354..e6bc258 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
@@ -163,6 +163,7 @@ public class Deployer {
public Map<String, Set<String>> requirements;
public Map<String, Map<String, FeatureState>> stateChanges;
public EnumSet<FeaturesService.Option> options;
+ public String outputFile;
}
static class Deployment {
@@ -261,7 +262,8 @@ public class Deployer {
resolver.resolve(
request.overrides,
request.featureResolutionRange,
- request.globalRepository);
+ request.globalRepository,
+ request.outputFile);
Map<String, StreamProvider> providers = resolver.getProviders();
Map<String, Set<Resource>> featuresPerRegion = resolver.getFeaturesPerRegions();
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/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
index b003331..4dcd267 100644
--- 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
@@ -160,6 +160,8 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
private final String blacklisted;
+ private final ThreadLocal<String> outputFile = new ThreadLocal<>();
+
/**
* Optional global repository
*/
@@ -799,6 +801,11 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
@Override
+ public void setResolutionOutputFile(String outputFile) {
+ this.outputFile.set(outputFile);
+ }
+
+ @Override
public void installFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception {
State state = copyState();
Map<String, Set<String>> required = copy(state.requirements);
@@ -954,10 +961,12 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
final EnumSet<Option> options) throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
try {
+ final String outputFile = this.outputFile.get();
+ this.outputFile.set(null);
executor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
- doProvision(requirements, stateChanges, state, options);
+ doProvision(requirements, stateChanges, state, options, outputFile);
return null;
}
}).get();
@@ -1026,7 +1035,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
return dstate;
}
- private Deployer.DeploymentRequest getDeploymentRequest(Map<String, Set<String>> requirements, Map<String, Map<String, FeatureState>> stateChanges, EnumSet<Option> options) {
+ private Deployer.DeploymentRequest getDeploymentRequest(Map<String, Set<String>> requirements, Map<String, Map<String, FeatureState>> stateChanges, EnumSet<Option> options, String outputFile) {
Deployer.DeploymentRequest request = new Deployer.DeploymentRequest();
request.bundleUpdateRange = bundleUpdateRange;
request.featureResolutionRange = featureResolutionRange;
@@ -1036,15 +1045,17 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
request.requirements = requirements;
request.stateChanges = stateChanges;
request.options = options;
+ request.outputFile = outputFile;
return request;
}
- public void doProvision(Map<String, Set<String>> requirements, // all requirements
- Map<String, Map<String, FeatureState>> stateChanges, // features state changes
- State state, // current state
- EnumSet<Option> options // installation options
+ public void doProvision(Map<String, Set<String>> requirements, // all requirements
+ Map<String, Map<String, FeatureState>> stateChanges, // features state changes
+ State state, // current state
+ EnumSet<Option> options, // installation options
+ String outputFile // file to store the resolution or null
) throws Exception {
Dictionary<String, String> props = getMavenConfig();
@@ -1057,7 +1068,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
while (true) {
try {
Deployer.DeploymentState dstate = getDeploymentState(state);
- Deployer.DeploymentRequest request = getDeploymentRequest(requirements, stateChanges, options);
+ Deployer.DeploymentRequest request = getDeploymentRequest(requirements, stateChanges, options, outputFile);
new Deployer(manager, this.resolver, this).deploy(dstate, request);
break;
} catch (Deployer.PartialDeploymentException e) {
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/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
index d65bc35..fb27346 100644
--- 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
@@ -76,14 +76,25 @@ public final class JsonWriter {
char c = value.charAt(i);
switch (c) {
case '\"':
+ writer.append("\\\"");
+ break;
case '\\':
+ writer.append("\\\\");
+ break;
case '\b':
+ writer.append("\\b");
+ break;
case '\f':
+ writer.append("\\f");
+ break;
case '\n':
+ writer.append("\\n");
+ break;
case '\r':
+ writer.append("\\r");
+ break;
case '\t':
- writer.append('\\');
- writer.append(c);
+ writer.append("\\t");
break;
default:
if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
http://git-wip-us.apache.org/repos/asf/karaf/blob/9770185c/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
index 965b5f6..846a2c9 100644
--- a/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java
@@ -67,7 +67,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -98,7 +98,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -119,7 +119,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.singleton("b"),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -139,7 +139,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -161,7 +161,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -183,7 +183,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -204,7 +204,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}
@@ -226,7 +226,7 @@ public class SubsystemTest {
Collections.<String, Set<BundleRevision>>emptyMap());
resolver.resolve(Collections.<String>emptySet(),
FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE,
- null);
+ null, null);
verify(resolver, expected);
}