Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java')
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java2270
1 files changed, 2270 insertions, 0 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
new file mode 100644
index 000000000..551abb015
--- /dev/null
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -0,0 +1,2270 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2012 IBM Corporation and others. 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:
+ * IBM Corporation - initial API and implementation
+ * Danail Nachev - ProSyst - bug 218625
+ * Rob Harrop - SpringSource Inc. (bug 247522)
+ ******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import java.security.AccessController;
+import java.util.*;
+import org.eclipse.osgi.framework.util.ArrayMap;
+import org.eclipse.osgi.framework.util.SecureAction;
+import org.eclipse.osgi.internal.debug.Debug;
+import org.eclipse.osgi.internal.debug.FrameworkDebugOptions;
+import org.eclipse.osgi.internal.framework.EquinoxContainer;
+import org.eclipse.osgi.internal.framework.FilterImpl;
+import org.eclipse.osgi.internal.module.GroupingChecker.PackageRoots;
+import org.eclipse.osgi.internal.resolver.*;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.*;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ResolverImpl implements Resolver {
+ // Debug fields
+ private static final String RESOLVER = EquinoxContainer.NAME + "/resolver"; //$NON-NLS-1$
+ private static final String OPTION_DEBUG = RESOLVER + "/debug";//$NON-NLS-1$
+ private static final String OPTION_WIRING = RESOLVER + "/wiring"; //$NON-NLS-1$
+ private static final String OPTION_IMPORTS = RESOLVER + "/imports"; //$NON-NLS-1$
+ private static final String OPTION_REQUIRES = RESOLVER + "/requires"; //$NON-NLS-1$
+ private static final String OPTION_GENERICS = RESOLVER + "/generics"; //$NON-NLS-1$
+ private static final String OPTION_USES = RESOLVER + "/uses"; //$NON-NLS-1$
+ private static final String OPTION_CYCLES = RESOLVER + "/cycles"; //$NON-NLS-1$
+ public static boolean DEBUG = false;
+ public static boolean DEBUG_WIRING = false;
+ public static boolean DEBUG_IMPORTS = false;
+ public static boolean DEBUG_REQUIRES = false;
+ public static boolean DEBUG_GENERICS = false;
+ public static boolean DEBUG_USES = false;
+ public static boolean DEBUG_CYCLES = false;
+ private static int MAX_MULTIPLE_SUPPLIERS_MERGE = 10;
+ private static int MAX_USES_TIME_BASE = 30000; // 30 seconds
+ private static int MAX_USES_TIME_LIMIT = 90000; // 90 seconds
+ private static final String USES_TIMEOUT_PROP = "osgi.usesTimeout"; //$NON-NLS-1$
+ private static final String MULTIPLE_SUPPLIERS_LIMIT_PROP = "osgi.usesLimit"; //$NON-NLS-1$
+ static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
+
+ private String[][] CURRENT_EES;
+ private ResolverHook hook;
+
+ // The State associated with this resolver
+ private State state;
+ // Used to check permissions for import/export, provide/require, host/fragment
+ private final PermissionChecker permissionChecker;
+ // Set of bundles that are pending removal
+ private MappedList<Long, BundleDescription> removalPending = new MappedList<Long, BundleDescription>();
+ // Indicates whether this resolver has been initialized
+ private boolean initialized = false;
+
+ // Repository for exports
+ private VersionHashMap<ResolverExport> resolverExports = null;
+ // Repository for bundles
+ private VersionHashMap<ResolverBundle> resolverBundles = null;
+ // Repository for generics
+ private Map<String, VersionHashMap<GenericCapability>> resolverGenerics = null;
+ // List of unresolved bundles
+ private HashSet<ResolverBundle> unresolvedBundles = null;
+ // Keys are BundleDescriptions, values are ResolverBundles
+ private HashMap<BundleDescription, ResolverBundle> bundleMapping = null;
+ private GroupingChecker groupingChecker;
+ private Comparator<BaseDescription> selectionPolicy;
+ private boolean developmentMode = false;
+ private boolean usesCalculationTimeout = false;
+ private long usesTimeout = -1;
+ private int usesMultipleSuppliersLimit;
+ private volatile CompositeResolveHelperRegistry compositeHelpers;
+
+ public ResolverImpl(boolean checkPermissions) {
+ this.permissionChecker = new PermissionChecker(checkPermissions, this);
+ }
+
+ PermissionChecker getPermissionChecker() {
+ return permissionChecker;
+ }
+
+ // Initializes the resolver
+ private void initialize() {
+ resolverExports = new VersionHashMap<ResolverExport>(this);
+ resolverBundles = new VersionHashMap<ResolverBundle>(this);
+ resolverGenerics = new HashMap<String, VersionHashMap<GenericCapability>>();
+ unresolvedBundles = new HashSet<ResolverBundle>();
+ bundleMapping = new HashMap<BundleDescription, ResolverBundle>();
+ BundleDescription[] bundles = state.getBundles();
+ groupingChecker = new GroupingChecker();
+
+ ArrayList<ResolverBundle> fragmentBundles = new ArrayList<ResolverBundle>();
+ // Add each bundle to the resolver's internal state
+ for (int i = 0; i < bundles.length; i++)
+ initResolverBundle(bundles[i], fragmentBundles, false);
+ // Add each removal pending bundle to the resolver's internal state
+ List<BundleDescription> removedBundles = removalPending.getAllValues();
+ for (BundleDescription removed : removedBundles)
+ initResolverBundle(removed, fragmentBundles, true);
+ // Iterate over the resolved fragments and attach them to their hosts
+ for (Iterator<ResolverBundle> iter = fragmentBundles.iterator(); iter.hasNext();) {
+ ResolverBundle fragment = iter.next();
+ BundleDescription[] hosts = ((HostSpecification) fragment.getHost().getVersionConstraint()).getHosts();
+ for (int i = 0; i < hosts.length; i++) {
+ ResolverBundle host = bundleMapping.get(hosts[i]);
+ if (host != null)
+ // Do not add fragment exports here because they would have been added by the host above.
+ host.attachFragment(fragment, false);
+ }
+ }
+ rewireBundles(); // Reconstruct wirings
+ setDebugOptions();
+ initialized = true;
+ }
+
+ private void initResolverBundle(BundleDescription bundleDesc, ArrayList<ResolverBundle> fragmentBundles, boolean pending) {
+ ResolverBundle bundle = new ResolverBundle(bundleDesc, this);
+ bundleMapping.put(bundleDesc, bundle);
+ if (!pending || bundleDesc.isResolved()) {
+ resolverExports.put(bundle.getExportPackages());
+ resolverBundles.put(bundle.getName(), bundle);
+ addGenerics(bundle.getGenericCapabilities());
+ }
+ if (bundleDesc.isResolved()) {
+ bundle.setState(ResolverBundle.RESOLVED);
+ if (bundleDesc.getHost() != null)
+ fragmentBundles.add(bundle);
+ } else {
+ if (!pending)
+ unresolvedBundles.add(bundle);
+ }
+ }
+
+ // Re-wire previously resolved bundles
+ private void rewireBundles() {
+ List<ResolverBundle> visited = new ArrayList<ResolverBundle>(bundleMapping.size());
+ for (ResolverBundle rb : bundleMapping.values()) {
+ if (!rb.getBundleDescription().isResolved())
+ continue;
+ rewireBundle(rb, visited);
+ }
+ }
+
+ private void rewireBundle(ResolverBundle rb, List<ResolverBundle> visited) {
+ if (visited.contains(rb))
+ return;
+ visited.add(rb);
+ // Wire requires to bundles
+ BundleConstraint[] requires = rb.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ rewireRequire(requires[i], visited);
+ }
+ // Wire imports to exports
+ ResolverImport[] imports = rb.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ rewireImport(imports[i], visited);
+ }
+ // Wire generics
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++)
+ rewireGeneric(genericRequires[i], visited);
+ }
+
+ private void rewireGeneric(GenericConstraint constraint, List<ResolverBundle> visited) {
+ if (constraint.getSelectedSupplier() != null)
+ return;
+ GenericDescription[] suppliers = ((GenericSpecification) constraint.getVersionConstraint()).getSuppliers();
+ if (suppliers == null)
+ return;
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(constraint.getNameSpace());
+ if (namespace == null) {
+ System.err.println("Could not find matching capability for " + constraint.getVersionConstraint()); //$NON-NLS-1$
+ // TODO log error!!
+ return;
+ }
+ String constraintName = constraint.getName();
+ List<GenericCapability> matches = constraintName == null ? namespace.get(constraintName) : namespace.getAllValues();
+ for (GenericCapability match : matches) {
+ for (GenericDescription supplier : suppliers)
+ if (match.getBaseDescription() == supplier)
+ constraint.addPossibleSupplier(match);
+ }
+ VersionSupplier[] matchingCapabilities = constraint.getPossibleSuppliers();
+ if (matchingCapabilities != null)
+ for (int i = 0; i < matchingCapabilities.length; i++)
+ rewireBundle(matchingCapabilities[i].getResolverBundle(), visited);
+ }
+
+ private void rewireRequire(BundleConstraint req, List<ResolverBundle> visited) {
+ if (req.getSelectedSupplier() != null)
+ return;
+ ResolverBundle matchingBundle = bundleMapping.get(req.getVersionConstraint().getSupplier());
+ req.addPossibleSupplier(matchingBundle);
+ if (matchingBundle == null && !req.isOptional()) {
+ System.err.println("Could not find matching bundle for " + req.getVersionConstraint()); //$NON-NLS-1$
+ // TODO log error!!
+ }
+ if (matchingBundle != null) {
+ rewireBundle(matchingBundle, visited);
+ }
+ }
+
+ private void rewireImport(ResolverImport imp, List<ResolverBundle> visited) {
+ if (imp.isDynamic() || imp.getSelectedSupplier() != null)
+ return;
+ // Re-wire 'imp'
+ ResolverExport matchingExport = null;
+ ExportPackageDescription importSupplier = (ExportPackageDescription) imp.getVersionConstraint().getSupplier();
+ ResolverBundle exporter = importSupplier == null ? null : (ResolverBundle) bundleMapping.get(importSupplier.getExporter());
+ List<ResolverExport> matches = resolverExports.get(imp.getName());
+ for (ResolverExport export : matches) {
+ if (export.getExporter() == exporter && importSupplier == export.getExportPackageDescription()) {
+ matchingExport = export;
+ break;
+ }
+ }
+ imp.addPossibleSupplier(matchingExport);
+ // If we still have a null wire and it's not optional, then we have an error
+ if (imp.getSelectedSupplier() == null && !imp.isOptional()) {
+ System.err.println("Could not find matching export for " + imp.getVersionConstraint()); //$NON-NLS-1$
+ // TODO log error!!
+ }
+ if (imp.getSelectedSupplier() != null) {
+ rewireBundle(((ResolverExport) imp.getSelectedSupplier()).getExporter(), visited);
+ }
+ }
+
+ // Checks a bundle to make sure it is valid. If this method returns false for
+ // a given bundle, then that bundle will not even be considered for resolution
+ private boolean isResolvable(ResolverBundle bundle, Dictionary<Object, Object>[] platformProperties, Collection<ResolverBundle> hookDisabled) {
+ BundleDescription bundleDesc = bundle.getBundleDescription();
+
+ // check if the bundle is a hook disabled bundle
+ if (hookDisabled.contains(bundle)) {
+ state.addResolverError(bundleDesc, ResolverError.DISABLED_BUNDLE, "Resolver hook disabled bundle.", null); //$NON-NLS-1$
+ return false;
+ }
+ // check to see if the bundle is disabled
+ DisabledInfo[] disabledInfos = state.getDisabledInfos(bundleDesc);
+ if (disabledInfos.length > 0) {
+ StringBuffer message = new StringBuffer();
+ for (int i = 0; i < disabledInfos.length; i++) {
+ if (i > 0)
+ message.append(' ');
+ message.append('\"').append(disabledInfos[i].getPolicyName()).append(':').append(disabledInfos[i].getMessage()).append('\"');
+ }
+ state.addResolverError(bundleDesc, ResolverError.DISABLED_BUNDLE, message.toString(), null);
+ return false; // fail because we are disable
+ }
+
+ // check the required execution environment
+ String[] ees = bundleDesc.getExecutionEnvironments();
+ boolean matchedEE = ees.length == 0;
+ if (!matchedEE)
+ for (int i = 0; i < ees.length && !matchedEE; i++)
+ for (int j = 0; j < CURRENT_EES.length && !matchedEE; j++)
+ for (int k = 0; k < CURRENT_EES[j].length && !matchedEE; k++)
+ if (CURRENT_EES[j][k].equals(ees[i])) {
+ ((BundleDescriptionImpl) bundleDesc).setEquinoxEE(j);
+ matchedEE = true;
+ }
+ if (!matchedEE) {
+ StringBuffer bundleEE = new StringBuffer(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT.length() + 20);
+ bundleEE.append(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT).append(": "); //$NON-NLS-1$
+ for (int i = 0; i < ees.length; i++) {
+ if (i > 0)
+ bundleEE.append(","); //$NON-NLS-1$
+ bundleEE.append(ees[i]);
+ }
+ state.addResolverError(bundleDesc, ResolverError.MISSING_EXECUTION_ENVIRONMENT, bundleEE.toString(), null);
+ return false;
+ }
+
+ // check the native code specification
+ NativeCodeSpecification nativeCode = bundleDesc.getNativeCodeSpecification();
+ if (nativeCode != null) {
+ NativeCodeDescription[] nativeCodeSuppliers = nativeCode.getPossibleSuppliers();
+ NativeCodeDescription highestRanked = null;
+ for (int i = 0; i < nativeCodeSuppliers.length; i++)
+ if (nativeCode.isSatisfiedBy(nativeCodeSuppliers[i]) && (highestRanked == null || highestRanked.compareTo(nativeCodeSuppliers[i]) < 0))
+ highestRanked = nativeCodeSuppliers[i];
+ if (highestRanked == null) {
+ if (!nativeCode.isOptional()) {
+ state.addResolverError(bundleDesc, ResolverError.NO_NATIVECODE_MATCH, nativeCode.toString(), nativeCode);
+ return false;
+ }
+ } else {
+ if (highestRanked.hasInvalidNativePaths()) {
+ state.addResolverError(bundleDesc, ResolverError.INVALID_NATIVECODE_PATHS, highestRanked.toString(), nativeCode);
+ return false;
+ }
+ }
+ state.resolveConstraint(nativeCode, highestRanked);
+ }
+
+ // check the platform filter
+ String platformFilter = bundleDesc.getPlatformFilter();
+ if (platformFilter == null)
+ return true;
+ if (platformProperties == null)
+ return false;
+ try {
+ Filter filter = FilterImpl.newInstance(platformFilter);
+ for (int i = 0; i < platformProperties.length; i++) {
+ // using matchCase here in case of duplicate case invarient keys (bug 180817)
+ @SuppressWarnings("rawtypes")
+ Dictionary props = platformProperties[i];
+ if (filter.matchCase(props))
+ return true;
+ }
+ } catch (InvalidSyntaxException e) {
+ // return false below
+ }
+ state.addResolverError(bundleDesc, ResolverError.PLATFORM_FILTER, platformFilter, null);
+ return false;
+ }
+
+ // Attach fragment to its host
+ private void attachFragment(ResolverBundle bundle, Collection<String> processedFragments) {
+ if (processedFragments.contains(bundle.getName()))
+ return;
+ processedFragments.add(bundle.getName());
+ // we want to attach multiple versions of the same fragment
+ // from highest version to lowest to give the higher versions first pick
+ // of the available host bundles.
+ List<ResolverBundle> fragments = resolverBundles.get(bundle.getName());
+ for (ResolverBundle fragment : fragments) {
+ if (!fragment.isResolved())
+ attachFragment0(fragment);
+ }
+ }
+
+ private void attachFragment0(ResolverBundle bundle) {
+ if (!bundle.isFragment() || !bundle.isResolvable())
+ return;
+ bundle.clearWires();
+ if (!resolveOSGiEE(bundle))
+ return;
+ // no need to select singletons now; it will be done when we select the rest of the singleton bundles (bug 152042)
+ // find all available hosts to attach to.
+ boolean foundMatch = false;
+ BundleConstraint hostConstraint = bundle.getHost();
+ long timestamp;
+ List<ResolverBundle> candidates;
+ do {
+ timestamp = state.getTimeStamp();
+ List<ResolverBundle> hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
+ candidates = new ArrayList<ResolverBundle>(hosts);
+ List<BundleCapability> hostCapabilities = new ArrayList<BundleCapability>(hosts.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<ResolverBundle> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ ResolverBundle host = iCandidates.next();
+ if (!host.isResolvable() || !host.getBundleDescription().attachFragments() || !hostConstraint.isSatisfiedBy(host)) {
+ iCandidates.remove();
+ } else {
+ List<BundleCapability> h = host.getBundleDescription().getDeclaredCapabilities(BundleRevision.HOST_NAMESPACE);
+ // the bundle must have 1 host capability.
+ hostCapabilities.add(h.get(0));
+ }
+ }
+
+ if (hook != null)
+ hook.filterMatches(hostConstraint.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, ResolverBundle>(hostCapabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ // we are left with only candidates that satisfy the host constraint
+ for (ResolverBundle host : candidates) {
+ foundMatch = true;
+ host.attachFragment(bundle, true);
+ }
+ if (!foundMatch)
+ state.addResolverError(bundle.getBundleDescription(), ResolverError.MISSING_FRAGMENT_HOST, bundle.getHost().getVersionConstraint().toString(), bundle.getHost().getVersionConstraint());
+
+ }
+
+ private boolean resolveOSGiEE(ResolverBundle bundle) {
+ GenericConstraint[] requirements = bundle.getGenericRequires();
+ for (GenericConstraint requirement : requirements) {
+ if (!(StateImpl.OSGI_EE_NAMESPACE.equals(requirement.getNameSpace()) || requirement.isEffective()))
+ continue;
+ {
+ if (!resolveGenericReq(requirement, new ArrayList<ResolverBundle>(0))) {
+ if (DEBUG || DEBUG_GENERICS)
+ ResolverImpl.log("** GENERICS " + requirement.getVersionConstraint().getName() + "[" + requirement.getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ state.addResolverError(requirement.getVersionConstraint().getBundle(), ResolverError.MISSING_GENERIC_CAPABILITY, requirement.getVersionConstraint().toString(), requirement.getVersionConstraint());
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ return false;
+ }
+ } else {
+ VersionSupplier supplier = requirement.getSelectedSupplier();
+ Integer ee = supplier == null ? null : (Integer) ((GenericDescription) supplier.getBaseDescription()).getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (ee != null && ((BundleDescriptionImpl) bundle.getBaseDescription()).getEquinoxEE() < 0)
+ ((BundleDescriptionImpl) bundle.getBundleDescription()).setEquinoxEE(ee);
+ }
+ }
+ }
+ return true;
+ }
+
+ public synchronized void resolve(BundleDescription[] reRefresh, Dictionary<Object, Object>[] platformProperties) {
+ if (DEBUG)
+ ResolverImpl.log("*** BEGIN RESOLUTION ***"); //$NON-NLS-1$
+ if (state == null)
+ throw new IllegalStateException("RESOLVER_NO_STATE"); //$NON-NLS-1$
+
+ if (!initialized)
+ initialize();
+ hook = (state instanceof StateImpl) ? ((StateImpl) state).getResolverHook() : null;
+ try {
+ // set developmentMode each resolution
+ developmentMode = platformProperties.length == 0 ? false : StateImpl.DEVELOPMENT_MODE.equals(platformProperties[0].get(StateImpl.OSGI_RESOLVER_MODE));
+ // set uses timeout each resolution
+ usesTimeout = getUsesTimeout(platformProperties);
+ // set limit for constraints with multiple suppliers each resolution
+ usesMultipleSuppliersLimit = getMultipleSuppliersLimit(platformProperties);
+ reRefresh = addDevConstraints(reRefresh);
+ // Unresolve all the supplied bundles and their dependents
+ if (reRefresh != null)
+ for (int i = 0; i < reRefresh.length; i++) {
+ ResolverBundle rb = bundleMapping.get(reRefresh[i]);
+ if (rb != null)
+ unresolveBundle(rb, false);
+ }
+ // reorder exports and bundles after unresolving the bundles
+ resolverExports.reorder();
+ resolverBundles.reorder();
+ reorderGenerics();
+ // always get the latest EEs
+ getCurrentEEs(platformProperties);
+ boolean resolveOptional = platformProperties.length == 0 ? false : "true".equals(platformProperties[0].get("osgi.resolveOptional")); //$NON-NLS-1$//$NON-NLS-2$
+ ResolverBundle[] currentlyResolved = null;
+ if (resolveOptional) {
+ BundleDescription[] resolvedBundles = state.getResolvedBundles();
+ currentlyResolved = new ResolverBundle[resolvedBundles.length];
+ for (int i = 0; i < resolvedBundles.length; i++)
+ currentlyResolved[i] = bundleMapping.get(resolvedBundles[i]);
+ }
+ // attempt to resolve all unresolved bundles
+ @SuppressWarnings("unchecked")
+ Collection<ResolverBundle> hookDisabled = Collections.EMPTY_LIST;
+ if (hook != null) {
+ List<ResolverBundle> resolvableBundles = new ArrayList<ResolverBundle>(unresolvedBundles);
+ List<BundleRevision> resolvableRevisions = new ArrayList<BundleRevision>(resolvableBundles.size());
+ for (ResolverBundle bundle : resolvableBundles)
+ resolvableRevisions.add(bundle.getBundleDescription());
+ ArrayMap<BundleRevision, ResolverBundle> resolvable = new ArrayMap<BundleRevision, ResolverBundle>(resolvableRevisions, resolvableBundles);
+ int size = resolvableBundles.size();
+ hook.filterResolvable(resolvable);
+ if (resolvable.size() < size) {
+ hookDisabled = new ArrayList<ResolverBundle>(unresolvedBundles);
+ hookDisabled.removeAll(resolvableBundles);
+ }
+ }
+
+ usesCalculationTimeout = false;
+
+ List<ResolverBundle> toResolve = new ArrayList<ResolverBundle>(unresolvedBundles);
+ // first resolve the system bundle to allow osgi.ee capabilities to be resolved
+ List<ResolverBundle> unresolvedSystemBundles = new ArrayList<ResolverBundle>(1);
+ String systemBSN = getSystemBundle();
+ for (Iterator<ResolverBundle> iToResolve = toResolve.iterator(); iToResolve.hasNext();) {
+ ResolverBundle rb = iToResolve.next();
+ String symbolicName = rb.getName();
+ if (symbolicName != null && symbolicName.equals(systemBSN)) {
+ unresolvedSystemBundles.add(rb);
+ iToResolve.remove();
+ }
+ }
+ if (!unresolvedSystemBundles.isEmpty())
+ resolveBundles(unresolvedSystemBundles.toArray(new ResolverBundle[unresolvedSystemBundles.size()]), platformProperties, hookDisabled);
+
+ // Now resolve the rest
+ resolveBundles(toResolve.toArray(new ResolverBundle[toResolve.size()]), platformProperties, hookDisabled);
+
+ @SuppressWarnings("unchecked")
+ Collection<ResolverBundle> optionalResolved = resolveOptional ? resolveOptionalConstraints(currentlyResolved) : Collections.EMPTY_LIST;
+ ResolverHook current = hook;
+ if (current != null) {
+ hook = null;
+ current.end();
+ }
+
+ // set the resolved status of the bundles in the State
+ // Note this must be done after calling end above in case end throws errors
+ stateResolveBundles(bundleMapping.values().toArray(new ResolverBundle[bundleMapping.size()]));
+
+ for (ResolverBundle bundle : optionalResolved) {
+ state.resolveBundle(bundle.getBundleDescription(), false, null, null, null, null, null, null, null, null);
+ stateResolveBundle(bundle);
+ }
+ // reorder exports and bundles after resolving the bundles
+ resolverExports.reorder();
+ resolverBundles.reorder();
+ reorderGenerics();
+ if (resolveOptional)
+ resolveOptionalConstraints(currentlyResolved);
+ if (DEBUG)
+ ResolverImpl.log("*** END RESOLUTION ***"); //$NON-NLS-1$
+ } finally {
+ if (hook != null)
+ hook.end(); // need to make sure end is always called
+ hook = null;
+ }
+ }
+
+ private long getUsesTimeout(Dictionary<Object, Object>[] platformProperties) {
+ try {
+ Object timeout = platformProperties.length == 0 ? null : platformProperties[0].get(USES_TIMEOUT_PROP);
+ if (timeout != null) {
+ long temp = Long.parseLong(timeout.toString());
+ if (temp < 0) {
+ return -1;
+ } else if (temp == 0) {
+ return Long.MAX_VALUE;
+ } else {
+ return temp;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // nothing;
+ }
+ return -1;
+ }
+
+ private int getMultipleSuppliersLimit(Dictionary<Object, Object>[] platformProperties) {
+ try {
+ Object limit = platformProperties.length == 0 ? null : platformProperties[0].get(MULTIPLE_SUPPLIERS_LIMIT_PROP);
+ if (limit != null) {
+ int temp = Integer.parseInt(limit.toString());
+ if (temp < 0) {
+ return MAX_MULTIPLE_SUPPLIERS_MERGE;
+ } else if (temp == 0) {
+ return Integer.MAX_VALUE;
+ } else {
+ return temp;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // nothing;
+ }
+ return MAX_MULTIPLE_SUPPLIERS_MERGE;
+ }
+
+ private BundleDescription[] addDevConstraints(BundleDescription[] reRefresh) {
+ if (!developmentMode)
+ 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<BundleDescription> additionalRefresh = new HashSet<BundleDescription>();
+ ResolverBundle[] unresolved = unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
+ for (int i = 0; i < unresolved.length; i++) {
+ addUnresolvedWithDependents(unresolved[i], additionalRefresh);
+ addHostsFromFragmentConstraints(unresolved[i], additionalRefresh);
+ }
+ if (additionalRefresh.size() == 0)
+ return reRefresh; // no new bundles found to refresh
+ // add the original reRefresh bundles to the set
+ if (reRefresh != null)
+ for (int i = 0; i < reRefresh.length; i++)
+ additionalRefresh.add(reRefresh[i]);
+ return additionalRefresh.toArray(new BundleDescription[additionalRefresh.size()]);
+ }
+
+ private void addUnresolvedWithDependents(ResolverBundle unresolved, Set<BundleDescription> additionalRefresh) {
+ BundleDescription[] dependents = unresolved.getBundleDescription().getDependents();
+ if (dependents.length > 0)
+ additionalRefresh.add(unresolved.getBundleDescription());
+ }
+
+ private void addHostsFromFragmentConstraints(ResolverBundle unresolved, Set<BundleDescription> additionalRefresh) {
+ if (!unresolved.isFragment())
+ return;
+ ImportPackageSpecification[] newImports = unresolved.getBundleDescription().getImportPackages();
+ BundleSpecification[] newRequires = unresolved.getBundleDescription().getRequiredBundles();
+ if (newImports.length == 0 && newRequires.length == 0)
+ return; // the fragment does not have its own constraints
+ BundleConstraint hostConstraint = unresolved.getHost();
+ List<ResolverBundle> hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
+ for (ResolverBundle host : hosts)
+ if (hostConstraint.isSatisfiedBy(host) && host.isResolved())
+ // we found a host that is resolved;
+ // add it to the set of bundle to refresh so we can ensure this fragment is allowed to resolve
+ additionalRefresh.add(host.getBundleDescription());
+
+ }
+
+ private Collection<ResolverBundle> resolveOptionalConstraints(ResolverBundle[] bundles) {
+ Collection<ResolverBundle> result = new ArrayList<ResolverBundle>();
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundles[i] != null && resolveOptionalConstraints(bundles[i])) {
+ result.add(bundles[i]);
+ }
+ }
+ return result;
+ }
+
+ // TODO this does not do proper uses constraint verification.
+ private boolean resolveOptionalConstraints(ResolverBundle bundle) {
+ BundleConstraint[] requires = bundle.getRequires();
+ List<ResolverBundle> cycle = new ArrayList<ResolverBundle>();
+ boolean resolvedOptional = false;
+ for (int i = 0; i < requires.length; i++)
+ if (requires[i].isOptional() && requires[i].getSelectedSupplier() == null) {
+ cycle.clear();
+ resolveRequire(requires[i], cycle);
+ if (requires[i].getSelectedSupplier() != null)
+ resolvedOptional = true;
+ }
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++)
+ if (imports[i].isOptional() && imports[i].getSelectedSupplier() == null) {
+ cycle.clear();
+ resolveImport(imports[i], cycle);
+ if (imports[i].getSelectedSupplier() != null)
+ resolvedOptional = true;
+ }
+ return resolvedOptional;
+ }
+
+ private void getCurrentEEs(Dictionary<Object, Object>[] platformProperties) {
+ CURRENT_EES = new String[platformProperties.length][];
+ for (int i = 0; i < platformProperties.length; i++) {
+ String eeSpecs = (String) platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
+ CURRENT_EES[i] = ManifestElement.getArrayFromList(eeSpecs, ","); //$NON-NLS-1$
+ }
+ }
+
+ private void resolveBundles(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties, Collection<ResolverBundle> hookDisabled) {
+
+ // First check that all the meta-data is valid for each unresolved bundle
+ // This will reset the resolvable flag for each bundle
+ for (ResolverBundle bundle : bundles) {
+ 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);
+ }
+ selectSingletons(bundles);
+ resolveBundles0(bundles, platformProperties);
+ if (DEBUG_WIRING)
+ printWirings();
+ }
+
+ private void selectSingletons(ResolverBundle[] bundles) {
+ if (developmentMode)
+ return; // want all singletons to resolve in devmode
+ Map<String, Collection<ResolverBundle>> selectedSingletons = new HashMap<String, Collection<ResolverBundle>>(bundles.length);
+ for (ResolverBundle bundle : bundles) {
+ if (!bundle.getBundleDescription().isSingleton() || !bundle.isResolvable())
+ continue;
+ String bsn = bundle.getName();
+ Collection<ResolverBundle> selected = selectedSingletons.get(bsn);
+ if (selected != null)
+ continue; // already processed the bsn
+ selected = new ArrayList<ResolverBundle>(1);
+ selectedSingletons.put(bsn, selected);
+
+ List<ResolverBundle> sameBSN = resolverBundles.get(bsn);
+ if (sameBSN.size() < 2) {
+ selected.add(bundle);
+ continue;
+ }
+ // prime selected with resolved singleton bundles
+ for (ResolverBundle singleton : sameBSN) {
+ if (singleton.getBundleDescription().isSingleton() && singleton.getBundleDescription().isResolved())
+ selected.add(singleton);
+ }
+ // get the collision map for the BSN
+ Map<ResolverBundle, Collection<ResolverBundle>> collisionMap = getCollisionMap(sameBSN);
+ // process the collision map
+ for (ResolverBundle singleton : sameBSN) {
+ if (selected.contains(singleton))
+ continue; // no need to process resolved bundles
+ Collection<ResolverBundle> collisions = collisionMap.get(singleton);
+ if (collisions == null || !singleton.isResolvable())
+ continue; // not a singleton or not resolvable
+ Collection<ResolverBundle> pickOneToResolve = new ArrayList<ResolverBundle>();
+ for (ResolverBundle collision : collisions) {
+ if (selected.contains(collision)) {
+ // Must fail since there is already a selected bundle which is a collision of the singleton bundle
+ singleton.setResolvable(false);
+ state.addResolverError(singleton.getBundleDescription(), ResolverError.SINGLETON_SELECTION, collision.getBundleDescription().toString(), null);
+ break;
+ }
+ if (!pickOneToResolve.contains(collision))
+ pickOneToResolve.add(collision);
+ }
+ // need to make sure the bundle does not collide from the POV of another entry
+ for (Map.Entry<ResolverBundle, Collection<ResolverBundle>> collisionEntry : collisionMap.entrySet()) {
+ if (collisionEntry.getKey() != singleton && collisionEntry.getValue().contains(singleton)) {
+ if (selected.contains(collisionEntry.getKey())) {
+ // Must fail since there is already a selected bundle for which the singleton bundle is a collision
+ singleton.setResolvable(false);
+ state.addResolverError(singleton.getBundleDescription(), ResolverError.SINGLETON_SELECTION, collisionEntry.getKey().getBundleDescription().toString(), null);
+ break;
+ }
+ if (!pickOneToResolve.contains(collisionEntry.getKey()))
+ pickOneToResolve.add(collisionEntry.getKey());
+ }
+ }
+ if (singleton.isResolvable()) {
+ pickOneToResolve.add(singleton);
+ selected.add(pickOneToResolve(pickOneToResolve));
+ }
+ }
+ }
+ }
+
+ private ResolverBundle pickOneToResolve(Collection<ResolverBundle> pickOneToResolve) {
+ ResolverBundle selectedVersion = null;
+ for (ResolverBundle singleton : pickOneToResolve) {
+ if (selectedVersion == null)
+ selectedVersion = singleton;
+ boolean higherVersion = selectionPolicy != null ? selectionPolicy.compare(selectedVersion.getBundleDescription(), singleton.getBundleDescription()) > 0 : selectedVersion.getVersion().compareTo(singleton.getVersion()) < 0;
+ if (higherVersion)
+ selectedVersion = singleton;
+ }
+
+ for (ResolverBundle singleton : pickOneToResolve) {
+ if (singleton != selectedVersion) {
+ singleton.setResolvable(false);
+ state.addResolverError(singleton.getBundleDescription(), ResolverError.SINGLETON_SELECTION, selectedVersion.getBundleDescription().toString(), null);
+ }
+ }
+ return selectedVersion;
+ }
+
+ private Map<ResolverBundle, Collection<ResolverBundle>> getCollisionMap(List<ResolverBundle> sameBSN) {
+ Map<ResolverBundle, Collection<ResolverBundle>> result = new HashMap<ResolverBundle, Collection<ResolverBundle>>();
+ for (ResolverBundle singleton : sameBSN) {
+ if (!singleton.getBundleDescription().isSingleton() || !singleton.isResolvable())
+ continue; // ignore non-singleton and non-resolvable
+ List<ResolverBundle> collisionCandidates = new ArrayList<ResolverBundle>(sameBSN.size() - 1);
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(sameBSN.size() - 1);
+ for (ResolverBundle collision : sameBSN) {
+ if (collision == singleton || !collision.getBundleDescription().isSingleton() || !collision.isResolvable())
+ continue; // Ignore the bundle we are checking and non-singletons and non-resolvable
+ collisionCandidates.add(collision);
+ capabilities.add(getIdentity(collision));
+ }
+ if (hook != null)
+ hook.filterSingletonCollisions(getIdentity(singleton), asCapabilities(new ArrayMap<BundleCapability, ResolverBundle>(capabilities, collisionCandidates)));
+ result.put(singleton, collisionCandidates);
+ }
+ return result;
+ }
+
+ private BundleCapability getIdentity(ResolverBundle bundle) {
+ List<BundleCapability> identities = bundle.getBundleDescription().getDeclaredCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+ return identities.size() == 1 ? identities.get(0) : bundle.getCapability();
+ }
+
+ private void resolveBundles0(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ if (developmentMode)
+ // need to sort bundles to keep consistent order for fragment attachment (bug 174930)
+ Arrays.sort(bundles);
+ // First attach all fragments to the matching hosts
+ Collection<String> processedFragments = new HashSet<String>(bundles.length);
+ for (int i = 0; i < bundles.length; i++)
+ attachFragment(bundles[i], processedFragments);
+
+ // Lists of cyclic dependencies recording during resolving
+ List<ResolverBundle> cycle = new ArrayList<ResolverBundle>(1); // start small
+ // Attempt to resolve all unresolved bundles
+ for (int i = 0; i < bundles.length; i++) {
+ if (DEBUG)
+ ResolverImpl.log("** RESOLVING " + bundles[i] + " **"); //$NON-NLS-1$ //$NON-NLS-2$
+ cycle.clear();
+ resolveBundle(bundles[i], cycle);
+ // Check for any bundles involved in a cycle.
+ // if any bundles in the cycle are not resolved then we need to resolve the resolvable ones
+ checkCycle(cycle);
+ }
+ // Resolve all fragments that are still attached to at least one host.
+ if (unresolvedBundles.size() > 0) {
+ ResolverBundle[] unresolved = unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
+ for (int i = 0; i < unresolved.length; i++)
+ resolveFragment(unresolved[i]);
+ }
+ checkUsesConstraints(bundles, platformProperties);
+ checkComposites(bundles, platformProperties);
+ }
+
+ private void checkComposites(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ CompositeResolveHelperRegistry helpers = getCompositeHelpers();
+ if (helpers == null)
+ return;
+ Set<ResolverBundle> exclude = null;
+ for (int i = 0; i < bundles.length; i++) {
+ CompositeResolveHelper helper = helpers.getCompositeResolveHelper(bundles[i].getBundleDescription());
+ if (helper == null)
+ continue;
+ if (!bundles[i].isResolved())
+ continue;
+ if (!helper.giveExports(getExportsWiredTo(bundles[i], null))) {
+ state.addResolverError(bundles[i].getBundleDescription(), ResolverError.DISABLED_BUNDLE, null, null);
+ bundles[i].setResolvable(false);
+ // We pass false for keepFragmentsAttached because we need to redo the attachments (bug 272561)
+ setBundleUnresolved(bundles[i], false, false);
+ if (exclude == null)
+ exclude = new HashSet<ResolverBundle>(1);
+ exclude.add(bundles[i]);
+ }
+ }
+ reResolveBundles(exclude, bundles, platformProperties);
+ }
+
+ private void checkUsesConstraints(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ List<ResolverConstraint> conflictingConstraints = findBestCombination(bundles, platformProperties);
+ if (conflictingConstraints == null)
+ return;
+ Set<ResolverBundle> conflictedBundles = null;
+ for (ResolverConstraint conflict : conflictingConstraints) {
+ if (conflict.isOptional()) {
+ conflict.clearPossibleSuppliers();
+ continue;
+ }
+ if (conflictedBundles == null)
+ conflictedBundles = new HashSet<ResolverBundle>(conflictingConstraints.size());
+ ResolverBundle conflictedBundle;
+ if (conflict.isFromFragment())
+ conflictedBundle = bundleMapping.get(conflict.getVersionConstraint().getBundle());
+ else
+ conflictedBundle = conflict.getBundle();
+ if (conflictedBundle != null) {
+ if (DEBUG_USES)
+ System.out.println("Found conflicting constraint: " + conflict + " in bundle " + conflictedBundle); //$NON-NLS-1$//$NON-NLS-2$
+ conflictedBundles.add(conflictedBundle);
+ int type = conflict instanceof ResolverImport ? ResolverError.IMPORT_PACKAGE_USES_CONFLICT : ResolverError.REQUIRE_BUNDLE_USES_CONFLICT;
+ state.addResolverError(conflictedBundle.getBundleDescription(), type, conflict.getVersionConstraint().toString(), conflict.getVersionConstraint());
+ conflictedBundle.setResolvable(false);
+ // We pass false for keepFragmentsAttached because we need to redo the attachments (bug 272561)
+ setBundleUnresolved(conflictedBundle, false, false);
+ }
+ }
+ reResolveBundles(conflictedBundles, bundles, platformProperties);
+ }
+
+ private void reResolveBundles(Set<ResolverBundle> exclude, ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ if (exclude == null || exclude.size() == 0)
+ return;
+ List<ResolverBundle> remainingUnresolved = new ArrayList<ResolverBundle>();
+ for (int i = 0; i < bundles.length; i++) {
+ if (!exclude.contains(bundles[i])) {
+ // We pass false for keepFragmentsAttached because we need to redo the attachments (bug 272561)
+ setBundleUnresolved(bundles[i], false, false);
+ remainingUnresolved.add(bundles[i]);
+ }
+ }
+ resolveBundles0(remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties);
+ }
+
+ private List<ResolverConstraint> findBestCombination(ResolverBundle[] bundles, Dictionary<Object, Object>[] platformProperties) {
+ Object usesMode = platformProperties.length == 0 ? null : platformProperties[0].get("osgi.resolver.usesMode"); //$NON-NLS-1$
+ if (usesMode == null)
+ usesMode = secureAction.getProperty("osgi.resolver.usesMode"); //$NON-NLS-1$
+ if ("ignore".equals(usesMode) || developmentMode) //$NON-NLS-1$
+ return null;
+ Set<String> bundleConstraints = new HashSet<String>();
+ Set<String> packageConstraints = new HashSet<String>();
+ Collection<GenericConstraint> multiRequirementWithMultiSuppliers = new ArrayList<GenericConstraint>();
+ // first try out the initial selections
+ List<ResolverConstraint> initialConflicts = getConflicts(bundles, packageConstraints, bundleConstraints, multiRequirementWithMultiSuppliers);
+ if (initialConflicts == null || "tryFirst".equals(usesMode) || usesCalculationTimeout) { //$NON-NLS-1$
+ groupingChecker.clear();
+ // the first combination have no conflicts or
+ // we only are trying the first combination or
+ // we have timed out the calculation; return without iterating over all combinations
+ return initialConflicts;
+ }
+ ResolverConstraint[][] multipleSuppliers = getMultipleSuppliers(bundles, packageConstraints, bundleConstraints);
+ List<ResolverConstraint> conflicts = null;
+ int[] bestCombination = new int[multipleSuppliers.length];
+ conflicts = findBestCombination(bundles, multipleSuppliers, bestCombination, initialConflicts);
+ if (DEBUG_USES) {
+ System.out.print("Best combination found: "); //$NON-NLS-1$
+ printCombination(bestCombination);
+ }
+ for (int i = 0; i < bestCombination.length; i++) {
+ for (int j = 0; j < multipleSuppliers[i].length; j++) {
+ ResolverConstraint constraint = multipleSuppliers[i][j];
+ constraint.setSelectedSupplier(bestCombination[i]);
+ // sanity check to make sure we did not just get wired to our own dropped export
+ VersionSupplier selectedSupplier = constraint.getSelectedSupplier();
+ if (selectedSupplier != null)
+ selectedSupplier.setSubstitute(null);
+ }
+ }
+ if (!multiRequirementWithMultiSuppliers.isEmpty()) {
+ groupingChecker.clear();
+ for (GenericConstraint multiConstraint : multiRequirementWithMultiSuppliers) {
+ VersionSupplier[] matchingSuppliers = multiConstraint.getMatchingCapabilities();
+ if (matchingSuppliers != null) {
+ for (VersionSupplier supplier : matchingSuppliers) {
+ if (groupingChecker.isConsistent(multiConstraint.getBundle(), (GenericCapability) supplier) != null) {
+ multiConstraint.removePossibleSupplier(supplier);
+ }
+ }
+ }
+ }
+ }
+ // do not need to keep uses data in memory
+ groupingChecker.clear();
+ return conflicts;
+ }
+
+ private int[] getCombination(ResolverConstraint[][] multipleSuppliers, int[] combination) {
+ for (int i = 0; i < combination.length; i++)
+ combination[i] = multipleSuppliers[i][0].getSelectedSupplierIndex();
+ return combination;
+ }
+
+ private List<ResolverConstraint> findBestCombination(ResolverBundle[] bundles, ResolverConstraint[][] multipleSuppliers, int[] bestCombination, List<ResolverConstraint> bestConflicts) {
+ // now iterate over every possible combination until either zero conflicts are found
+ // or we have run out of combinations
+ // if all combinations are tried then return the combination with the lowest number of conflicts
+ long initialTime = System.currentTimeMillis();
+ long timeLimit;
+ if (usesTimeout < 0)
+ timeLimit = Math.min(MAX_USES_TIME_BASE + (bundles.length * 30), MAX_USES_TIME_LIMIT);
+ else
+ timeLimit = usesTimeout;
+
+ int bestConflictCount = getConflictCount(bestConflicts);
+ ResolverBundle[] bestConflictBundles = getConflictedBundles(bestConflicts);
+ while (bestConflictCount != 0 && getNextCombination(multipleSuppliers)) {
+ if ((System.currentTimeMillis() - initialTime) > timeLimit) {
+ if (DEBUG_USES)
+ System.out.println("Uses constraint check has timedout. Using the best solution found so far."); //$NON-NLS-1$
+ usesCalculationTimeout = true;
+ break;
+ }
+ if (DEBUG_USES)
+ printCombination(getCombination(multipleSuppliers, new int[multipleSuppliers.length]));
+ // first count the conflicts for the bundles with conflicts from the best combination
+ // this significantly reduces the time it takes to populate the GroupingChecker for cases where
+ // the combination is no better.
+ List<ResolverConstraint> conflicts = getConflicts(bestConflictBundles, null, null, null);
+ int conflictCount = getConflictCount(conflicts);
+ if (conflictCount >= bestConflictCount) {
+ if (DEBUG_USES)
+ System.out.println("Combination is not better that current best: " + conflictCount + ">=" + bestConflictCount); //$NON-NLS-1$ //$NON-NLS-2$
+ // no need to test the other bundles;
+ // this combination is no better for the bundles which conflict with the current best combination
+ continue;
+ }
+ // this combination improves upon the conflicts for the bundles which conflict with the current best combination;
+ // do an complete conflict count
+ conflicts = getConflicts(bundles, null, null, null);
+ conflictCount = getConflictCount(conflicts);
+ if (conflictCount < bestConflictCount) {
+ // this combination is better that the current best combination; save this combination as the current best
+ bestConflictCount = conflictCount;
+ bestConflicts = conflicts;
+ getCombination(multipleSuppliers, bestCombination);
+ bestConflictBundles = getConflictedBundles(bestConflicts);
+ if (DEBUG_USES)
+ System.out.println("Combination selected as current best: number of conflicts: " + bestConflictCount); //$NON-NLS-1$
+ } else if (DEBUG_USES) {
+ System.out.println("Combination is not better that current best: " + conflictCount + ">=" + bestConflictCount); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ return bestConflicts;
+ }
+
+ private void printCombination(int[] curCombination) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ for (int i = 0; i < curCombination.length; i++) {
+ sb.append(curCombination[i]);
+ if (i < curCombination.length - 1)
+ sb.append(',');
+ }
+ sb.append(']');
+ System.out.println(sb.toString());
+ }
+
+ private ResolverBundle[] getConflictedBundles(List<ResolverConstraint> bestConflicts) {
+ if (bestConflicts == null)
+ return new ResolverBundle[0];
+ List<ResolverBundle> conflictedBundles = new ArrayList<ResolverBundle>(bestConflicts.size());
+ for (ResolverConstraint constraint : bestConflicts)
+ if (!conflictedBundles.contains(constraint.getBundle()))
+ conflictedBundles.add(constraint.getBundle());
+ return conflictedBundles.toArray(new ResolverBundle[conflictedBundles.size()]);
+ }
+
+ private boolean getNextCombination(ResolverConstraint[][] multipleSuppliers) {
+ int current = 0;
+ while (current < multipleSuppliers.length) {
+ if (multipleSuppliers[current][0].selectNextSupplier()) {
+ for (int i = 1; i < multipleSuppliers[current].length; i++)
+ multipleSuppliers[current][i].selectNextSupplier();
+ return true; // the current slot has a next supplier
+ }
+ for (int i = 0; i < multipleSuppliers[current].length; i++)
+ multipleSuppliers[current][i].setSelectedSupplier(0); // reset the current slot
+ current++; // move to the next slot
+ }
+ return false;
+ }
+
+ // only count non-optional conflicts
+ private int getConflictCount(List<ResolverConstraint> conflicts) {
+ if (conflicts == null || conflicts.size() == 0)
+ return 0;
+ int result = 0;
+ for (ResolverConstraint constraint : conflicts)
+ if (!constraint.isOptional())
+ result += 1;
+ return result;
+ }
+
+ private List<ResolverConstraint> getConflicts(ResolverBundle[] bundles, Set<String> packageConstraints, Set<String> bundleConstraints, Collection<GenericConstraint> multiRequirementWithMultiSuppliers) {
+ groupingChecker.clear();
+ List<ResolverConstraint> conflicts = null;
+ for (int i = 0; i < bundles.length; i++)
+ conflicts = addConflicts(bundles[i], packageConstraints, bundleConstraints, multiRequirementWithMultiSuppliers, conflicts);
+ return conflicts;
+ }
+
+ private List<ResolverConstraint> addConflicts(ResolverBundle bundle, Set<String> packageConstraints, Set<String> bundleConstraints, Collection<GenericConstraint> multiRequirementWithMultiSuppliers, List<ResolverConstraint> conflicts) {
+ BundleConstraint[] requires = bundle.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ ResolverBundle selectedSupplier = (ResolverBundle) requires[i].getSelectedSupplier();
+ PackageRoots[][] conflict = selectedSupplier == null ? null : groupingChecker.isConsistent(bundle, selectedSupplier);
+ if (conflict != null) {
+ addConflictNames(conflict, packageConstraints, bundleConstraints);
+ if (conflicts == null)
+ conflicts = new ArrayList<ResolverConstraint>(1);
+ conflicts.add(requires[i]);
+ }
+ }
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ ResolverExport selectedSupplier = (ResolverExport) imports[i].getSelectedSupplier();
+ PackageRoots[][] conflict = selectedSupplier == null ? null : groupingChecker.isConsistent(bundle, selectedSupplier);
+ if (conflict != null) {
+ addConflictNames(conflict, packageConstraints, bundleConstraints);
+ if (conflicts == null)
+ conflicts = new ArrayList<ResolverConstraint>(1);
+ conflicts.add(imports[i]);
+ }
+ }
+
+ GenericConstraint[] genericRequires = bundle.getGenericRequires();
+ for (GenericConstraint capabilityRequirement : genericRequires) {
+ VersionSupplier[] suppliers = capabilityRequirement.getMatchingCapabilities();
+ if (suppliers == null)
+ continue;
+
+ if (multiRequirementWithMultiSuppliers != null && capabilityRequirement.isMultiple() && suppliers.length > 1) {
+ multiRequirementWithMultiSuppliers.add(capabilityRequirement);
+ }
+ // search for at least one capability that does not conflict
+ // in case of single cardinality there will only be one matching supplier
+ // in case of multiple there may be multiple suppliers, but we only need one or more to not conflict with the class space
+ Collection<PackageRoots[][]> capabilityConflicts = null;
+ for (VersionSupplier supplier : suppliers) {
+ PackageRoots[][] conflict = groupingChecker.isConsistent(bundle, (GenericCapability) supplier);
+ if (conflict != null) {
+ if (capabilityConflicts == null)
+ capabilityConflicts = new ArrayList<PackageRoots[][]>(1);
+ capabilityConflicts.add(conflict);
+ }
+ }
+ if (capabilityConflicts != null) {
+ for (PackageRoots[][] conflict : capabilityConflicts) {
+ addConflictNames(conflict, packageConstraints, bundleConstraints);
+ }
+ if (capabilityConflicts.size() == suppliers.length) {
+ // every capability conflicted
+ if (conflicts == null)
+ conflicts = new ArrayList<ResolverConstraint>(1);
+ conflicts.add(capabilityRequirement);
+ }
+ }
+ }
+ return conflicts;
+ }
+
+ // records the conflict names we can use to scope down the list of multiple suppliers
+ private void addConflictNames(PackageRoots[][] conflict, Set<String> packageConstraints, Set<String> bundleConstraints) {
+ if (packageConstraints == null || bundleConstraints == null)
+ return;
+ for (int i = 0; i < conflict.length; i++) {
+ packageConstraints.add(conflict[i][0].getName());
+ packageConstraints.add(conflict[i][1].getName());
+ ResolverExport[] exports0 = conflict[i][0].getRoots();
+ if (exports0 != null)
+ for (int j = 0; j < exports0.length; j++) {
+ ResolverBundle exporter = exports0[j].getExporter();
+ if (exporter != null && exporter.getName() != null)
+ bundleConstraints.add(exporter.getName());
+ }
+ ResolverExport[] exports1 = conflict[i][1].getRoots();
+ if (exports1 != null)
+ for (int j = 0; j < exports1.length; j++) {
+ ResolverBundle exporter = exports1[j].getExporter();
+ if (exporter != null && exporter.getName() != null)
+ bundleConstraints.add(exporter.getName());
+ }
+ }
+ }
+
+ // get a list of resolver constraints that have multiple suppliers
+ // a 2 demensional array is used each entry is a list of identical constraints that have identical suppliers.
+ private ResolverConstraint[][] getMultipleSuppliers(ResolverBundle[] bundles, Set<String> packageConstraints, Set<String> bundleConstraints) {
+ List<ResolverImport> multipleImportSupplierList = new ArrayList<ResolverImport>(1);
+ List<BundleConstraint> multipleRequireSupplierList = new ArrayList<BundleConstraint>(1);
+ List<GenericConstraint> multipleGenericSupplierList = new ArrayList<GenericConstraint>(1);
+ for (ResolverBundle bundle : bundles) {
+ BundleConstraint[] requires = bundle.getRequires();
+ for (BundleConstraint require : requires)
+ if (require.getNumPossibleSuppliers() > 1)
+ multipleRequireSupplierList.add(require);
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (ResolverImport importPkg : imports) {
+ if (importPkg.getNumPossibleSuppliers() > 1) {
+ Integer eeProfile = (Integer) ((ResolverExport) importPkg.getSelectedSupplier()).getExportPackageDescription().getDirective(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (eeProfile.intValue() < 0) {
+ // this is a normal package; always add it
+ multipleImportSupplierList.add(importPkg);
+ } else {
+ // this is a system bundle export
+ // If other exporters of this package also require the system bundle
+ // then this package does not need to be added to the mix
+ // this is an optimization for bundles like org.eclipse.xerces
+ // that export lots of packages also exported by the system bundle on J2SE 1.4
+ VersionSupplier[] suppliers = importPkg.getPossibleSuppliers();
+ for (int suppliersIndex = 1; suppliersIndex < suppliers.length; suppliersIndex++) {
+ Integer ee = (Integer) ((ResolverExport) suppliers[suppliersIndex]).getExportPackageDescription().getDirective(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (ee.intValue() >= 0)
+ continue;
+ if (((ResolverExport) suppliers[suppliersIndex]).getExporter().getRequire(getSystemBundle()) == null)
+ if (((ResolverExport) suppliers[suppliersIndex]).getExporter().getRequire(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) == null) {
+ multipleImportSupplierList.add(importPkg);
+ break;
+ }
+ }
+ }
+ }
+ }
+ GenericConstraint[] genericRequires = bundle.getGenericRequires();
+ for (GenericConstraint genericRequire : genericRequires)
+ if (genericRequire.getNumPossibleSuppliers() > 1 && !genericRequire.isMultiple())
+ multipleGenericSupplierList.add(genericRequire);
+ }
+ List<ResolverConstraint[]> results = new ArrayList<ResolverConstraint[]>();
+ 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<String, List<List<ResolverConstraint>>> multipleImportSupplierMaps = new HashMap<String, List<List<ResolverConstraint>>>();
+ for (ResolverImport importPkg : multipleImportSupplierList)
+ addMutipleSupplierConstraint(multipleImportSupplierMaps, importPkg, importPkg.getName());
+ Map<String, List<List<ResolverConstraint>>> multipleRequireSupplierMaps = new HashMap<String, List<List<ResolverConstraint>>>();
+ for (BundleConstraint requireBundle : multipleRequireSupplierList)
+ addMutipleSupplierConstraint(multipleRequireSupplierMaps, requireBundle, requireBundle.getName());
+ Map<String, List<List<ResolverConstraint>>> multipleGenericSupplierMaps = new HashMap<String, List<List<ResolverConstraint>>>();
+ for (GenericConstraint genericRequire : multipleGenericSupplierList)
+ addMutipleSupplierConstraint(multipleGenericSupplierMaps, genericRequire, genericRequire.getNameSpace());
+ addMergedSuppliers(results, multipleImportSupplierMaps);
+ addMergedSuppliers(results, multipleRequireSupplierMaps);
+ addMergedSuppliers(results, multipleGenericSupplierMaps);
+ // check the results to see if we have reduced the number enough
+ if (results.size() > usesMultipleSuppliersLimit && packageConstraints != null && bundleConstraints != null) {
+ // we still have too big of a list; filter out constraints that are not in conflict
+ List<ResolverConstraint[]> tooBig = results;
+ results = new ArrayList<ResolverConstraint[]>();
+ for (ResolverConstraint[] constraints : tooBig) {
+ ResolverConstraint constraint = constraints.length > 0 ? constraints[0] : null;
+ if (constraint instanceof ResolverImport) {
+ if (packageConstraints.contains(constraint.getName()))
+ results.add(constraints);
+ } else if (constraint instanceof BundleConstraint) {
+ if (bundleConstraints.contains(constraint.getName()))
+ results.add(constraints);
+ }
+ }
+ }
+ } else {
+ // the size is acceptable; just copy the lists as-is
+ for (ResolverConstraint constraint : multipleImportSupplierList)
+ results.add(new ResolverConstraint[] {constraint});
+ for (ResolverConstraint constraint : multipleRequireSupplierList)
+ results.add(new ResolverConstraint[] {constraint});
+ for (ResolverConstraint constraint : multipleGenericSupplierList)
+ results.add(new ResolverConstraint[] {constraint});
+
+ }
+ return results.toArray(new ResolverConstraint[results.size()][]);
+ }
+
+ String getSystemBundle() {
+ Dictionary<?, ?>[] platformProperties = state.getPlatformProperties();
+ String systemBundle = platformProperties.length == 0 ? null : (String) platformProperties[0].get(StateImpl.STATE_SYSTEM_BUNDLE);
+ if (systemBundle == null)
+ systemBundle = EquinoxContainer.NAME;
+ return systemBundle;
+ }
+
+ private void addMergedSuppliers(List<ResolverConstraint[]> mergedSuppliers, Map<String, List<List<ResolverConstraint>>> constraints) {
+ for (List<List<ResolverConstraint>> mergedConstraintLists : constraints.values()) {
+ for (List<ResolverConstraint> constraintList : mergedConstraintLists) {
+ mergedSuppliers.add(constraintList.toArray(new ResolverConstraint[constraintList.size()]));
+ }
+ }
+ }
+
+ private void addMutipleSupplierConstraint(Map<String, List<List<ResolverConstraint>>> constraints, ResolverConstraint constraint, String key) {
+ List<List<ResolverConstraint>> mergedConstraintLists = constraints.get(key);
+ if (mergedConstraintLists == null) {
+ mergedConstraintLists = new ArrayList<List<ResolverConstraint>>(0);
+ List<ResolverConstraint> constraintList = new ArrayList<ResolverConstraint>(1);
+ constraintList.add(constraint);
+ mergedConstraintLists.add(constraintList);
+ constraints.put(key, mergedConstraintLists);
+ return;
+ }
+ for (List<ResolverConstraint> constraintList : mergedConstraintLists) {
+ ResolverConstraint mergedConstraint = constraintList.get(0);
+ VersionSupplier[] suppliers1 = constraint.getPossibleSuppliers();
+ VersionSupplier[] suppliers2 = mergedConstraint.getPossibleSuppliers();
+ if (suppliers1.length != suppliers2.length)
+ continue;
+ for (int i = 0; i < suppliers1.length; i++)
+ if (suppliers1[i] != suppliers2[i])
+ continue;
+ constraintList.add(constraint);
+ return;
+ }
+ List<ResolverConstraint> constraintList = new ArrayList<ResolverConstraint>(1);
+ constraintList.add(constraint);
+ mergedConstraintLists.add(constraintList);
+ }
+
+ private void checkCycle(List<ResolverBundle> cycle) {
+ int cycleSize = cycle.size();
+ if (cycleSize == 0)
+ return;
+ cycleLoop: for (Iterator<ResolverBundle> iCycle = cycle.iterator(); iCycle.hasNext();) {
+ ResolverBundle cycleBundle = iCycle.next();
+ if (!cycleBundle.isResolvable()) {
+ iCycle.remove(); // remove this bundle from the list of bundles that need re-resolved
+ continue cycleLoop;
+ }
+ // Check that we haven't wired to any dropped exports
+ ResolverImport[] imports = cycleBundle.getImportPackages();
+ for (int j = 0; j < imports.length; j++) {
+ // check for dropped exports
+ while (imports[j].getSelectedSupplier() != null) {
+ ResolverExport importSupplier = (ResolverExport) imports[j].getSelectedSupplier();
+ if (importSupplier.getSubstitute() != null)
+ imports[j].selectNextSupplier();
+ else
+ break;
+ }
+ if (!imports[j].isDynamic() && !imports[j].isOptional() && imports[j].getSelectedSupplier() == null) {
+ cycleBundle.setResolvable(false);
+ state.addResolverError(imports[j].getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, imports[j].getVersionConstraint().toString(), imports[j].getVersionConstraint());
+ iCycle.remove();
+ continue cycleLoop;
+ }
+ }
+ }
+ if (cycle.size() != cycleSize) {
+ //we removed an un-resolvable bundle; must re-resolve remaining cycle
+ for (int i = 0; i < cycle.size(); i++) {
+ ResolverBundle cycleBundle = cycle.get(i);
+ cycleBundle.clearWires();
+ }
+ List<ResolverBundle> innerCycle = new ArrayList<ResolverBundle>(cycle.size());
+ for (int i = 0; i < cycle.size(); i++)
+ resolveBundle(cycle.get(i), innerCycle);
+ checkCycle(innerCycle);
+ } else {
+ for (int i = 0; i < cycle.size(); i++) {
+ if (DEBUG || DEBUG_CYCLES)
+ ResolverImpl.log("Pushing " + cycle.get(i) + " to RESOLVED"); //$NON-NLS-1$ //$NON-NLS-2$
+ setBundleResolved(cycle.get(i));
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ static Collection<BundleCapability> asCapabilities(Collection<? extends BundleCapability> capabilities) {
+ return (Collection<BundleCapability>) capabilities;
+ }
+
+ private void resolveFragment(ResolverBundle fragment) {
+ if (!fragment.isFragment())
+ return;
+ if (fragment.getHost().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
+ private boolean resolveBundle(ResolverBundle bundle, List<ResolverBundle> cycle) {
+ if (bundle.isFragment())
+ return false;
+ if (!bundle.isResolvable()) {
+ if (DEBUG)
+ ResolverImpl.log(" - " + bundle + " is unresolvable"); //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ }
+ switch (bundle.getState()) {
+ case ResolverBundle.RESOLVED :
+ // 'bundle' is already resolved so just return
+ if (DEBUG)
+ ResolverImpl.log(" - " + bundle + " already resolved"); //$NON-NLS-1$ //$NON-NLS-2$
+ return true;
+ case ResolverBundle.UNRESOLVED :
+ // 'bundle' is UNRESOLVED so move to RESOLVING
+ bundle.clearWires();
+ setBundleResolving(bundle);
+ break;
+ case ResolverBundle.RESOLVING :
+ if (cycle.contains(bundle))
+ return true;
+ break;
+ default :
+ break;
+ }
+
+ boolean failed = false;
+
+ if (!failed) {
+ GenericConstraint[] genericRequires = bundle.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++) {
+ if (genericRequires[i].isEffective()) {
+ if (!resolveGenericReq(genericRequires[i], cycle)) {
+ if (DEBUG || DEBUG_GENERICS)
+ ResolverImpl.log("** GENERICS " + genericRequires[i].getVersionConstraint().getName() + "[" + genericRequires[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ state.addResolverError(genericRequires[i].getVersionConstraint().getBundle(), ResolverError.MISSING_GENERIC_CAPABILITY, genericRequires[i].getVersionConstraint().toString(), genericRequires[i].getVersionConstraint());
+ if (genericRequires[i].isFromFragment()) {
+ if (!developmentMode) // only detach fragments when not in devmode
+ bundle.detachFragment(bundleMapping.get(genericRequires[i].getVersionConstraint().getBundle()), null);
+ continue;
+ }
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ failed = true;
+ break;
+ }
+ } else {
+ if (StateImpl.OSGI_EE_NAMESPACE.equals(genericRequires[i].getNameSpace())) {
+ VersionSupplier supplier = genericRequires[i].getSelectedSupplier();
+ Integer ee = supplier == null ? null : (Integer) ((GenericDescription) supplier.getBaseDescription()).getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE);
+ if (ee != null && ((BundleDescriptionImpl) bundle.getBaseDescription()).getEquinoxEE() < 0)
+ ((BundleDescriptionImpl) bundle.getBundleDescription()).setEquinoxEE(ee);
+ }
+ }
+ }
+ }
+ }
+
+ if (!failed) {
+ // Iterate thru required bundles of 'bundle' trying to find matching bundles.
+ BundleConstraint[] requires = bundle.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ if (!resolveRequire(requires[i], cycle)) {
+ if (DEBUG || DEBUG_REQUIRES)
+ ResolverImpl.log("** REQUIRE " + requires[i].getVersionConstraint().getName() + "[" + requires[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ state.addResolverError(requires[i].getVersionConstraint().getBundle(), ResolverError.MISSING_REQUIRE_BUNDLE, requires[i].getVersionConstraint().toString(), requires[i].getVersionConstraint());
+ // If the require has failed to resolve and it is from a fragment, then remove the fragment from the host
+ if (requires[i].isFromFragment()) {
+ if (!developmentMode) // only detach fragments when not in devmode
+ bundle.detachFragment(bundleMapping.get(requires[i].getVersionConstraint().getBundle()), requires[i]);
+ continue;
+ }
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ failed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!failed) {
+ // Iterate thru imports of 'bundle' trying to find matching exports.
+ ResolverImport[] imports = bundle.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ // Only resolve non-dynamic imports here
+ if (!imports[i].isDynamic() && !resolveImport(imports[i], cycle)) {
+ if (DEBUG || DEBUG_IMPORTS)
+ ResolverImpl.log("** IMPORT " + imports[i].getName() + "[" + imports[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ // If the import has failed to resolve and it is from a fragment, then remove the fragment from the host
+ state.addResolverError(imports[i].getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, imports[i].getVersionConstraint().toString(), imports[i].getVersionConstraint());
+ if (imports[i].isFromFragment()) {
+ if (!developmentMode) // only detach fragments when not in devmode
+ bundle.detachFragment(bundleMapping.get(imports[i].getVersionConstraint().getBundle()), imports[i]);
+ continue;
+ }
+ if (!developmentMode) {
+ // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
+ failed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // check that fragment constraints are met by the constraints that got resolved to the host
+ checkFragmentConstraints(bundle);
+
+ // do some extra checking when in development mode to see if other resolver error occurred
+ if (developmentMode && !failed && state.getResolverErrors(bundle.getBundleDescription()).length > 0)
+ failed = true;
+
+ // Need to check that all mandatory imports are wired. If they are then
+ // set the bundle RESOLVED, otherwise set it back to UNRESOLVED
+ if (failed) {
+ setBundleUnresolved(bundle, false, developmentMode);
+ if (DEBUG)
+ ResolverImpl.log(bundle + " NOT RESOLVED"); //$NON-NLS-1$
+ } else if (!cycle.contains(bundle)) {
+ setBundleResolved(bundle);
+ if (DEBUG)
+ ResolverImpl.log(bundle + " RESOLVED"); //$NON-NLS-1$
+ }
+
+ if (bundle.getState() == ResolverBundle.UNRESOLVED)
+ bundle.setResolvable(false); // Set it to unresolvable so we don't attempt to resolve it again in this round
+
+ return bundle.getState() != ResolverBundle.UNRESOLVED;
+ }
+
+ private void checkFragmentConstraints(ResolverBundle bundle) {
+ // get all currently attached fragments and ensure that any constraints
+ // they have do not conflict with the constraints resolved to by the host
+ ResolverBundle[] fragments = bundle.getFragments();
+ for (int i = 0; i < fragments.length; i++) {
+ BundleDescription fragment = fragments[i].getBundleDescription();
+ if (bundle.constraintsConflict(fragment, fragment.getImportPackages(), fragment.getRequiredBundles(), fragment.getGenericRequires()) && !developmentMode)
+ // found some conflicts; detach the fragment
+ bundle.detachFragment(fragments[i], null);
+ }
+ }
+
+ private boolean resolveGenericReq(GenericConstraint constraint, List<ResolverBundle> cycle) {
+ if (DEBUG_GENERICS)
+ ResolverImpl.log("Trying to resolve: " + constraint.getBundle() + ", " + constraint.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$
+ VersionSupplier matchingCapability = constraint.getSelectedSupplier();
+ if (matchingCapability != null) {
+ if (!cycle.contains(constraint.getBundle())) {
+ cycle.add(constraint.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("generic cycle: " + constraint.getBundle() + " -> " + constraint.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (DEBUG_GENERICS)
+ ResolverImpl.log(" - already wired"); //$NON-NLS-1$
+ return true; // Already wired (due to grouping dependencies) so just return
+ }
+ List<GenericCapability> candidates;
+ long timestamp;
+ do {
+ timestamp = state.getTimeStamp();
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(constraint.getNameSpace());
+ String name = constraint.getName();
+ List<GenericCapability> capabilities;
+ if (namespace == null)
+ capabilities = Collections.EMPTY_LIST;
+ else
+ capabilities = name == null || name.indexOf('*') >= 0 ? namespace.getAllValues() : namespace.get(name);
+ candidates = new ArrayList<GenericCapability>(capabilities);
+ List<BundleCapability> genCapabilities = new ArrayList<BundleCapability>(candidates.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<GenericCapability> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ GenericCapability capability = iCandidates.next();
+ if (!constraint.isSatisfiedBy(capability)) {
+ iCandidates.remove();
+ } else {
+ genCapabilities.add(capability.getCapability());
+ }
+ }
+ if (hook != null)
+ hook.filterMatches(constraint.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, GenericCapability>(genCapabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ boolean result = false;
+ // We are left with only capabilities that satisfy the constraint.
+ for (GenericCapability capability : candidates) {
+ if (DEBUG_GENERICS)
+ ResolverImpl.log("CHECKING GENERICS: " + capability.getBaseDescription()); //$NON-NLS-1$
+
+ // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
+ constraint.addPossibleSupplier(capability); // Wire to the capability
+ if (constraint.getBundle() == capability.getResolverBundle()) {
+ result = true; // Wired to ourselves
+ continue;
+ }
+ VersionSupplier[] capabilityHosts = capability.getResolverBundle().isFragment() ? capability.getResolverBundle().getHost().getPossibleSuppliers() : new ResolverBundle[] {capability.getResolverBundle()};
+ boolean foundResolvedMatch = false;
+ for (int i = 0; capabilityHosts != null && i < capabilityHosts.length; i++) {
+ ResolverBundle capabilitySupplier = capabilityHosts[i].getResolverBundle();
+ if (capabilitySupplier == constraint.getBundle()) {
+ // the capability is from a fragment attached to this host do not recursively resolve the host again
+ foundResolvedMatch = true;
+ continue;
+ }
+ boolean successfulResolve = false;
+ if (capabilitySupplier.getState() != ResolverBundle.RESOLVED) {
+ // only attempt to resolve the supplier if not osgi.ee name space
+ if (!StateImpl.OSGI_EE_NAMESPACE.equals(constraint.getNameSpace()))
+ successfulResolve = resolveBundle(capabilitySupplier, cycle);
+ }
+
+ // if in dev mode then allow a constraint to resolve to an unresolved bundle
+ if (capabilitySupplier.getState() == ResolverBundle.RESOLVED || (successfulResolve || developmentMode)) {
+ foundResolvedMatch |= !capability.getResolverBundle().isFragment() ? true : capability.getResolverBundle().getHost().getPossibleSuppliers() != null;
+ // Check cyclic dependencies
+ if (capabilitySupplier.getState() == ResolverBundle.RESOLVING)
+ if (!cycle.contains(capabilitySupplier))
+ cycle.add(capabilitySupplier);
+ }
+ }
+ if (!foundResolvedMatch) {
+ constraint.removePossibleSupplier(capability);
+ continue; // constraint hasn't resolved
+ }
+ if (DEBUG_GENERICS)
+ ResolverImpl.log("Found match: " + capability.getBaseDescription() + ". Wiring"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = true;
+ }
+ return result ? true : constraint.isOptional() || constraint.isFromRequiredEE();
+ }
+
+ // Resolve the supplied import. Returns true if the import can be resolved, false otherwise
+ private boolean resolveRequire(BundleConstraint req, List<ResolverBundle> cycle) {
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log("Trying to resolve: " + req.getBundle() + ", " + req.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$
+ if (req.getSelectedSupplier() != null) {
+ // Check for unrecorded cyclic dependency
+ if (!cycle.contains(req.getBundle())) {
+ cycle.add(req.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("require-bundle cycle: " + req.getBundle() + " -> " + req.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log(" - already wired"); //$NON-NLS-1$
+ return true; // Already wired (due to grouping dependencies) so just return
+ }
+ List<ResolverBundle> candidates;
+ long timestamp;
+ do {
+ timestamp = state.getTimeStamp();
+ List<ResolverBundle> bundles = resolverBundles.get(req.getVersionConstraint().getName());
+ candidates = new ArrayList<ResolverBundle>(bundles);
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(candidates.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<ResolverBundle> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ ResolverBundle bundle = iCandidates.next();
+ if (!req.isSatisfiedBy(bundle)) {
+ iCandidates.remove();
+ } else {
+ capabilities.add(bundle.getCapability());
+ }
+ }
+ if (hook != null)
+ hook.filterMatches(req.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, ResolverBundle>(capabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ // We are left with only capabilities that satisfy the require bundle.
+ boolean result = false;
+ for (ResolverBundle bundle : candidates) {
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log("CHECKING: " + bundle.getBundleDescription()); //$NON-NLS-1$
+
+ // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
+ req.addPossibleSupplier(bundle);
+ if (req.getBundle() != bundle) {
+ // if in dev mode then allow a constraint to resolve to an unresolved bundle
+ if (bundle.getState() != ResolverBundle.RESOLVED && !resolveBundle(bundle, cycle) && !developmentMode) {
+ req.removePossibleSupplier(bundle);
+ continue; // Bundle hasn't resolved
+ }
+ }
+ // Check cyclic dependencies
+ if (req.getBundle() != bundle) {
+ if (bundle.getState() == ResolverBundle.RESOLVING)
+ // If the bundle is RESOLVING, we have a cyclic dependency
+ if (!cycle.contains(req.getBundle())) {
+ cycle.add(req.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("require-bundle cycle: " + req.getBundle() + " -> " + req.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ if (DEBUG_REQUIRES)
+ ResolverImpl.log("Found match: " + bundle.getBundleDescription() + ". Wiring"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = true;
+ }
+
+ if (result || req.isOptional())
+ return true; // If the req is optional then just return true
+
+ return false;
+ }
+
+ // Resolve the supplied import. Returns true if the import can be resolved, false otherwise
+ private boolean resolveImport(ResolverImport imp, List<ResolverBundle> cycle) {
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("Trying to resolve: " + imp.getBundle() + ", " + imp.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+ if (imp.getSelectedSupplier() != null) {
+ // Check for unrecorded cyclic dependency
+ if (!cycle.contains(imp.getBundle())) {
+ cycle.add(imp.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("import-package cycle: " + imp.getBundle() + " -> " + imp.getSelectedSupplier() + " from " + imp.getSelectedSupplier().getBundleDescription()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log(" - already wired"); //$NON-NLS-1$
+ return true; // Already wired (due to grouping dependencies) so just return
+ }
+ boolean result = false;
+ ResolverExport[] substitutableExps = imp.getBundle().getExports(imp.getName());
+ long timestamp;
+ List<ResolverExport> candidates;
+ do {
+ timestamp = state.getTimeStamp();
+ List<ResolverExport> exports = resolverExports.get(imp.getName());
+ candidates = new ArrayList<ResolverExport>(exports);
+ List<BundleCapability> capabilities = new ArrayList<BundleCapability>(candidates.size());
+ // Must remove candidates that do not match before calling hooks.
+ for (Iterator<ResolverExport> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
+ ResolverExport export = iCandidates.next();
+ if (!imp.isSatisfiedBy(export)) {
+ iCandidates.remove();
+ } else {
+ capabilities.add(export.getCapability());
+ }
+ }
+ if (hook != null)
+ hook.filterMatches(imp.getRequirement(), asCapabilities(new ArrayMap<BundleCapability, ResolverExport>(capabilities, candidates)));
+ } while (timestamp != state.getTimeStamp());
+ // We are left with only capabilities that satisfy the import.
+ for (ResolverExport export : candidates) {
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("CHECKING: " + export.getExporter().getBundleDescription() + ", " + export.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+
+ int originalState = export.getExporter().getState();
+ if (imp.isDynamic() && originalState != ResolverBundle.RESOLVED)
+ continue; // Must not attempt to resolve an exporter when dynamic
+ if (imp.getSelectedSupplier() != null && ((ResolverExport) imp.getSelectedSupplier()).getExporter() == imp.getBundle())
+ break; // We wired to ourselves; nobody else matters
+ // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
+ imp.addPossibleSupplier(export);
+ if (imp.getBundle() != export.getExporter()) {
+ for (int j = 0; j < substitutableExps.length; j++)
+ if (substitutableExps[j].getSubstitute() == null)
+ substitutableExps[j].setSubstitute(export); // Import wins, drop export
+ // if in dev mode then allow a constraint to resolve to an unresolved bundle
+ if ((originalState != ResolverBundle.RESOLVED && !resolveBundle(export.getExporter(), cycle) && !developmentMode) || export.getSubstitute() != null) {
+ // remove the possible supplier
+ imp.removePossibleSupplier(export);
+ // add back the exports of this package from the importer
+ if (imp.getSelectedSupplier() == null)
+ for (int j = 0; j < substitutableExps.length; j++)
+ if (substitutableExps[j].getSubstitute() == export)
+ substitutableExps[j].setSubstitute(null);
+ continue; // Bundle hasn't resolved || export has not been selected and is unavailable
+ }
+ } else if (export.getSubstitute() != null)
+ continue; // we already found a possible import that satisifies us; our export is dropped
+
+ // Record any cyclic dependencies
+ if (imp.getBundle() != export.getExporter())
+ if (export.getExporter().getState() == ResolverBundle.RESOLVING) {
+ // If the exporter is RESOLVING, we have a cyclic dependency
+ if (!cycle.contains(imp.getBundle())) {
+ cycle.add(imp.getBundle());
+ if (DEBUG_CYCLES)
+ ResolverImpl.log("import-package cycle: " + imp.getBundle() + " -> " + imp.getSelectedSupplier() + " from " + imp.getSelectedSupplier().getBundleDescription()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("Found match: " + export.getExporter() + ". Wiring " + imp.getBundle() + ":" + imp.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ result = true;
+ }
+
+ if (result)
+ return true;
+ if (imp.isOptional())
+ return true; // If the import is optional then just return true
+ if (substitutableExps.length > 0 && substitutableExps[0].getSubstitute() == null)
+ return true; // If we still have an export that is not substituted return true
+ return false;
+ }
+
+ // Move a bundle to UNRESOLVED
+ private void setBundleUnresolved(ResolverBundle bundle, boolean removed, boolean keepFragsAttached) {
+ if (bundle.getState() == ResolverBundle.UNRESOLVED && !developmentMode)
+ // in this case there is nothing more to do
+ return;
+ // Note that when in dev mode we only want to force the fragment detach if asked to;
+ // this would be done only when forcing a dependency chain to unresolve from unresolveBundle method
+ if (removed || !keepFragsAttached) {
+ // Force the initialization of the bundle, its exports and its capabilities. This is needed to force proper attachment of fragments.
+ resolverExports.remove(bundle.getExportPackages());
+ removeGenerics(bundle.getGenericCapabilities());
+ bundle.detachAllFragments();
+ bundle.detachFromHosts();
+ bundle.initialize(false);
+ if (!removed) {
+ // add back the available exports/capabilities
+ resolverExports.put(bundle.getExportPackages());
+ addGenerics(bundle.getGenericCapabilities());
+ }
+ }
+ // TODO unresolvedBundles should be a set; for now only need to do a contains check in devMode.
+ if (!removed && (!developmentMode || !unresolvedBundles.contains(bundle)))
+ unresolvedBundles.add(bundle);
+ bundle.setState(ResolverBundle.UNRESOLVED);
+ }
+
+ // Move a bundle to RESOLVED
+ private void setBundleResolved(ResolverBundle bundle) {
+ if (bundle.getState() == ResolverBundle.RESOLVED)
+ return;
+ unresolvedBundles.remove(bundle);
+ bundle.setState(ResolverBundle.RESOLVED);
+ }
+
+ // Move a bundle to RESOLVING
+ private void setBundleResolving(ResolverBundle bundle) {
+ if (bundle.getState() == ResolverBundle.RESOLVING)
+ return;
+ unresolvedBundles.remove(bundle);
+ bundle.setState(ResolverBundle.RESOLVING);
+ }
+
+ // Resolves the bundles in the State
+ private void stateResolveBundles(ResolverBundle[] resolvedBundles) {
+ for (int i = 0; i < resolvedBundles.length; i++) {
+ if (!resolvedBundles[i].getBundleDescription().isResolved())
+ stateResolveBundle(resolvedBundles[i]);
+ }
+ }
+
+ private void stateResolveConstraints(ResolverBundle rb) {
+ ResolverImport[] imports = rb.getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ ResolverExport export = (ResolverExport) imports[i].getSelectedSupplier();
+ BaseDescription supplier = export == null ? null : export.getExportPackageDescription();
+ state.resolveConstraint(imports[i].getVersionConstraint(), supplier);
+ }
+ BundleConstraint[] requires = rb.getRequires();
+ for (int i = 0; i < requires.length; i++) {
+ ResolverBundle bundle = (ResolverBundle) requires[i].getSelectedSupplier();
+ BaseDescription supplier = bundle == null ? null : bundle.getBundleDescription();
+ state.resolveConstraint(requires[i].getVersionConstraint(), supplier);
+ }
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++) {
+ VersionSupplier[] matchingCapabilities = genericRequires[i].getMatchingCapabilities();
+ if (matchingCapabilities == null)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), null);
+ else
+ for (int j = 0; j < matchingCapabilities.length; j++)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), matchingCapabilities[j].getBaseDescription());
+ }
+ }
+
+ private void stateResolveFragConstraints(ResolverBundle rb) {
+ ResolverBundle host = (ResolverBundle) rb.getHost().getSelectedSupplier();
+ ImportPackageSpecification[] imports = rb.getBundleDescription().getImportPackages();
+ for (int i = 0; i < imports.length; i++) {
+ ResolverImport hostImport = host == null ? null : host.getImport(imports[i].getName());
+ ResolverExport export = (ResolverExport) (hostImport == null ? null : hostImport.getSelectedSupplier());
+ BaseDescription supplier = export == null ? null : export.getExportPackageDescription();
+ state.resolveConstraint(imports[i], supplier);
+ }
+ BundleSpecification[] requires = rb.getBundleDescription().getRequiredBundles();
+ for (int i = 0; i < requires.length; i++) {
+ BundleConstraint hostRequire = host == null ? null : host.getRequire(requires[i].getName());
+ ResolverBundle bundle = (ResolverBundle) (hostRequire == null ? null : hostRequire.getSelectedSupplier());
+ BaseDescription supplier = bundle == null ? null : bundle.getBundleDescription();
+ state.resolveConstraint(requires[i], supplier);
+ }
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ for (int i = 0; i < genericRequires.length; i++) {
+ VersionSupplier[] matchingCapabilities = genericRequires[i].getMatchingCapabilities();
+ if (matchingCapabilities == null)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), null);
+ else
+ for (int j = 0; j < matchingCapabilities.length; j++)
+ state.resolveConstraint(genericRequires[i].getVersionConstraint(), matchingCapabilities[j].getBaseDescription());
+ }
+ }
+
+ private void stateResolveBundle(ResolverBundle rb) {
+ // if in dev mode then we want to tell the state about the constraints we were able to resolve
+ if (!rb.isResolved() && !developmentMode)
+ return;
+ if (rb.isFragment())
+ stateResolveFragConstraints(rb);
+ else
+ stateResolveConstraints(rb);
+
+ // Build up the state wires
+ Map<String, List<StateWire>> stateWires = new HashMap<String, List<StateWire>>();
+
+ // Gather selected exports
+ ResolverExport[] exports = rb.getSelectedExports();
+ List<ExportPackageDescription> selectedExports = new ArrayList<ExportPackageDescription>(exports.length);
+ for (int i = 0; i < exports.length; i++) {
+ if (permissionChecker.checkPackagePermission(exports[i].getExportPackageDescription()))
+ selectedExports.add(exports[i].getExportPackageDescription());
+ }
+ ExportPackageDescription[] selectedExportsArray = selectedExports.toArray(new ExportPackageDescription[selectedExports.size()]);
+
+ // Gather substitute exports
+ ResolverExport[] substituted = rb.getSubstitutedExports();
+ List<ExportPackageDescription> substitutedExports = new ArrayList<ExportPackageDescription>(substituted.length);
+ for (int i = 0; i < substituted.length; i++) {
+ substitutedExports.add(substituted[i].getExportPackageDescription());
+ }
+ ExportPackageDescription[] substitutedExportsArray = substitutedExports.toArray(new ExportPackageDescription[substitutedExports.size()]);
+
+ // Gather exports that have been wired to
+ ExportPackageDescription[] exportsWiredToArray = getExportsWiredTo(rb, stateWires);
+
+ // Gather bundles that have been wired to
+ BundleConstraint[] requires = rb.getRequires();
+ List<BundleDescription> bundlesWiredTo = new ArrayList<BundleDescription>(requires.length);
+ List<StateWire> requireWires = new ArrayList<StateWire>(requires.length);
+ for (int i = 0; i < requires.length; i++)
+ if (requires[i].getSelectedSupplier() != null) {
+ BundleDescription supplier = (BundleDescription) requires[i].getSelectedSupplier().getBaseDescription();
+ bundlesWiredTo.add(supplier);
+ StateWire requireWire = newStateWire(rb.getBundleDescription(), requires[i].getVersionConstraint(), supplier, supplier);
+ requireWires.add(requireWire);
+ }
+ BundleDescription[] bundlesWiredToArray = bundlesWiredTo.toArray(new BundleDescription[bundlesWiredTo.size()]);
+ if (!requireWires.isEmpty())
+ stateWires.put(BundleRevision.BUNDLE_NAMESPACE, requireWires);
+
+ GenericCapability[] capabilities = rb.getGenericCapabilities();
+ List<GenericDescription> selectedCapabilities = new ArrayList<GenericDescription>(capabilities.length);
+ for (GenericCapability capability : capabilities)
+ if (capability.isEffective() && permissionChecker.checkCapabilityPermission(capability.getGenericDescription()))
+ selectedCapabilities.add(capability.getGenericDescription());
+ GenericDescription[] selectedCapabilitiesArray = selectedCapabilities.toArray(new GenericDescription[selectedCapabilities.size()]);
+
+ GenericConstraint[] genericRequires = rb.getGenericRequires();
+ List<GenericDescription> resolvedGenericRequires = new ArrayList<GenericDescription>(genericRequires.length);
+ for (GenericConstraint genericConstraint : genericRequires) {
+ VersionSupplier[] matching = genericConstraint.getMatchingCapabilities();
+ if (matching != null)
+ for (VersionSupplier capability : matching) {
+ GenericDescription supplier = ((GenericCapability) capability).getGenericDescription();
+ resolvedGenericRequires.add(supplier);
+ StateWire genericWire = newStateWire(rb.getBundleDescription(), genericConstraint.getVersionConstraint(), supplier.getSupplier(), supplier);
+ List<StateWire> genericWires = stateWires.get(genericConstraint.getNameSpace());
+ if (genericWires == null) {
+ genericWires = new ArrayList<StateWire>();
+ stateWires.put(genericConstraint.getNameSpace(), genericWires);
+ }
+ genericWires.add(genericWire);
+ }
+ }
+ GenericDescription[] capabilitiesWiredToArray = resolvedGenericRequires.toArray(new GenericDescription[resolvedGenericRequires.size()]);
+
+ BundleDescription[] hostBundles = null;
+ if (rb.isFragment()) {
+ VersionSupplier[] matchingBundles = rb.getHost().getPossibleSuppliers();
+ if (matchingBundles != null && matchingBundles.length > 0) {
+ hostBundles = new BundleDescription[matchingBundles.length];
+ List<StateWire> hostWires = new ArrayList<StateWire>(matchingBundles.length);
+ stateWires.put(BundleRevision.HOST_NAMESPACE, hostWires);
+ for (int i = 0; i < matchingBundles.length; i++) {
+ hostBundles[i] = matchingBundles[i].getBundleDescription();
+ StateWire hostWire = newStateWire(rb.getBundleDescription(), rb.getHost().getVersionConstraint(), hostBundles[i], hostBundles[i]);
+ hostWires.add(hostWire);
+ if (hostBundles[i].isResolved()) {
+ ExportPackageDescription[] newSelectedExports = null;
+ GenericDescription[] newSelectedCapabilities = null;
+ if (rb.isNewFragmentExports()) {
+ // update the host's set of selected exports
+ ResolverExport[] hostExports = ((ResolverBundle) matchingBundles[i]).getSelectedExports();
+ newSelectedExports = new ExportPackageDescription[hostExports.length];
+ for (int j = 0; j < hostExports.length; j++)
+ newSelectedExports[j] = hostExports[j].getExportPackageDescription();
+ }
+ if (rb.isNewFragmentCapabilities()) {
+ // update the host's set of selected capabilities
+ GenericCapability[] hostCapabilities = ((ResolverBundle) matchingBundles[i]).getGenericCapabilities();
+ newSelectedCapabilities = new GenericDescription[hostCapabilities.length];
+ for (int j = 0; j < hostCapabilities.length; j++)
+ newSelectedCapabilities[j] = hostCapabilities[j].getGenericDescription();
+ }
+ if (newSelectedCapabilities != null || newSelectedExports != null) {
+ if (newSelectedCapabilities == null)
+ newSelectedCapabilities = hostBundles[i].getSelectedGenericCapabilities();
+ if (newSelectedExports == null)
+ newSelectedExports = hostBundles[i].getSelectedExports();
+ state.resolveBundle(hostBundles[i], true, null, newSelectedExports, hostBundles[i].getSubstitutedExports(), newSelectedCapabilities, hostBundles[i].getResolvedRequires(), hostBundles[i].getResolvedImports(), hostBundles[i].getResolvedGenericRequires(), ((BundleDescriptionImpl) hostBundles[i]).getWires());
+ }
+ }
+ }
+ }
+ }
+
+ // Resolve the bundle in the state
+ state.resolveBundle(rb.getBundleDescription(), rb.isResolved(), hostBundles, selectedExportsArray, substitutedExportsArray, selectedCapabilitiesArray, bundlesWiredToArray, exportsWiredToArray, capabilitiesWiredToArray, stateWires);
+ }
+
+ private static ExportPackageDescription[] getExportsWiredTo(ResolverBundle rb, Map<String, List<StateWire>> stateWires) {
+ // Gather exports that have been wired to
+ ResolverImport[] imports = rb.getImportPackages();
+ List<ExportPackageDescription> exportsWiredTo = new ArrayList<ExportPackageDescription>(imports.length);
+ List<StateWire> importWires = new ArrayList<StateWire>(imports.length);
+ for (int i = 0; i < imports.length; i++)
+ if (imports[i].getSelectedSupplier() != null) {
+ ExportPackageDescription supplier = (ExportPackageDescription) imports[i].getSelectedSupplier().getBaseDescription();
+ exportsWiredTo.add(supplier);
+ StateWire wire = newStateWire(rb.getBundleDescription(), imports[i].getVersionConstraint(), supplier.getExporter(), supplier);
+ importWires.add(wire);
+ }
+ if (stateWires != null && !importWires.isEmpty())
+ stateWires.put(BundleRevision.PACKAGE_NAMESPACE, importWires);
+ return exportsWiredTo.toArray(new ExportPackageDescription[exportsWiredTo.size()]);
+ }
+
+ private static StateWire newStateWire(BundleDescription requirementHost, VersionConstraint declaredRequirement, BundleDescription capabilityHost, BaseDescription declaredCapability) {
+ BaseDescription fragDeclared = ((BaseDescriptionImpl) declaredCapability).getFragmentDeclaration();
+ declaredCapability = fragDeclared != null ? fragDeclared : declaredCapability;
+ return new StateWire(requirementHost, declaredRequirement, capabilityHost, declaredCapability);
+ }
+
+ // Resolve dynamic import
+ public synchronized ExportPackageDescription resolveDynamicImport(BundleDescription importingBundle, String requestedPackage) {
+ if (state == null)
+ throw new IllegalStateException("RESOLVER_NO_STATE"); //$NON-NLS-1$
+
+ // Make sure the resolver is initialized
+ if (!initialized)
+ initialize();
+ hook = (state instanceof StateImpl) ? ((StateImpl) state).getResolverHook() : null;
+ try {
+ ResolverBundle rb = bundleMapping.get(importingBundle);
+ if (rb.getExport(requestedPackage) != null)
+ return null; // do not allow dynamic wires for packages which this bundle exports
+ ResolverImport[] resolverImports = rb.getImportPackages();
+ // Check through the ResolverImports of this bundle.
+ // If there is a matching one then pass it into resolveImport()
+ for (int j = 0; j < resolverImports.length; j++) {
+ // Make sure it is a dynamic import
+ if (!resolverImports[j].isDynamic())
+ continue;
+ // Resolve the import
+ ExportPackageDescription supplier = resolveDynamicImport(resolverImports[j], requestedPackage);
+ if (supplier != null)
+ return supplier;
+ }
+ // look for packages added dynamically
+ ImportPackageSpecification[] addedDynamicImports = importingBundle.getAddedDynamicImportPackages();
+ for (ImportPackageSpecification addedDynamicImport : addedDynamicImports) {
+ ResolverImport newImport = new ResolverImport(rb, addedDynamicImport);
+ ExportPackageDescription supplier = resolveDynamicImport(newImport, requestedPackage);
+ if (supplier != null)
+ return supplier;
+ }
+
+ if (DEBUG || DEBUG_IMPORTS)
+ ResolverImpl.log("Failed to resolve dynamic import: " + requestedPackage); //$NON-NLS-1$
+ return null; // Couldn't resolve the import, so return null
+ } finally {
+ hook = null;
+ }
+ }
+
+ private void addStateWire(BundleDescription importingBundle, VersionConstraint requirement, BundleDescription capabilityHost, ExportPackageDescription capability) {
+ Map<String, List<StateWire>> wires = ((BundleDescriptionImpl) importingBundle).getWires();
+ List<StateWire> imports = wires.get(BundleRevision.PACKAGE_NAMESPACE);
+ if (imports == null) {
+ imports = new ArrayList<StateWire>();
+ wires.put(BundleRevision.PACKAGE_NAMESPACE, imports);
+ }
+ imports.add(newStateWire(importingBundle, requirement, capabilityHost, capability));
+ }
+
+ private ExportPackageDescription resolveDynamicImport(ResolverImport dynamicImport, String requestedPackage) {
+ String importName = dynamicImport.getName();
+ // If the import uses a wildcard, then temporarily replace this with the requested package
+ if (importName.equals("*") || //$NON-NLS-1$
+ (importName.endsWith(".*") && requestedPackage.startsWith(importName.substring(0, importName.length() - 1)))) { //$NON-NLS-1$
+ dynamicImport.setName(requestedPackage);
+ }
+ try {
+ // Resolve the import
+ if (!requestedPackage.equals(dynamicImport.getName()))
+ return null;
+
+ if (resolveImport(dynamicImport, new ArrayList<ResolverBundle>())) {
+ // populate the grouping checker with current imports
+ groupingChecker.populateRoots(dynamicImport.getBundle());
+ while (dynamicImport.getSelectedSupplier() != null) {
+ if (groupingChecker.isDynamicConsistent(dynamicImport.getBundle(), (ResolverExport) dynamicImport.getSelectedSupplier()) != null) {
+ dynamicImport.selectNextSupplier(); // not consistent; try the next
+ } else {
+ // If the import resolved then return it's matching export
+ if (DEBUG_IMPORTS)
+ ResolverImpl.log("Resolved dynamic import: " + dynamicImport.getBundle() + ":" + dynamicImport.getName() + " -> " + ((ResolverExport) dynamicImport.getSelectedSupplier()).getExporter() + ":" + requestedPackage); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+ // now that we have an export to wire to; populate the roots for that package for the bundle
+ ResolverExport export = (ResolverExport) dynamicImport.getSelectedSupplier();
+ groupingChecker.populateRoots(dynamicImport.getBundle(), export);
+
+ ExportPackageDescription supplier = export.getExportPackageDescription();
+ if (supplier != null)
+ addStateWire(dynamicImport.getBundleDescription(), dynamicImport.getVersionConstraint(), supplier.getExporter(), supplier);
+ return supplier;
+ }
+ }
+ dynamicImport.clearPossibleSuppliers();
+ }
+ } finally {
+ // If it is a wildcard import then clear the wire, so other
+ // exported packages can be found for it
+ if (importName.endsWith("*")) //$NON-NLS-1$
+ dynamicImport.clearPossibleSuppliers();
+ // Reset the import package name
+ dynamicImport.setName(null);
+ }
+ return null;
+ }
+
+ public void bundleAdded(BundleDescription bundle) {
+ if (!initialized)
+ return;
+
+ if (bundleMapping.get(bundle) != null)
+ return; // this description already exists in the resolver
+ ResolverBundle rb = new ResolverBundle(bundle, this);
+ bundleMapping.put(bundle, rb);
+ unresolvedBundles.add(rb);
+ resolverExports.put(rb.getExportPackages());
+ resolverBundles.put(rb.getName(), rb);
+ addGenerics(rb.getGenericCapabilities());
+ if (hook != null && rb.isFragment()) {
+ attachFragment0(rb);
+ }
+ }
+
+ public void bundleRemoved(BundleDescription bundle, boolean pending) {
+ ResolverBundle rb = initialized ? (ResolverBundle) bundleMapping.get(bundle) : null;
+ if (rb != null)
+ rb.setUninstalled();
+ internalBundleRemoved(bundle, pending);
+ }
+
+ private void internalBundleRemoved(BundleDescription bundle, boolean pending) {
+ // check if there are any dependants
+ if (pending)
+ removalPending.put(new Long(bundle.getBundleId()), bundle);
+ if (!initialized)
+ return;
+ ResolverBundle rb = bundleMapping.get(bundle);
+ if (rb == null)
+ return;
+
+ if (!pending) {
+ bundleMapping.remove(bundle);
+ groupingChecker.clear(rb);
+ }
+ if (!pending || !bundle.isResolved()) {
+ resolverExports.remove(rb.getExportPackages());
+ resolverBundles.remove(rb);
+ removeGenerics(rb.getGenericCapabilities());
+ }
+ unresolvedBundles.remove(rb);
+ }
+
+ private void unresolveBundle(ResolverBundle bundle, boolean removed) {
+ if (bundle == null)
+ return;
+ // check the removed list if unresolving then remove from the removed list
+ List<BundleDescription> removedBundles = removalPending.remove(new Long(bundle.getBundleDescription().getBundleId()));
+ for (BundleDescription removedDesc : removedBundles) {
+ ResolverBundle re = bundleMapping.get(removedDesc);
+ unresolveBundle(re, true);
+ state.removeBundleComplete(removedDesc);
+ resolverExports.remove(re.getExportPackages());
+ resolverBundles.remove(re);
+ removeGenerics(re.getGenericCapabilities());
+ bundleMapping.remove(removedDesc);
+ groupingChecker.clear(re);
+ // the bundle is removed
+ if (removedDesc == bundle.getBundleDescription())
+ removed = true;
+ }
+
+ if (!bundle.getBundleDescription().isResolved() && !developmentMode)
+ return;
+ CompositeResolveHelperRegistry currentLinks = compositeHelpers;
+ if (currentLinks != null) {
+ CompositeResolveHelper helper = currentLinks.getCompositeResolveHelper(bundle.getBundleDescription());
+ if (helper != null)
+ helper.giveExports(null);
+ }
+ // if not removed then add to the list of unresolvedBundles,
+ // passing false for devmode because we need all fragments detached
+ setBundleUnresolved(bundle, removed, false);
+ // Get bundles dependent on 'bundle'
+ BundleDescription[] dependents = bundle.getBundleDescription().getDependents();
+ state.resolveBundle(bundle.getBundleDescription(), false, null, null, null, null, null, null, null, null);
+ // Unresolve dependents of 'bundle'
+ for (int i = 0; i < dependents.length; i++)
+ unresolveBundle(bundleMapping.get(dependents[i]), false);
+ }
+
+ public void bundleUpdated(BundleDescription newDescription, BundleDescription existingDescription, boolean pending) {
+ internalBundleRemoved(existingDescription, pending);
+ bundleAdded(newDescription);
+ }
+
+ public void flush() {
+ resolverExports = null;
+ resolverBundles = null;
+ resolverGenerics = null;
+ unresolvedBundles = null;
+ bundleMapping = null;
+ List<BundleDescription> removed = removalPending.getAllValues();
+ for (BundleDescription removedDesc : removed)
+ state.removeBundleComplete(removedDesc);
+ removalPending.clear();
+ initialized = false;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public void setState(State newState) {
+ if (this.state != null) {
+ throw new IllegalStateException("Cannot change the State of a Resolver"); //$NON-NLS-1$
+ }
+ state = newState;
+ flush();
+ }
+
+ private void setDebugOptions() {
+ FrameworkDebugOptions options = null; //FrameworkDebugOptions.getDefault();
+ // may be null if debugging is not enabled
+ if (options == null)
+ return;
+ DEBUG = options.getBooleanOption(OPTION_DEBUG, false);
+ DEBUG_WIRING = options.getBooleanOption(OPTION_WIRING, false);
+ DEBUG_IMPORTS = options.getBooleanOption(OPTION_IMPORTS, false);
+ DEBUG_REQUIRES = options.getBooleanOption(OPTION_REQUIRES, false);
+ DEBUG_GENERICS = options.getBooleanOption(OPTION_GENERICS, false);
+ DEBUG_USES = options.getBooleanOption(OPTION_USES, false);
+ DEBUG_CYCLES = options.getBooleanOption(OPTION_CYCLES, false);
+ }
+
+ // LOGGING METHODS
+ private void printWirings() {
+ ResolverImpl.log("****** Result Wirings ******"); //$NON-NLS-1$
+ List<ResolverBundle> bundles = resolverBundles.getAllValues();
+ for (ResolverBundle rb : bundles) {
+ if (rb.getBundleDescription().isResolved()) {
+ continue;
+ }
+ ResolverImpl.log(" * WIRING for " + rb); //$NON-NLS-1$
+ // Require bundles
+ BundleConstraint[] requireBundles = rb.getRequires();
+ if (requireBundles.length == 0) {
+ ResolverImpl.log(" (r) no requires"); //$NON-NLS-1$
+ } else {
+ for (int i = 0; i < requireBundles.length; i++) {
+ if (requireBundles[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (r) " + rb.getBundleDescription() + " -> NULL!!!"); //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ ResolverImpl.log(" (r) " + rb.getBundleDescription() + " -> " + requireBundles[i].getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ // Hosts
+ BundleConstraint hostSpec = rb.getHost();
+ if (hostSpec != null) {
+ VersionSupplier[] hosts = hostSpec.getPossibleSuppliers();
+ if (hosts != null)
+ for (int i = 0; i < hosts.length; i++) {
+ ResolverImpl.log(" (h) " + rb.getBundleDescription() + " -> " + hosts[i].getBundleDescription()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ // Imports
+ ResolverImport[] imports = rb.getImportPackages();
+ if (imports.length == 0) {
+ ResolverImpl.log(" (w) no imports"); //$NON-NLS-1$
+ continue;
+ }
+ for (int i = 0; i < imports.length; i++) {
+ if (imports[i].isDynamic() && imports[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> DYNAMIC"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else if (imports[i].isOptional() && imports[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> OPTIONAL (could not be wired)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else if (imports[i].getSelectedSupplier() == null) {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> NULL!!!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else {
+ ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ ((ResolverExport) imports[i].getSelectedSupplier()).getExporter() + ":" + imports[i].getSelectedSupplier().getName()); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ static void log(String message) {
+ Debug.println(message);
+ }
+
+ VersionHashMap<ResolverExport> getResolverExports() {
+ return resolverExports;
+ }
+
+ public void setSelectionPolicy(Comparator<BaseDescription> selectionPolicy) {
+ this.selectionPolicy = selectionPolicy;
+ }
+
+ public Comparator<BaseDescription> getSelectionPolicy() {
+ return selectionPolicy;
+ }
+
+ public void setCompositeResolveHelperRegistry(CompositeResolveHelperRegistry compositeHelpers) {
+ this.compositeHelpers = compositeHelpers;
+ }
+
+ CompositeResolveHelperRegistry getCompositeHelpers() {
+ return compositeHelpers;
+ }
+
+ private void reorderGenerics() {
+ for (VersionHashMap<GenericCapability> namespace : resolverGenerics.values())
+ namespace.reorder();
+ }
+
+ void removeGenerics(GenericCapability[] generics) {
+ for (GenericCapability capability : generics) {
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(capability.getGenericDescription().getType());
+ if (namespace != null)
+ namespace.remove(capability);
+ }
+ }
+
+ void addGenerics(GenericCapability[] generics) {
+ for (GenericCapability capability : generics) {
+ if (!capability.isEffective())
+ continue;
+ String type = capability.getGenericDescription().getType();
+ VersionHashMap<GenericCapability> namespace = resolverGenerics.get(type);
+ if (namespace == null) {
+ namespace = new VersionHashMap<GenericCapability>(this);
+ resolverGenerics.put(type, namespace);
+ }
+ namespace.put(capability.getName(), capability);
+ }
+ }
+}

Back to the top