Repository: karaf Updated Branches: refs/heads/master f40c636b3 -> b8818c0bd
Move more code into FeatureReq Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/b8818c0b Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/b8818c0b Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/b8818c0b Branch: refs/heads/master Commit: b8818c0bd2282a2df0c2cb689b10b939639fd48e Parents: f40c636 Author: Guillaume Nodet <[email protected]> Authored: Mon Aug 28 17:31:19 2017 +0200 Committer: Guillaume Nodet <[email protected]> Committed: Mon Aug 28 17:31:19 2017 +0200 ---------------------------------------------------------------------- .../features/internal/service/Deployer.java | 7 +- .../features/internal/service/FeatureReq.java | 135 ++++++++++++---- .../internal/service/FeaturesServiceImpl.java | 155 ++++--------------- .../karaf/features/internal/util/MapUtils.java | 24 ++- 4 files changed, 164 insertions(+), 157 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/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 5f7b105..f17f1e6 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 @@ -36,6 +36,7 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; +import java.util.function.Function; import org.apache.felix.utils.version.VersionRange; import org.apache.felix.utils.version.VersionTable; @@ -1322,15 +1323,15 @@ public class Deployer { return result; } - protected <T> MapUtils.Function<Bundle, T> adapt(final Class<T> clazz) { + protected <T> Function<Bundle, T> adapt(final Class<T> clazz) { return bundle -> bundle.adapt(clazz); } - protected MapUtils.Function<Bundle, Long> bundleId() { + protected Function<Bundle, Long> bundleId() { return Bundle::getBundleId; } - protected MapUtils.Function<Resource, String> featureId() { + protected Function<Resource, String> featureId() { return ResourceUtils::getFeatureId; } http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java index 1bac816..a573642 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureReq.java @@ -16,10 +16,24 @@ */ package org.apache.karaf.features.internal.service; +import org.apache.felix.utils.version.VersionTable; import org.apache.karaf.features.Feature; import org.osgi.framework.Version; import org.osgi.framework.VersionRange; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.karaf.features.internal.util.MapUtils.filter; + /** * Requirement for a feature * @@ -33,12 +47,27 @@ import org.osgi.framework.VersionRange; * - range: Like defined in OSGi VersionRange. Example: [1.0.0, 1.1.0) */ public class FeatureReq { + public static final String VERSION_SEPARATOR = "/"; - private static Version HIGHEST = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); - private static final VersionRange RANGE_ALL = new VersionRange(VersionRange.LEFT_CLOSED, Version.emptyVersion, HIGHEST, VersionRange.RIGHT_CLOSED); + + private static final VersionRange RANGE_ALL = new VersionRange(VersionRange.LEFT_CLOSED, Version.emptyVersion, null, VersionRange.RIGHT_OPEN); + private static final String FEATURE_OSGI_REQUIREMENT_PREFIX = "feature:"; + private String name; private VersionRange versionRange; - + + public static FeatureReq parseRequirement(String featureReq) { + if (!featureReq.startsWith(FEATURE_OSGI_REQUIREMENT_PREFIX)) { + return null; + } + String featureReq1 = featureReq.substring(FEATURE_OSGI_REQUIREMENT_PREFIX.length()); + return parseNameAndRange(featureReq1); + } + + public static FeatureReq parseNameAndRange(String nameAndRange) { + return new FeatureReq(nameAndRange); + } + public FeatureReq(String nameAndRange) { String[] parts = nameAndRange.trim().split(VERSION_SEPARATOR); this.name = parts[0]; @@ -50,28 +79,6 @@ public class FeatureReq { this.versionRange = range(versionRange); } - private VersionRange range(String versionRange) { - if (versionRange == null) { - return RANGE_ALL; - } - versionRange = versionRange.trim(); - if ("0.0.0".equals(versionRange)) { - return RANGE_ALL; - } - if (versionRange.contains(",")) { - return new VersionRange(versionRange); - } else { - return exactVersion(versionRange); - } - } - - private static VersionRange exactVersion(String versionRange) { - return new VersionRange(VersionRange.LEFT_CLOSED, - new Version(versionRange), - new Version(versionRange), - VersionRange.RIGHT_CLOSED); - } - public FeatureReq(String name, VersionRange versionRange) { this.name = name; this.versionRange = versionRange; @@ -88,9 +95,85 @@ public class FeatureReq { public VersionRange getVersionRange() { return versionRange; } - + + public Set<FeatureReq> getMatchingRequirements(Set<FeatureReq> reqs) { + Pattern pattern = Pattern.compile(name); + // TODO: should we use the intersection of the 2 ranges ? + return filter(reqs, fr -> pattern.matcher(fr.getName()).matches() + && versionRange.includes(fr.getVersionRange().getLeft())); + } + + public Stream<Feature> getMatchingFeatures(Map<String, Map<String, Feature>> allFeatures) { + Pattern pattern = Pattern.compile(name); + Function<String, Optional<Feature>> func = featureName -> { + Feature matchingFeature = null; + if (pattern.matcher(featureName).matches()) { + Map<String, Feature> versions = allFeatures.get(featureName); + matchingFeature = getLatestFeature(versions, versionRange); + } + return Optional.ofNullable(matchingFeature); + }; + return allFeatures.keySet().stream().map(func).filter(Optional::isPresent).map(Optional::get); + } + + private static Feature getLatestFeature(Map<String, Feature> versions, VersionRange versionRange) { + Feature feature = null; + if (versions != null && !versions.isEmpty()) { + Version latest = Version.emptyVersion; + for (String available : versions.keySet()) { + Version availableVersion = VersionTable.getVersion(available); + if (availableVersion.compareTo(latest) >= 0 && versionRange.includes(availableVersion)) { + feature = versions.get(available); + latest = availableVersion; + } + } + } + return feature; + } + @Override public String toString() { return this.name + "/" + this.getVersionRange().toString(); } + + public String toRequirement() { + return FEATURE_OSGI_REQUIREMENT_PREFIX + toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FeatureReq that = (FeatureReq) o; + return Objects.equals(name, that.name) && + Objects.equals(versionRange, that.versionRange); + } + + @Override + public int hashCode() { + return Objects.hash(name, versionRange); + } + + private static VersionRange range(String versionRange) { + if (versionRange == null) { + return RANGE_ALL; + } + versionRange = versionRange.trim(); + if ("0.0.0".equals(versionRange)) { + return RANGE_ALL; + } + if (versionRange.contains(",")) { + return new VersionRange(versionRange); + } else { + return exactVersion(versionRange); + } + } + + private static VersionRange exactVersion(String versionRange) { + return new VersionRange(VersionRange.LEFT_CLOSED, + new Version(versionRange), + new Version(versionRange), + VersionRange.RIGHT_CLOSED); + } + } http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/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 574998a..05177fb 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 @@ -43,13 +43,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.function.Function; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.felix.utils.version.VersionCleaner; -import org.apache.felix.utils.version.VersionTable; import org.apache.karaf.features.DeploymentEvent; import org.apache.karaf.features.DeploymentListener; import org.apache.karaf.features.Feature; @@ -73,8 +70,6 @@ import org.ops4j.pax.url.mvn.MavenResolvers; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.Version; -import org.osgi.framework.VersionRange; import org.osgi.resource.Resource; import org.osgi.resource.Wire; import org.osgi.service.cm.Configuration; @@ -86,9 +81,7 @@ import org.slf4j.LoggerFactory; import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toSet; import static org.apache.karaf.features.internal.service.StateStorage.toStringStringSetMap; -import static org.apache.karaf.features.internal.util.MapUtils.add; -import static org.apache.karaf.features.internal.util.MapUtils.copy; -import static org.apache.karaf.features.internal.util.MapUtils.remove; +import static org.apache.karaf.features.internal.util.MapUtils.*; /** * @@ -97,7 +90,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall private static final String RESOLVE_FILE = "resolve"; private static final Logger LOGGER = LoggerFactory.getLogger(FeaturesServiceImpl.class); - private static final String FEATURE_OSGI_REQUIREMENT_PREFIX = "feature:"; private static final String VERSION_SEPARATOR = "/"; /** @@ -432,7 +424,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall return Stream.of(repo.getFeatures()) .filter(this::isRequired) .map(Feature::getId) - .collect(Collectors.toSet()); + .collect(toSet()); } } @@ -520,60 +512,17 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall @Override public Feature[] getFeatures(String nameOrId) throws Exception { - return toArray(getFeatures(new FeatureReq(nameOrId))); + return getFeatures(FeatureReq.parseNameAndRange(nameOrId)); } @Override public Feature[] getFeatures(String name, String version) throws Exception { - return toArray(getFeatures(new FeatureReq(name, version))); + return getFeatures(new FeatureReq(name, version)); } - private Collection<Feature> getFeatures(FeatureReq featureReq) throws Exception { - List<Feature> features = new ArrayList<>(); - Pattern pattern = Pattern.compile(featureReq.getName()); + private Feature[] getFeatures(FeatureReq featureReq) throws Exception { Map<String, Map<String, Feature>> allFeatures = getFeatureCache(); - for (String featureName : allFeatures.keySet()) { - Matcher matcher = pattern.matcher(featureName); - if (matcher.matches()) { - Feature matchingFeature = getFeatureMatching(featureName, featureReq.getVersionRange()); - if (matchingFeature != null) { - features.add(matchingFeature); - } - } - } - return features; - } - - private Feature[] toArray(Collection<Feature> features) { - return features.toArray(new Feature[features.size()]); - } - - private Feature getFeatureMatching(String featureName, VersionRange version) throws Exception { - Map<String, Map<String, Feature>> allFeatures = getFeatureCache(); - Map<String, Feature> versions = allFeatures.get(featureName); - if (versions == null || versions.isEmpty()) { - return null; - } - return getLatestFeature(versions, version); - } - - private Feature getLatestFeature(Map<String, Feature> versions, VersionRange versionRange) { - Version latest = Version.emptyVersion; - Feature feature = null; - for (String available : versions.keySet()) { - Version availableVersion = VersionTable.getVersion(available); - if (availableVersion.compareTo(latest) >= 0 && versionRange.includes(availableVersion)) { - feature = versions.get(available); - latest = availableVersion; - } - } - return feature; - } - - @Override - public Feature[] listFeatures() throws Exception { - Map<String, Map<String, Feature>> allFeatures = getFeatureCache(); - return flattenFeatures(allFeatures); + return featureReq.getMatchingFeatures(allFeatures).toArray(Feature[]::new); } private void ensureCacheLoaded() throws Exception { @@ -654,6 +603,12 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall // @Override + public Feature[] listFeatures() throws Exception { + Map<String, Map<String, Feature>> allFeatures = getFeatureCache(); + return flattenFeatures(allFeatures, f -> true); + } + + @Override public Feature[] listInstalledFeatures() throws Exception { Map<String, Map<String, Feature>> allFeatures = getFeatureCache(); synchronized (lock) { @@ -669,10 +624,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall } } - private Feature[] flattenFeatures(Map<String, Map<String, Feature>> features) { - return flattenFeatures(features, f -> true /* include all */); - } - private Feature[] flattenFeatures(Map<String, Map<String, Feature>> features, Predicate<Feature> pred) { return features.values().stream() .map(Map::values) @@ -706,7 +657,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall @Override public boolean isRequired(Feature f) { - String id = FEATURE_OSGI_REQUIREMENT_PREFIX + new FeatureReq(f).toString(); + String id = new FeatureReq(f).toRequirement(); synchronized (lock) { Set<String> features = state.requirements.get(ROOT_REGION); return features != null && features.contains(id); @@ -793,25 +744,22 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall @Override public void installFeatures(Set<String> featuresIn, String region, EnumSet<Option> options) throws Exception { - Set<FeatureReq> toInstall = new HashSet<>(); - for (String feature : featuresIn) { - toInstall.add(new FeatureReq(feature)); - } + Set<FeatureReq> toInstall = map(featuresIn, FeatureReq::parseNameAndRange); State state = copyState(); Map<String, Set<String>> requires = copy(state.requirements); if (region == null || region.isEmpty()) { region = ROOT_REGION; } Set<String> requirements = requires.computeIfAbsent(region, k -> new HashSet<>()); - Set<FeatureReq> existingFeatures = requirements.stream().map(r -> toFeatureReq(r)).collect(toSet()); + Set<FeatureReq> existingFeatures = map(requirements, FeatureReq::parseRequirement); Set<FeatureReq> toAdd = computeFeaturesToAdd(options, toInstall); - toAdd.stream().forEach(f -> requirements.add(toRequirement(f))); + toAdd.forEach(f -> requirements.add(f.toRequirement())); print("Adding features: " + join(toAdd), options.contains(Option.Verbose)); if (options.contains(Option.Upgrade)) { Set<FeatureReq> toRemove = computeFeaturesToRemoveOnUpdate(toAdd, existingFeatures); - toRemove.stream().forEach(f -> requirements.remove(toRequirement(f))); + toRemove.forEach(f -> requirements.remove(f.toRequirement())); if (!toRemove.isEmpty()) { print("Removing features: " + join(toRemove), options.contains(Option.Verbose)); } @@ -822,17 +770,14 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall private Set<FeatureReq> computeFeaturesToAdd(EnumSet<Option> options, Set<FeatureReq> toInstall) throws Exception { + Map<String, Map<String, Feature>> allFeatures = getFeatureCache(); Feature[] installedFeatures = listInstalledFeatures(); Set<FeatureReq> toAdd = new HashSet<>(); for (FeatureReq featureReq : toInstall) { - Collection<Feature> matching = getFeatures(featureReq); - for (Feature f: matching) { + Collection<Feature> matching = featureReq.getMatchingFeatures(allFeatures).collect(toSet()); + for (Feature f : matching) { toAdd.add(new FeatureReq(f)); - for (Feature installedFeature : installedFeatures) { - if (isSameFeature(f, installedFeature)) { - logInstalledOrUpdated(f); - } - } + Arrays.stream(installedFeatures).filter(fi -> isSameFeature(f, fi)).forEach(this::logInstalledOrUpdated); } if (matching.isEmpty() && !options.contains(Option.NoFailOnFeatureNotFound)) { throw new IllegalArgumentException("No matching features for " + featureReq); @@ -851,67 +796,37 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall } private Set<FeatureReq> computeFeaturesToRemoveOnUpdate(Set<FeatureReq> featuresToAdd, - Set<FeatureReq> existingFeatures) throws Exception { - Set<String> namesToAdd = featuresToAdd.stream().map(f -> f.getName()).collect(toSet()); - return existingFeatures.stream() - .filter(f -> namesToAdd.contains(f.getName()) && !featuresToAdd.contains(f)) - .collect(toSet()); - } - - private String toRequirement(FeatureReq feature) { - return FEATURE_OSGI_REQUIREMENT_PREFIX + feature.toString(); + Set<FeatureReq> existingFeatures) throws Exception { + Set<String> namedToAdd = map(featuresToAdd, FeatureReq::getName); + return filter(existingFeatures, f -> namedToAdd.contains(f.getName()) && !featuresToAdd.contains(f)); } @Override public void uninstallFeatures(Set<String> featuresIn, String region, EnumSet<Option> options) throws Exception { - Set<FeatureReq> featureReqs = new HashSet<>(); - for (String feature : featuresIn) { - featureReqs.add(new FeatureReq(feature)); - } + Set<FeatureReq> featureReqs = map(featuresIn, FeatureReq::parseNameAndRange); State state = copyState(); Map<String, Set<String>> required = copy(state.requirements); if (region == null || region.isEmpty()) { region = ROOT_REGION; } Set<String> requirements = required.computeIfAbsent(region, k -> new HashSet<>()); - Set<FeatureReq> existingFeatures = requirements.stream().map(r -> toFeatureReq(r)).collect(toSet()); + Set<FeatureReq> existingFeatures = map(requirements, FeatureReq::parseRequirement); Set<FeatureReq> featuresToRemove = new HashSet<>(); - for (FeatureReq feature : featureReqs) { - List<FeatureReq> toRemove = getMatching(existingFeatures, feature); + for (FeatureReq featureReq : featureReqs) { + Collection<FeatureReq> toRemove = featureReq.getMatchingRequirements(existingFeatures); if (toRemove.isEmpty()) { - throw new IllegalArgumentException("Feature named '" + feature + "' is not installed"); + throw new IllegalArgumentException("Feature named '" + featureReq + "' is not installed"); } featuresToRemove.addAll(toRemove); } print("Removing features: " + join(featuresToRemove), options.contains(Option.Verbose)); - featuresToRemove.stream().forEach(f -> requirements.remove(toRequirement(f))); + featuresToRemove.forEach(f -> requirements.remove(f.toRequirement())); if (requirements.isEmpty()) { required.remove(region); } doProvisionInThread(required, emptyMap(), state, getFeaturesById(), options); } - private List<FeatureReq> getMatching(Set<FeatureReq> existingFeatures, FeatureReq feature) { - Pattern pattern = Pattern.compile(feature.getName()); - List<FeatureReq> matching = new ArrayList<>(); - for (FeatureReq existingFeatureReq : existingFeatures) { - Matcher matcher = pattern.matcher(existingFeatureReq.getName()); - Version existingVersion = existingFeatureReq.getVersionRange().getLeft(); - if (matcher.matches() && feature.getVersionRange().includes(existingVersion)) { - matching.add(existingFeatureReq); - } - } - return matching; - } - - private FeatureReq toFeatureReq(String featureReq) { - if (!featureReq.startsWith(FEATURE_OSGI_REQUIREMENT_PREFIX)) { - return null; - } - String featureReq1 = featureReq.substring(FEATURE_OSGI_REQUIREMENT_PREFIX.length()); - return new FeatureReq(featureReq1); - } - @Override public void updateFeaturesState(Map<String, Map<String, FeatureState>> stateChanges, EnumSet<Option> options) throws Exception { State state = copyState(); @@ -939,7 +854,7 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall State stateCopy; synchronized (lock) { // Remove repo - Set<String> reps = repos.stream().map(URI::toString).collect(Collectors.toSet()); + Set<String> reps = map(repos, URI::toString); Set<String> toRemove = diff(state.repositories, reps); Set<String> toAdd = diff(reps, state.repositories); state.repositories.removeAll(toRemove); @@ -957,12 +872,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall doProvisionInThread(requirements, emptyMap(), stateCopy, getFeaturesById(), options); } - private <T> Set<T> diff(Set<T> s1, Set<T> s2) { - Set<T> s = new HashSet<>(s1); - s.removeAll(s2); - return s; - } - @Override public Repository createRepository(URI uri) throws Exception { return repositories.create(uri, true); @@ -1194,6 +1103,6 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall } private String join(Collection<FeatureReq> reqs) { - return reqs.stream().map(f->f.toString()).collect(Collectors.joining(",")); + return reqs.stream().map(FeatureReq::toString).collect(Collectors.joining(",")); } } http://git-wip-us.apache.org/repos/asf/karaf/blob/b8818c0b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java index 32607e4..92128f0 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java @@ -24,16 +24,16 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; + +import static java.util.stream.Collectors.toSet; public final class MapUtils { private MapUtils() { } - public interface Function<T, U> { - U apply(T t); - } - public static <S, T> Map<S, Set<T>> invert(Map<T, S> map) { Map<S, Set<T>> inverted = new HashMap<>(map.size()); for (Map.Entry<T, S> entry : map.entrySet()) { @@ -77,7 +77,7 @@ public final class MapUtils { return s -> f2.apply(f1.apply(s)); } - public static <T, U> MapUtils.Function<T, U> map(final Map<T, U> map) { + public static <T, U> Function<T, U> map(final Map<T, U> map) { return map::get; } @@ -199,4 +199,18 @@ public final class MapUtils { } } + public static <T> Set<T> diff(Set<T> s1, Set<T> s2) { + Set<T> s = new HashSet<>(s1); + s.removeAll(s2); + return s; + } + + public static <S, T> Set<T> map(Set<S> s, Function<S, T> mapper) { + return s.stream().map(mapper).collect(toSet()); + } + + public static <S> Set<S> filter(Set<S> s, Predicate<S> predicate) { + return s.stream().filter(predicate).collect(toSet()); + } + }
