From a7941db61a7405ab9524b9b749731a5ad1825ca4 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Tue, 30 Jan 2024 14:53:12 +0100 Subject: [PATCH] Small Resolver refactoring for PDE API tooling stability - changed Maps/Sets to "Linked" versions where iteration was done - changed resolveBundles() to resolve fragments only if asked to - this avoids the problem that fragments are resolved before host bundles - added some debug logging for unresolved fragment hosts See https://github.com/eclipse-pde/eclipse.pde/issues/1073 --- .../osgi/internal/module/ResolverImpl.java | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java index 693bd6f27b2..7eee1335a7d 100644 --- a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java +++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java @@ -26,6 +26,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -100,9 +102,9 @@ public class ResolverImpl implements Resolver { // Repository for generics private Map> resolverGenerics = null; // List of unresolved bundles - private HashSet unresolvedBundles = null; + private Set unresolvedBundles; // Keys are BundleDescriptions, values are ResolverBundles - private HashMap bundleMapping = null; + private Map bundleMapping; private GroupingChecker groupingChecker; private Comparator selectionPolicy; private boolean developmentMode = false; @@ -123,9 +125,9 @@ PermissionChecker getPermissionChecker() { private void initialize() { resolverExports = new VersionHashMap<>(this); resolverBundles = new VersionHashMap<>(this); - resolverGenerics = new HashMap<>(); - unresolvedBundles = new HashSet<>(); - bundleMapping = new HashMap<>(); + resolverGenerics = new LinkedHashMap<>(); + unresolvedBundles = new LinkedHashSet<>(); + bundleMapping = new LinkedHashMap<>(); BundleDescription[] bundles = state.getBundles(); groupingChecker = new GroupingChecker(); @@ -512,11 +514,12 @@ public synchronized void resolve(BundleDescription[] reRefresh, Dictionary optionalResolved = resolveOptional ? resolveOptionalConstraints(currentlyResolved) : Collections.EMPTY_LIST; ResolverHook current = hook; @@ -591,7 +594,7 @@ private BundleDescription[] addDevConstraints(BundleDescription[] reRefresh) { return reRefresh; // we don't care about this unless we are in development mode // when in develoment mode we need to reRefresh hosts of unresolved fragments that add new constraints // and reRefresh and unresolved bundles that have dependents - Set additionalRefresh = new HashSet<>(); + Set additionalRefresh = new LinkedHashSet<>(); ResolverBundle[] allUnresolved = unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]); for (ResolverBundle unresolved : allUnresolved) { addUnresolvedWithDependents(unresolved, additionalRefresh); @@ -673,7 +676,7 @@ private void getCurrentEEs(Dictionary[] platformProperties) { } } - private void resolveBundles(ResolverBundle[] bundles, Dictionary[] platformProperties, Collection hookDisabled) { + private void resolveBundles(ResolverBundle[] bundles, Dictionary[] platformProperties, Collection hookDisabled, boolean resolveFragments) { // First check that all the meta-data is valid for each unresolved bundle // This will reset the resolvable flag for each bundle @@ -681,10 +684,11 @@ private void resolveBundles(ResolverBundle[] bundles, Dictionary state.removeResolverErrors(bundle.getBundleDescription()); // if in development mode then make all bundles resolvable // we still want to call isResolvable here to populate any possible ResolverErrors for the bundle - bundle.setResolvable(isResolvable(bundle, platformProperties, hookDisabled) || developmentMode); + boolean resolvable = isResolvable(bundle, platformProperties, hookDisabled); + bundle.setResolvable(resolvable || developmentMode); } selectSingletons(bundles); - resolveBundles0(bundles, platformProperties); + resolveBundles0(bundles, platformProperties, resolveFragments); if (DEBUG_WIRING) printWirings(); } @@ -774,7 +778,7 @@ private ResolverBundle pickOneToResolve(Collection pickOneToReso } private Map> getCollisionMap(List sameBSN) { - Map> result = new HashMap<>(); + Map> result = new LinkedHashMap<>(); for (ResolverBundle singleton : sameBSN) { if (!singleton.getBundleDescription().isSingleton() || !singleton.isResolvable()) continue; // ignore non-singleton and non-resolvable @@ -798,7 +802,7 @@ private BundleCapability getIdentity(ResolverBundle bundle) { return identities.size() == 1 ? identities.get(0) : bundle.getCapability(); } - private void resolveBundles0(ResolverBundle[] bundles, Dictionary[] platformProperties) { + private void resolveBundles0(ResolverBundle[] bundles, Dictionary[] platformProperties, boolean resolveFragments) { if (developmentMode) // need to sort bundles to keep consistent order for fragment attachment (bug 174930) Arrays.sort(bundles); @@ -822,7 +826,7 @@ private void resolveBundles0(ResolverBundle[] bundles, Dictionary 0) { + if (resolveFragments && unresolvedBundles.size() > 0) { ResolverBundle[] unresolved = unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]); for (ResolverBundle toResolve : unresolved) { resolveFragment(toResolve); @@ -899,7 +903,7 @@ private void reResolveBundles(Set exclude, ResolverBundle[] bund remainingUnresolved.add(bundle); } } - resolveBundles0(remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties); + resolveBundles0(remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties, true); } private List findBestCombination(ResolverBundle[] bundles, Dictionary[] platformProperties) { @@ -1230,13 +1234,13 @@ private ResolverConstraint[][] getMultipleSuppliers(ResolverBundle[] bundles, Se if (multipleImportSupplierList.size() + multipleRequireSupplierList.size() + multipleGenericSupplierList.size() > usesMultipleSuppliersLimit) { // we have hit a max on the multiple suppliers in the lists without merging. // first merge the identical constraints that have identical suppliers - Map>> multipleImportSupplierMaps = new HashMap<>(); + Map>> multipleImportSupplierMaps = new LinkedHashMap<>(); for (ResolverImport importPkg : multipleImportSupplierList) addMutipleSupplierConstraint(multipleImportSupplierMaps, importPkg, importPkg.getName()); - Map>> multipleRequireSupplierMaps = new HashMap<>(); + Map>> multipleRequireSupplierMaps = new LinkedHashMap<>(); for (BundleConstraint requireBundle : multipleRequireSupplierList) addMutipleSupplierConstraint(multipleRequireSupplierMaps, requireBundle, requireBundle.getName()); - Map>> multipleGenericSupplierMaps = new HashMap<>(); + Map>> multipleGenericSupplierMaps = new LinkedHashMap<>(); for (GenericConstraint genericRequire : multipleGenericSupplierList) addMutipleSupplierConstraint(multipleGenericSupplierMaps, genericRequire, genericRequire.getNameSpace()); addMergedSuppliers(results, multipleImportSupplierMaps); @@ -1380,11 +1384,26 @@ static Collection asCapabilities(Collection 0) - if (!developmentMode || state.getResolverErrors(fragment.getBundleDescription()).length == 0) + } + BundleConstraint hostConstraint = fragment.getHost(); + if (DEBUG){ + ResolverBundle host = null; + if (hostConstraint != null) { + String hostName = hostConstraint.getName(); + host = unresolvedBundles.stream().filter(b -> hostName.equals(b.getName())).findFirst().orElse(null); + if (host != null && host.getState() != ResolverBundle.RESOLVED) { + ResolverImpl.log("Host " + host + " is not resolved, but fragment " + fragment + " is!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + } + + if (hostConstraint.getNumPossibleSuppliers() > 0) { + if (!developmentMode || state.getResolverErrors(fragment.getBundleDescription()).length == 0) { setBundleResolved(fragment); + } + } } // This method will attempt to resolve the supplied bundle and any bundles that it is dependent on