From 7ddbc243e5ede35461232fbe2762d117dbedf7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 3 May 2024 19:18:01 +0200 Subject: [PATCH] Filter use-constraint violating providers of packages If a provider is chosen for a given requirement this can imply that also other providers are now become invalid, currently the resolver simply checks the whole state and adds new permutations what is rather expensive. This now adds an additional local filtering step that checks for the chosen provider for the requirement and discards any incompatible provider for other packages in the same resource. As a result for a small testcase this saves 3 out of 23 (~ 10%) uses permutations. --- .../META-INF/MANIFEST.MF | 2 +- bundles/org.eclipse.osgi.tests/pom.xml | 2 +- .../tests/container/TestModuleContainer.java | 7 +- bundles/org.eclipse.osgi/META-INF/MANIFEST.MF | 4 +- .../osgi/container/ModuleContainer.java | 28 ++++ .../osgi/container/ModuleResolver.java | 70 ++++++++++ .../org/apache/felix/resolver/Candidates.java | 74 +++++----- .../src/org/apache/felix/resolver/Logger.java | 29 ++++ .../felix/resolver/ProblemReduction.java | 126 ++++++++++++++++++ .../apache/felix/resolver/ResolverImpl.java | 49 ++++++- .../src/org/apache/felix/resolver/Util.java | 40 +++--- bundles/org.eclipse.osgi/pom.xml | 2 +- 12 files changed, 356 insertions(+), 77 deletions(-) create mode 100644 bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ProblemReduction.java diff --git a/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF index 48e1b899f23..14ce523fdbf 100644 --- a/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Core OSGi Tests Bundle-SymbolicName: org.eclipse.osgi.tests;singleton:=true -Bundle-Version: 3.19.100.qualifier +Bundle-Version: 3.19.200.qualifier Bundle-Vendor: Eclipse.org Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)", diff --git a/bundles/org.eclipse.osgi.tests/pom.xml b/bundles/org.eclipse.osgi.tests/pom.xml index d3e9d4bab10..beabd708d5f 100644 --- a/bundles/org.eclipse.osgi.tests/pom.xml +++ b/bundles/org.eclipse.osgi.tests/pom.xml @@ -19,7 +19,7 @@ org.eclipse.osgi org.eclipse.osgi.tests - 3.19.100-SNAPSHOT + 3.19.200-SNAPSHOT eclipse-test-plugin diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java index 4bd75eee64c..22f0c87774d 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java @@ -3959,7 +3959,8 @@ protected void assertNotMoreThanPermutationCreated(ResolutionReport report, fail("Maximum of " + max + " permutations expected but was " + permutations); } else if (permutations < max) { System.out.println( - "## Permutations are below the threshold, consider adjusting the testcase to assert the lower count!"); + "## Permutations (" + permutations + ") are below the threshold (" + max + + "), consider adjusting the testcase to assert the lower count!"); } return; } @@ -4367,8 +4368,8 @@ private static void assertWires(List required, List... p public void testLocalUseConstraintViolations() throws Exception { ResolutionReport result = resolveTestSet("set1"); // TODO get down permutation count! - assertSucessfulWith(result, 52); - assertNotMoreThanPermutationCreated(result, ResolutionReport::getSubstitutionPermutations, 23); + assertSucessfulWith(result, 49); + assertNotMoreThanPermutationCreated(result, ResolutionReport::getSubstitutionPermutations, 20); } private ResolutionReport resolveTestSet(String name) throws Exception { diff --git a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF index d5482329f91..4faf9b4dc46 100644 --- a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.runtime", org.eclipse.core.runtime.internal.adaptor;x-internal:=true, org.eclipse.equinox.log;version="1.1";uses:="org.osgi.framework,org.osgi.service.log", - org.eclipse.osgi.container;version="1.7.0"; + org.eclipse.osgi.container;version="1.8.0"; uses:="org.eclipse.osgi.report.resolution, org.osgi.framework.wiring, org.eclipse.osgi.framework.eventmgr, @@ -107,7 +107,7 @@ Bundle-Activator: org.eclipse.osgi.internal.framework.SystemBundleActivator Bundle-Description: %systemBundle Bundle-Copyright: %copyright Bundle-Vendor: %eclipse.org -Bundle-Version: 3.20.100.qualifier +Bundle-Version: 3.21.0.qualifier Bundle-Localization: systembundle Bundle-DocUrl: http://www.eclipse.org Eclipse-ExtensibleAPI: true diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java index d93eb6d3a06..2386a8b4b13 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java @@ -261,6 +261,34 @@ public static String toString(Capability capability) { return Constants.PROVIDE_CAPABILITY + ": " + capability.toString(); //$NON-NLS-1$ } + /** + * Generates a human readable string representation of the the given + * {@link Resource} using the IDENTITY_NAMESPACE + * + * @param resource the {@link Resource} for which a string representation is + * desired + * + * @since 3.21 + */ + public static String toString(Resource resource) { + String id = null; + Version version = null; + List caps = resource.getCapabilities(null); + for (Capability cap : caps) { + if (cap.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE)) { + id = cap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE).toString(); + version = (Version) cap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE); + } + } + if (id != null) { + if (version != null) { + return String.format("%s %s", id, version); //$NON-NLS-1$ + } + return id; + } + return resource.toString(); + } + private static String createOSGiCapability(Capability cap) { Map attributes = new HashMap<>(cap.getAttributes()); Map directives = cap.getDirectives(); diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java index 6efc6216841..eb1b0dcd4f9 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java @@ -16,6 +16,7 @@ import static org.eclipse.osgi.internal.container.NamespaceList.WIRE; import java.security.Permission; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -37,6 +38,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import org.apache.felix.resolver.Logger; import org.apache.felix.resolver.PermutationType; import org.apache.felix.resolver.ResolutionError; @@ -504,6 +506,74 @@ public void logUsesConstraintViolation(Resource resource, ResolutionError error) } } + public void logRequirement(String message, Requirement requirement) { + debug(String.format(message, ModuleContainer.toString(requirement))); + } + + public void logCapability(String message, Capability requirement) { + debug(String.format(message, ModuleContainer.toString(requirement))); + } + + @Override + public void logCandidates(Resource resource, Function> candidateLookup) { + if (DEBUG_USES) { + Wiring wiring = getWirings().get(resource); + List reqs = (wiring != null) ? wiring.getResourceRequirements(null) + : resource.getRequirements(null); + List dreqs = (wiring != null) + ? getDynamicRequirements(wiring.getResourceRequirements(null)) + : getDynamicRequirements(resource.getRequirements(null)); + boolean hasMulti = hasMulti(reqs, candidateLookup); + Debug.println(String.format(" %s%s (%s)", getMultiMarker(hasMulti), //$NON-NLS-1$ + ModuleContainer.toString(resource), + ((wiring != null) ? "RESOLVED)" : "UNRESOLVED)"))); //$NON-NLS-1$ //$NON-NLS-2$ + printRe(reqs, candidateLookup); + printRe(dreqs, candidateLookup); + } + } + + private String getMultiMarker(boolean hasMulti) { + return hasMulti ? "[?]" : "[!]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + private boolean hasMulti(List reqs, Function> candidateLookup) { + for (Requirement req : reqs) { + List remaining = candidateLookup.apply(req); + if (remaining.size() > 1) { + return true; + } + } + return false; + } + + private int printRe(List reqs, Function> candidateLookup) { + int dup = 0; + for (Requirement req : reqs) { + List remaining = candidateLookup.apply(req); + boolean hasMulti = remaining.size() > 1; + dup++; + Debug.println(MessageFormat.format(" {0}{1}: ", getMultiMarker(hasMulti), //$NON-NLS-1$ + ModuleContainer.toString(req))); + for (Capability cap : remaining) { + Debug.println(String.format(" %s", ModuleContainer.toString(cap))); //$NON-NLS-1$ + } + } + return dup; + } + + private List getDynamicRequirements(List reqs) { + List result = new ArrayList<>(); + if (reqs != null) { + for (Requirement req : reqs) { + String resolution = req.getDirectives().get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE); + if ((resolution != null) && resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC)) { + result.add(req); + } + } + } + return result; + } + Map getUsesConstraintViolations() { return errors == null ? Collections.emptyMap() : errors; } diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java index 0b59f6dac0b..25aab4e6072 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java @@ -18,6 +18,7 @@ */ package org.apache.felix.resolver; +import java.io.PrintStream; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; @@ -34,6 +35,8 @@ class Candidates { + private static final boolean FILTER_USES = Boolean + .parseBoolean(System.getProperty("felix.resolver.candidates.filteruses", "true")); static class PopulateResult { boolean success; ResolutionError error; @@ -709,7 +712,7 @@ public Capability getFirstCandidate(Requirement req) return null; } - public void removeFirstCandidate(Requirement req) + public Capability removeFirstCandidate(Requirement req) { CandidateSelector candidates = m_candidateMap.get(req); // Remove the conflicting candidate. @@ -721,6 +724,7 @@ public void removeFirstCandidate(Requirement req) // Update the delta with the removed capability CopyOnWriteSet capPath = m_delta.getOrCompute(req); capPath.add(cap); + return cap; } public CandidateSelector clearMultipleCardinalityCandidates(Requirement req, Collection caps) @@ -1145,54 +1149,15 @@ public Candidates copy() m_delta.deepClone()); } - public void dump(ResolveContext rc) - { - // Create set of all revisions from requirements. - Set resources = new CopyOnWriteSet(); - for (Entry entry - : m_candidateMap.entrySet()) - { - resources.add(entry.getKey().getResource()); - } - // Now dump the revisions. - System.out.println("=== BEGIN CANDIDATE MAP ==="); - for (Resource resource : resources) - { - Wiring wiring = rc.getWirings().get(resource); - System.out.println(" " + resource - + " (" + ((wiring != null) ? "RESOLVED)" : "UNRESOLVED)")); - List reqs = (wiring != null) - ? wiring.getResourceRequirements(null) - : resource.getRequirements(null); - for (Requirement req : reqs) - { - CandidateSelector candidates = m_candidateMap.get(req); - if ((candidates != null) && (!candidates.isEmpty())) - { - System.out.println(" " + req + ": " + candidates); - } - } - reqs = (wiring != null) - ? Util.getDynamicRequirements(wiring.getResourceRequirements(null)) - : Util.getDynamicRequirements(resource.getRequirements(null)); - for (Requirement req : reqs) - { - CandidateSelector candidates = m_candidateMap.get(req); - if ((candidates != null) && (!candidates.isEmpty())) - { - System.out.println(" " + req + ": " + candidates); - } - } - } - System.out.println("=== END CANDIDATE MAP ==="); - } - public Candidates permutate(Requirement req) { if (!Util.isMultiple(req) && canRemoveCandidate(req)) { Candidates perm = copy(); - perm.removeFirstCandidate(req); + Capability removed = perm.removeFirstCandidate(req); + if (FILTER_USES) { + ProblemReduction.removeUsesViolations(perm, req, m_session.getLogger()); + } return perm; } return null; @@ -1341,4 +1306,25 @@ public ResolutionException toException() { } + /** + * Returns the current provided {@link Capability} for the given resource if it + * is a candidate for the {@link Requirement} + * + * @param resource the resource to check + * @param requirement the requirement to check + * @return the {@link Capability} this Resource currently provides for the given + * {@link Requirement} or null if none is provided. + */ + public Capability getCapability(Resource resource, Requirement requirement) { + List providers = getCandidates(requirement); + if (providers != null) { + for (Capability capability : providers) { + if (capability.getResource().equals(resource)) { + return capability; + } + } + } + return null; + } + } diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Logger.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Logger.java index b82c0d9e424..f4a6eebaed1 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Logger.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Logger.java @@ -18,6 +18,10 @@ */ package org.apache.felix.resolver; +import java.util.List; +import java.util.function.Function; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; import org.osgi.resource.Resource; /** @@ -132,6 +136,31 @@ public void logUsesConstraintViolation(Resource resource, ResolutionError error) // do nothing by default } + /** + * Called to debug the current mapping of {@link Requirement}s to + * {@link Capability}s in a resolve operation + * + * @param resource the resource for this log message + * @param candidateLookup a mapping between a requirement and a list of all + * current candidate {@link Capability}s eligible for + * resolving + */ + public void logCandidates(Resource resource, + Function> candidateLookup) + { + // do nothing by default + } + + public void logRequirement(String message, Requirement requirement) + { + debug(String.format(message, requirement)); + } + + public void logCapability(String message, Capability requirement) + { + debug(String.format(message, requirement)); + } + /** * Called whenever a new permutation is added by the resolver. * diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ProblemReduction.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ProblemReduction.java new file mode 100644 index 00000000000..fea2cab9f0d --- /dev/null +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ProblemReduction.java @@ -0,0 +1,126 @@ +/* + * 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.felix.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import org.osgi.framework.namespace.PackageNamespace; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; +import org.osgi.resource.Resource; + +/** + * The idea of the {@link ProblemReduction} class is to strike out + * {@link Capability}s that might satisfy {@link Requirement}s but violates some + * contracts that would lead to a guaranteed unresolvable state. + */ +class ProblemReduction { + + private static final Capability[] EMPTY_CAPABILITIES = new Capability[0]; + + /** + * Removes all violating providers for a given {@link Requirement} and + * {@link Candidates} in a local search, that is if the requirement has any uses + * it checks if there are other packages used by this one and removes any + * offending providers from the top of the list. + * + * @param candidates candidates to filter + * @param requirement the requirement where the search should start + * @return a list of Candidates that where dropped as part of the filtering + */ + static List removeUsesViolations(Candidates candidates, Requirement requirement, Logger logger) { + Resource targetResource = requirement.getResource(); + // fetch the current candidate for this requirement + Capability currentCandidate = candidates.getFirstCandidate(requirement); + Resource candidateResource = currentCandidate.getResource(); + // now check if it has any uses constraints + Set uses = new TreeSet<>(Util.getUses(currentCandidate)); + if (uses.isEmpty()) { + // there is nothing this one can conflict in this current set of candidates + return Collections.emptyList(); + } + + + if (logger.isDebugEnabled()) { + logger.logRequirement("=== remove uses violations for %s", requirement); + logger.logCapability("== current candidate is %s", currentCandidate); + logger.logCandidates(targetResource, req -> getCapabilityList(candidates, req)); + } + boolean repeat; + int round = 0; + List dropped = new ArrayList<>(); + do { + repeat = false; + round++; + if (logger.isDebugEnabled()) { + logger.debug("Round " + round + ":"); + for (String usedPackage : uses) { + logger.debug(" uses: " + usedPackage); + } + } + // now look at all other imports of the target resource if it is a package that + // is part of a used package + for (Requirement packageRequirement : targetResource.getRequirements(PackageNamespace.PACKAGE_NAMESPACE)) { + if (packageRequirement == requirement) { + continue; + } + Capability providedPackage = candidates.getCapability(candidateResource, packageRequirement); + if (providedPackage == null) { + // we do not provide anything for this package + continue; + } + if (uses.contains(Util.getPackageName(providedPackage))) { + // this is a package where we are a candidate and that has a uses constraint, so + // this package must be provided by us as well or we run into a uses-violation + // later on! + Capability capability = removeViolators(candidates, candidateResource, packageRequirement, dropped); + // if we have added any additional uses we need to reiterate... + repeat |= uses.addAll(Util.getUses(capability)); + } + } + } while (repeat); + if (logger.isDebugEnabled() && !dropped.isEmpty()) { + logger.debug("After removal (" + dropped.size() + " dropped)"); + logger.logCandidates(targetResource, req -> getCapabilityList(candidates, req)); + } + return dropped; + } + + private static Capability removeViolators(Candidates candidates, Resource candidateResource, + Requirement packageRequirement, List dropped) { + Capability capability; + while ((capability = candidates.getFirstCandidate(packageRequirement)).getResource() != candidateResource) { + dropped.add(candidates.copy()); + candidates.removeFirstCandidate(packageRequirement); + } + return capability; + } + + private static List getCapabilityList(Candidates candidates, Requirement requirement) { + List list = candidates.getCandidates(requirement); + if (list == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(list); + } + +} diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java index 77963fbe4d8..0a5d0a4347f 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java @@ -18,19 +18,50 @@ */ package org.apache.felix.resolver; -import java.security.*; -import java.util.*; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.*; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.ToIntFunction; import org.apache.felix.resolver.reason.ReasonException; import org.apache.felix.resolver.util.ArrayMap; import org.apache.felix.resolver.util.CandidateSelector; import org.apache.felix.resolver.util.OpenHashMap; -import org.osgi.framework.namespace.*; -import org.osgi.resource.*; -import org.osgi.service.resolver.*; +import org.osgi.framework.namespace.BundleNamespace; +import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; +import org.osgi.framework.namespace.HostNamespace; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.framework.namespace.PackageNamespace; +import org.osgi.resource.Capability; +import org.osgi.resource.Namespace; +import org.osgi.resource.Requirement; +import org.osgi.resource.Resource; +import org.osgi.resource.Wire; +import org.osgi.resource.Wiring; +import org.osgi.service.resolver.HostedCapability; +import org.osgi.service.resolver.ResolutionException; +import org.osgi.service.resolver.ResolveContext; +import org.osgi.service.resolver.Resolver; public class ResolverImpl implements Resolver { @@ -138,6 +169,10 @@ ConcurrentMap> getUsesCache() { return m_usesCache; } + public Logger getLogger() { + return logger; + } + void permutateIfNeeded(PermutationType type, Requirement req, Candidates permutation) { List candidates = permutation.getCandidates(req); if ((candidates != null) && (candidates.size() > 1)) diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Util.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Util.java index 973cd5b72c6..37b6775ce77 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Util.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Util.java @@ -18,8 +18,11 @@ */ package org.apache.felix.resolver; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import org.osgi.framework.Version; import org.osgi.framework.namespace.BundleNamespace; import org.osgi.framework.namespace.IdentityNamespace; @@ -81,13 +84,13 @@ public static boolean isOptional(Requirement req) public static boolean isMultiple(Requirement req) { - return Namespace.CARDINALITY_MULTIPLE.equals(req.getDirectives() + return Namespace.CARDINALITY_MULTIPLE.equals(req.getDirectives() .get(Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE)) && !isDynamic(req); } public static boolean isDynamic(Requirement req) { - return PackageNamespace.RESOLUTION_DYNAMIC.equals(req.getDirectives() + return PackageNamespace.RESOLUTION_DYNAMIC.equals(req.getDirectives() .get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE)); } @@ -97,22 +100,23 @@ public static boolean isReexport(Requirement req) .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE)); } - public static List getDynamicRequirements(List reqs) - { - List result = new ArrayList(); - if (reqs != null) - { - for (Requirement req : reqs) - { - String resolution = req.getDirectives() - .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE); - if ((resolution != null) - && resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC)) - { - result.add(req); - } + public static String getPackageName(Capability capability) { + if (capability != null && PackageNamespace.PACKAGE_NAMESPACE.equals(capability.getNamespace())) { + Object object = capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE); + if (object instanceof String) { + return (String) object; + } + } + return ""; + } + + public static Set getUses(Capability capability) { + if (capability != null && PackageNamespace.PACKAGE_NAMESPACE.equals(capability.getNamespace())) { + String uses = capability.getDirectives().get(PackageNamespace.CAPABILITY_USES_DIRECTIVE); + if (uses != null && !uses.isEmpty()) { + return Arrays.stream(uses.split(",")).map(String::trim).collect(Collectors.toSet()); } } - return result; + return Collections.emptySet(); } } \ No newline at end of file diff --git a/bundles/org.eclipse.osgi/pom.xml b/bundles/org.eclipse.osgi/pom.xml index 6375514ebd3..d9f4b6e0cf6 100644 --- a/bundles/org.eclipse.osgi/pom.xml +++ b/bundles/org.eclipse.osgi/pom.xml @@ -19,7 +19,7 @@ org.eclipse.osgi org.eclipse.osgi - 3.20.100-SNAPSHOT + 3.21.0-SNAPSHOT eclipse-plugin