KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > internal > module > ResolverImpl


1 /*******************************************************************************
2  * Copyright (c) 2004, 2007 IBM Corporation and others. All rights reserved.
3  * This program and the accompanying materials are made available under the
4  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/epl-v10.html
6  *
7  * Contributors: IBM Corporation - initial API and implementation
8  ******************************************************************************/

9 package org.eclipse.osgi.internal.module;
10
11 import java.util.*;
12 import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
13 import org.eclipse.osgi.framework.debug.Debug;
14 import org.eclipse.osgi.framework.debug.FrameworkDebugOptions;
15 import org.eclipse.osgi.internal.module.GroupingChecker.PackageRoots;
16 import org.eclipse.osgi.internal.resolver.BundleDescriptionImpl;
17 import org.eclipse.osgi.internal.resolver.ExportPackageDescriptionImpl;
18 import org.eclipse.osgi.service.resolver.*;
19 import org.eclipse.osgi.util.ManifestElement;
20 import org.osgi.framework.*;
21
22 public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver {
23     // Debug fields
24
private static final String JavaDoc RESOLVER = FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + "/resolver"; //$NON-NLS-1$
25
private static final String JavaDoc OPTION_DEBUG = RESOLVER + "/debug";//$NON-NLS-1$
26
private static final String JavaDoc OPTION_WIRING = RESOLVER + "/wiring"; //$NON-NLS-1$
27
private static final String JavaDoc OPTION_IMPORTS = RESOLVER + "/imports"; //$NON-NLS-1$
28
private static final String JavaDoc OPTION_REQUIRES = RESOLVER + "/requires"; //$NON-NLS-1$
29
private static final String JavaDoc OPTION_GENERICS = RESOLVER + "/generics"; //$NON-NLS-1$
30
private static final String JavaDoc OPTION_GROUPING = RESOLVER + "/grouping"; //$NON-NLS-1$
31
private static final String JavaDoc OPTION_CYCLES = RESOLVER + "/cycles"; //$NON-NLS-1$
32
public static boolean DEBUG = false;
33     public static boolean DEBUG_WIRING = false;
34     public static boolean DEBUG_IMPORTS = false;
35     public static boolean DEBUG_REQUIRES = false;
36     public static boolean DEBUG_GENERICS = false;
37     public static boolean DEBUG_GROUPING = false;
38     public static boolean DEBUG_CYCLES = false;
39     private static int MAX_MULTIPLE_SUPPLIERS_MERGE = 10;
40     private static long MAX_COMBINATIONS = 1000000;
41
42     private static String JavaDoc[][] CURRENT_EES;
43
44     // The State associated with this resolver
45
private State state;
46     // Used to check permissions for import/export, provide/require, host/fragment
47
private PermissionChecker permissionChecker;
48     // Set of bundles that are pending removal
49
private MappedList removalPending = new MappedList();
50     // Indicates whether this resolver has been initialized
51
private boolean initialized = false;
52
53     // Repository for exports
54
private VersionHashMap resolverExports = null;
55     // Repository for bundles
56
private VersionHashMap resolverBundles = null;
57     // Repository for generics
58
private VersionHashMap resolverGenerics = null;
59     // List of unresolved bundles
60
private ArrayList unresolvedBundles = null; // TODO make this a Set
61
// Keys are BundleDescriptions, values are ResolverBundles
62
private HashMap bundleMapping = null;
63     private GroupingChecker groupingChecker;
64     private Comparator selectionPolicy;
65     private boolean developmentMode = false;
66
67     public ResolverImpl(BundleContext context, boolean checkPermissions) {
68         this.permissionChecker = new PermissionChecker(context, checkPermissions, this);
69     }
70
71     PermissionChecker getPermissionChecker() {
72         return permissionChecker;
73     }
74
75     // Initializes the resolver
76
private void initialize() {
77         resolverExports = new VersionHashMap(this);
78         resolverBundles = new VersionHashMap(this);
79         resolverGenerics = new VersionHashMap(this);
80         unresolvedBundles = new ArrayList();
81         bundleMapping = new HashMap();
82         BundleDescription[] bundles = state.getBundles();
83         groupingChecker = new GroupingChecker();
84
85         ArrayList fragmentBundles = new ArrayList();
86         // Add each bundle to the resolver's internal state
87
for (int i = 0; i < bundles.length; i++)
88             initResolverBundle(bundles[i], fragmentBundles, false);
89         // Add each removal pending bundle to the resolver's internal state
90
Object JavaDoc[] removedBundles = removalPending.getAllValues();
91         for (int i = 0; i < removedBundles.length; i++)
92             initResolverBundle((BundleDescription) removedBundles[i], fragmentBundles, true);
93         // Iterate over the resolved fragments and attach them to their hosts
94
for (Iterator iter = fragmentBundles.iterator(); iter.hasNext();) {
95             ResolverBundle fragment = (ResolverBundle) iter.next();
96             BundleDescription[] hosts = ((HostSpecification) fragment.getHost().getVersionConstraint()).getHosts();
97             for (int i = 0; i < hosts.length; i++) {
98                 ResolverBundle host = (ResolverBundle) bundleMapping.get(hosts[i]);
99                 if (host != null)
100                     // Do not add fragment exports here because they would have been added by the host above.
101
host.attachFragment(fragment, false);
102             }
103         }
104         rewireBundles(); // Reconstruct wirings
105
setDebugOptions();
106         initialized = true;
107     }
108
109     private void initResolverBundle(BundleDescription bundleDesc, ArrayList fragmentBundles, boolean pending) {
110         ResolverBundle bundle = new ResolverBundle(bundleDesc, this);
111         bundleMapping.put(bundleDesc, bundle);
112         if (!pending || bundleDesc.isResolved()) {
113             resolverExports.put(bundle.getExportPackages());
114             resolverBundles.put(bundle.getName(), bundle);
115             resolverGenerics.put(bundle.getGenericCapabilities());
116         }
117         if (bundleDesc.isResolved()) {
118             bundle.setState(ResolverBundle.RESOLVED);
119             if (bundleDesc.getHost() != null)
120                 fragmentBundles.add(bundle);
121         } else {
122             if (!pending)
123                 unresolvedBundles.add(bundle);
124         }
125     }
126
127     // Re-wire previously resolved bundles
128
private void rewireBundles() {
129         ArrayList visited = new ArrayList(bundleMapping.size());
130         for (Iterator iter = bundleMapping.values().iterator(); iter.hasNext();) {
131             ResolverBundle rb = (ResolverBundle) iter.next();
132             if (!rb.getBundle().isResolved() || rb.isFragment())
133                 continue;
134             rewireBundle(rb, visited);
135         }
136     }
137
138     private void rewireBundle(ResolverBundle rb, ArrayList visited) {
139         if (visited.contains(rb))
140             return;
141         visited.add(rb);
142         // Wire requires to bundles
143
BundleConstraint[] requires = rb.getRequires();
144         for (int i = 0; i < requires.length; i++) {
145             rewireRequire(requires[i], visited);
146         }
147         // Wire imports to exports
148
ResolverImport[] imports = rb.getImportPackages();
149         for (int i = 0; i < imports.length; i++) {
150             rewireImport(imports[i], visited);
151         }
152         // Wire generics
153
GenericConstraint[] genericRequires = rb.getGenericRequires();
154         for (int i = 0; i < genericRequires.length; i++)
155             rewireGeneric(genericRequires[i], visited);
156     }
157
158     private void rewireGeneric(GenericConstraint constraint, ArrayList visited) {
159         if (constraint.getMatchingCapabilities() != null)
160             return;
161         GenericDescription[] suppliers = ((GenericSpecification) constraint.getVersionConstraint()).getSuppliers();
162         if (suppliers == null)
163             return;
164         Object JavaDoc[] matches = resolverGenerics.get(constraint.getName());
165         for (int i = 0; i < matches.length; i++) {
166             GenericCapability match = (GenericCapability) matches[i];
167             for (int j = 0; j < suppliers.length; j++)
168                 if (match.getBaseDescription() == suppliers[j])
169                     constraint.setMatchingCapability(match);
170         }
171         GenericCapability[] matchingCapabilities = constraint.getMatchingCapabilities();
172         if (matchingCapabilities != null)
173             for (int i = 0; i < matchingCapabilities.length; i++)
174                 rewireBundle(matchingCapabilities[i].getResolverBundle(), visited);
175     }
176
177     private void rewireRequire(BundleConstraint req, ArrayList visited) {
178         if (req.getSelectedSupplier() != null)
179             return;
180         ResolverBundle matchingBundle = (ResolverBundle) bundleMapping.get(req.getVersionConstraint().getSupplier());
181         req.addPossibleSupplier(matchingBundle);
182         if (matchingBundle == null && !req.isOptional()) {
183             System.err.println("Could not find matching bundle for " + req.getVersionConstraint()); //$NON-NLS-1$
184
// TODO log error!!
185
}
186         if (matchingBundle != null) {
187             rewireBundle(matchingBundle, visited);
188         }
189     }
190
191     private void rewireImport(ResolverImport imp, ArrayList visited) {
192         if (imp.isDynamic() || imp.getSelectedSupplier() != null)
193             return;
194         // Re-wire 'imp'
195
ResolverExport matchingExport = null;
196         ExportPackageDescription importSupplier = (ExportPackageDescription) imp.getVersionConstraint().getSupplier();
197         ResolverBundle exporter = importSupplier == null ? null : (ResolverBundle) bundleMapping.get(importSupplier.getExporter());
198         Object JavaDoc[] matches = resolverExports.get(imp.getName());
199         for (int j = 0; j < matches.length; j++) {
200             ResolverExport export = (ResolverExport) matches[j];
201             if (export.getExporter() == exporter && importSupplier == export.getExportPackageDescription()) {
202                 matchingExport = export;
203                 break;
204             }
205         }
206         imp.addPossibleSupplier(matchingExport);
207         // Check if we wired to a reprovided package (in which case the ResolverExport doesn't exist)
208
if (matchingExport == null && exporter != null) {
209             ResolverExport reprovidedExport = new ResolverExport(exporter, importSupplier);
210             if (exporter.getExport(imp.getName()) == null) {
211                 exporter.addExport(reprovidedExport);
212                 resolverExports.put(reprovidedExport.getName(), reprovidedExport);
213             }
214             imp.addPossibleSupplier(reprovidedExport);
215         }
216         // If we still have a null wire and it's not optional, then we have an error
217
if (imp.getSelectedSupplier() == null && !imp.isOptional()) {
218             System.err.println("Could not find matching export for " + imp.getVersionConstraint()); //$NON-NLS-1$
219
// TODO log error!!
220
}
221         if (imp.getSelectedSupplier() != null) {
222             rewireBundle(((ResolverExport) imp.getSelectedSupplier()).getExporter(), visited);
223         }
224     }
225
226     // Checks a bundle to make sure it is valid. If this method returns false for
227
// a given bundle, then that bundle will not even be considered for resolution
228
private boolean isResolvable(BundleDescription bundle, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
229         // check if this is a rejected singleton
230
if (rejectedSingletons.contains(bundle))
231             return false;
232         // Check for singletons
233
if (bundle.isSingleton()) {
234             Object JavaDoc[] sameName = resolverBundles.get(bundle.getName());
235             if (sameName.length > 1) // Need to check if one is already resolved
236
for (int i = 0; i < sameName.length; i++) {
237                     if (sameName[i] == bundle || !((ResolverBundle) sameName[i]).getBundle().isSingleton())
238                         continue; // Ignore the bundle we are resolving and non-singletons
239
if (((ResolverBundle) sameName[i]).getBundle().isResolved()) {
240                         rejectedSingletons.add(bundle);
241                         return false; // Must fail since there is already a resolved bundle
242
}
243                 }
244         }
245         // check the required execution environment
246
String JavaDoc[] ees = bundle.getExecutionEnvironments();
247         boolean matchedEE = ees.length == 0;
248         if (!matchedEE)
249             for (int i = 0; i < ees.length && !matchedEE; i++)
250                 for (int j = 0; j < CURRENT_EES.length && !matchedEE; j++)
251                     for (int k = 0; k < CURRENT_EES[j].length && !matchedEE; k++)
252                         if (CURRENT_EES[j][k].equals(ees[i])) {
253                             ((BundleDescriptionImpl) bundle).setEquinoxEE(j);
254                             matchedEE = true;
255                         }
256         if (!matchedEE) {
257             StringBuffer JavaDoc bundleEE = new StringBuffer JavaDoc(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT.length() + 20);
258             bundleEE.append(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT).append(": "); //$NON-NLS-1$
259
for (int i = 0; i < ees.length; i++) {
260                 if (i > 0)
261                     bundleEE.append(","); //$NON-NLS-1$
262
bundleEE.append(ees[i]);
263             }
264             state.addResolverError(bundle, ResolverError.MISSING_EXECUTION_ENVIRONMENT, bundleEE.toString(), null);
265             return false;
266         }
267
268         // check the platform filter
269
String JavaDoc platformFilter = bundle.getPlatformFilter();
270         if (platformFilter == null)
271             return true;
272         if (platformProperties == null)
273             return false;
274         try {
275             Filter filter = FrameworkUtil.createFilter(platformFilter);
276             for (int i = 0; i < platformProperties.length; i++)
277                 // using matchCase here in case of duplicate case invarient keys (bug 180817)
278
if (filter.matchCase(platformProperties[i]))
279                     return true;
280         } catch (InvalidSyntaxException e) {
281             // return false below
282
}
283         state.addResolverError(bundle, ResolverError.PLATFORM_FILTER, platformFilter, null);
284         return false;
285     }
286
287     // Attach fragment to its host
288
private void attachFragment(ResolverBundle bundle, ArrayList rejectedSingletons) {
289         if (!bundle.isFragment() || !bundle.isResolvable() || rejectedSingletons.contains(bundle.getBundle()))
290             return;
291         // no need to select singletons now; it will be done when we select the rest of the singleton bundles (bug 152042)
292
// find all available hosts to attach to.
293
boolean foundMatch = false;
294         BundleConstraint hostConstraint = bundle.getHost();
295         Object JavaDoc[] hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
296         for (int i = 0; i < hosts.length; i++)
297             if (((ResolverBundle) hosts[i]).isResolvable() && hostConstraint.isSatisfiedBy((ResolverBundle) hosts[i])) {
298                 foundMatch = true;
299                 resolverExports.put(((ResolverBundle) hosts[i]).attachFragment(bundle, true));
300             }
301         if (!foundMatch)
302             state.addResolverError(bundle.getBundle(), ResolverError.MISSING_FRAGMENT_HOST, bundle.getHost().getVersionConstraint().toString(), bundle.getHost().getVersionConstraint());
303     }
304
305     public synchronized void resolve(BundleDescription[] reRefresh, Dictionary[] platformProperties) {
306         if (DEBUG)
307             ResolverImpl.log("*** BEGIN RESOLUTION ***"); //$NON-NLS-1$
308
if (state == null)
309             throw new IllegalStateException JavaDoc("RESOLVER_NO_STATE"); //$NON-NLS-1$
310

311         if (!initialized)
312             initialize();
313         developmentMode = platformProperties.length == 0 ? false : org.eclipse.osgi.framework.internal.core.Constants.DEVELOPMENT_MODE.equals(platformProperties[0].get(org.eclipse.osgi.framework.internal.core.Constants.OSGI_RESOLVER_MODE));
314         reRefresh = addDevConstraints(reRefresh);
315         // Unresolve all the supplied bundles and their dependents
316
if (reRefresh != null)
317             for (int i = 0; i < reRefresh.length; i++) {
318                 ResolverBundle rb = (ResolverBundle) bundleMapping.get(reRefresh[i]);
319                 if (rb != null)
320                     unresolveBundle(rb, false);
321             }
322         // reorder exports and bundles after unresolving the bundles
323
resolverExports.reorder();
324         resolverBundles.reorder();
325         resolverGenerics.reorder();
326         // always get the latest EEs
327
getCurrentEEs(platformProperties);
328         // keep a list of rejected singltons
329
ArrayList rejectedSingletons = new ArrayList();
330         boolean resolveOptional = platformProperties.length == 0 ? false : "true".equals(platformProperties[0].get("osgi.resolveOptional")); //$NON-NLS-1$//$NON-NLS-2$
331
ResolverBundle[] currentlyResolved = null;
332         if (resolveOptional) {
333             BundleDescription[] resolvedBundles = state.getResolvedBundles();
334             currentlyResolved = new ResolverBundle[resolvedBundles.length];
335             for (int i = 0; i < resolvedBundles.length; i++)
336                 currentlyResolved[i] = (ResolverBundle) bundleMapping.get(resolvedBundles[i]);
337         }
338         // attempt to resolve all unresolved bundles
339
ResolverBundle[] bundles = (ResolverBundle[]) unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
340         resolveBundles(bundles, platformProperties, rejectedSingletons);
341         if (selectSingletons(bundles, rejectedSingletons)) {
342             // a singleton was unresolved as a result of selecting a different version
343
// try to resolve unresolved bundles again; this will attempt to use the selected singleton
344
bundles = (ResolverBundle[]) unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
345             resolveBundles(bundles, platformProperties, rejectedSingletons);
346         }
347         for (Iterator rejected = rejectedSingletons.iterator(); rejected.hasNext();) {
348             BundleDescription reject = (BundleDescription) rejected.next();
349             BundleDescription sameName = state.getBundle(reject.getSymbolicName(), null);
350             state.addResolverError(reject, ResolverError.SINGLETON_SELECTION, sameName.toString(), null);
351         }
352         if (resolveOptional)
353             resolveOptionalConstraints(currentlyResolved);
354         if (DEBUG)
355             ResolverImpl.log("*** END RESOLUTION ***"); //$NON-NLS-1$
356
}
357
358     private BundleDescription[] addDevConstraints(BundleDescription[] reRefresh) {
359         if (!developmentMode)
360             return reRefresh; // we don't care about this unless we are in development mode
361
// when in develoment mode we need to reRefresh hosts of unresolved fragments that add new constraints
362
// and reRefresh and unresolved bundles that have dependents
363
HashSet additionalRefresh = new HashSet();
364         ResolverBundle[] unresolved = (ResolverBundle[]) unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
365         for (int i = 0; i < unresolved.length; i++) {
366             addUnresolvedWithDependents(unresolved[i], additionalRefresh);
367             addHostsFromFragmentConstraints(unresolved[i], additionalRefresh);
368         }
369         if (additionalRefresh.size() == 0)
370             return reRefresh; // no new bundles found to refresh
371
// add the original reRefresh bundles to the set
372
if (reRefresh != null)
373             for (int i = 0; i < reRefresh.length; i++)
374                 additionalRefresh.add(reRefresh[i]);
375         return (BundleDescription[]) additionalRefresh.toArray(new BundleDescription[additionalRefresh.size()]);
376     }
377
378     private void addUnresolvedWithDependents(ResolverBundle unresolved, HashSet additionalRefresh) {
379         BundleDescription[] dependents = unresolved.getBundle().getDependents();
380         if (dependents.length > 0)
381             additionalRefresh.add(unresolved.getBundle());
382     }
383
384     private void addHostsFromFragmentConstraints(ResolverBundle unresolved, Set additionalRefresh) {
385         if (!unresolved.isFragment())
386             return;
387         ImportPackageSpecification[] newImports = unresolved.getBundle().getImportPackages();
388         BundleSpecification[] newRequires = unresolved.getBundle().getRequiredBundles();
389         if (newImports.length == 0 && newRequires.length == 0)
390             return; // the fragment does not have its own constraints
391
BundleConstraint hostConstraint = unresolved.getHost();
392         Object JavaDoc[] hosts = resolverBundles.get(hostConstraint.getVersionConstraint().getName());
393         for (int j = 0; j < hosts.length; j++)
394             if (hostConstraint.isSatisfiedBy((ResolverBundle) hosts[j]) && ((ResolverBundle) hosts[j]).isResolved())
395                 // we found a host that is resolved;
396
// add it to the set of bundle to refresh so we can ensure this fragment is allowed to resolve
397
additionalRefresh.add(((ResolverBundle) hosts[j]).getBundle());
398
399     }
400
401     private void resolveOptionalConstraints(ResolverBundle[] bundles) {
402         for (int i = 0; i < bundles.length; i++) {
403             if (bundles[i] != null)
404                 resolveOptionalConstraints(bundles[i]);
405         }
406     }
407
408     // TODO this does not do proper uses constraint verification.
409
private void resolveOptionalConstraints(ResolverBundle bundle) {
410         BundleConstraint[] requires = bundle.getRequires();
411         ArrayList cycle = new ArrayList();
412         boolean resolvedOptional = false;
413         for (int i = 0; i < requires.length; i++)
414             if (requires[i].isOptional() && requires[i].getSelectedSupplier() == null) {
415                 cycle.clear();
416                 resolveRequire(requires[i], cycle);
417                 if (requires[i].getSelectedSupplier() != null)
418                     resolvedOptional = true;
419             }
420         ResolverImport[] imports = bundle.getImportPackages();
421         for (int i = 0; i < imports.length; i++)
422             if (imports[i].isOptional() && imports[i].getSelectedSupplier() == null) {
423                 cycle.clear();
424                 resolveImport(imports[i], cycle);
425                 if (imports[i].getSelectedSupplier() != null)
426                     resolvedOptional = true;
427             }
428         if (resolvedOptional) {
429             state.resolveBundle(bundle.getBundle(), false, null, null, null, null);
430             stateResolveConstraints(bundle);
431             stateResolveBundle(bundle);
432         }
433     }
434
435     private void getCurrentEEs(Dictionary[] platformProperties) {
436         CURRENT_EES = new String JavaDoc[platformProperties.length][];
437         for (int i = 0; i < platformProperties.length; i++) {
438             String JavaDoc eeSpecs = (String JavaDoc) platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
439             CURRENT_EES[i] = ManifestElement.getArrayFromList(eeSpecs, ","); //$NON-NLS-1$
440
}
441     }
442
443     private void resolveBundles(ResolverBundle[] bundles, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
444         // First check that all the meta-data is valid for each unresolved bundle
445
// This will reset the resolvable flag for each bundle
446
for (int i = 0; i < bundles.length; i++) {
447             state.removeResolverErrors(bundles[i].getBundle());
448             // if in development mode then make all bundles resolvable
449
// we still want to call isResolvable here to populate any possible ResolverErrors for the bundle
450
bundles[i].setResolvable(isResolvable(bundles[i].getBundle(), platformProperties, rejectedSingletons) || developmentMode);
451             bundles[i].clearRefs();
452         }
453         resolveBundles0(bundles, platformProperties, rejectedSingletons);
454         if (DEBUG_WIRING)
455             printWirings();
456         // set the resolved status of the bundles in the State
457
stateResolveBundles(bundles);
458     }
459
460     private void resolveBundles0(ResolverBundle[] bundles, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
461         if (developmentMode)
462             // need to sort bundles to keep consistent order for fragment attachment (bug 174930)
463
Arrays.sort(bundles);
464         // First attach all fragments to the matching hosts
465
for (int i = 0; i < bundles.length; i++)
466             attachFragment(bundles[i], rejectedSingletons);
467
468         // Lists of cyclic dependencies recording during resolving
469
ArrayList cycle = new ArrayList(1); // start small
470
// Attempt to resolve all unresolved bundles
471
for (int i = 0; i < bundles.length; i++) {
472             if (DEBUG)
473                 ResolverImpl.log("** RESOLVING " + bundles[i] + " **"); //$NON-NLS-1$ //$NON-NLS-2$
474
cycle.clear();
475             resolveBundle(bundles[i], cycle);
476             // Check for any bundles involved in a cycle.
477
// if any bundles in the cycle are not resolved then we need to resolve the resolvable ones
478
checkCycle(cycle);
479         }
480         // Resolve all fragments that are still attached to at least one host.
481
if (unresolvedBundles.size() > 0) {
482             ResolverBundle[] unresolved = (ResolverBundle[]) unresolvedBundles.toArray(new ResolverBundle[unresolvedBundles.size()]);
483             for (int i = 0; i < unresolved.length; i++)
484                 resolveFragment(unresolved[i]);
485         }
486         checkUsesConstraints(bundles, platformProperties, rejectedSingletons);
487     }
488
489     private void checkUsesConstraints(ResolverBundle[] bundles, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
490         ArrayList conflictingConstraints = findBestCombination(bundles);
491         Set conflictedBundles = null;
492         if (conflictingConstraints != null) {
493             for (Iterator conflicts = conflictingConstraints.iterator(); conflicts.hasNext();) {
494                 ResolverConstraint conflict = (ResolverConstraint) conflicts.next();
495                 if (conflict.isOptional()) {
496                     conflict.clearPossibleSuppliers();
497                     continue;
498                 }
499                 conflictedBundles = new HashSet(conflictingConstraints.size());
500                 ResolverBundle conflictedBundle;
501                 if (conflict.isFromFragment())
502                     conflictedBundle = (ResolverBundle) bundleMapping.get(conflict.getVersionConstraint().getBundle());
503                 else
504                     conflictedBundle = conflict.getBundle();
505                 if (conflictedBundle != null) {
506                     conflictedBundles.add(conflictedBundle);
507                     int type = conflict instanceof ResolverImport ? ResolverError.IMPORT_PACKAGE_USES_CONFLICT : ResolverError.REQUIRE_BUNDLE_USES_CONFLICT;
508                     state.addResolverError(conflictedBundle.getBundle(), type, conflict.getVersionConstraint().toString(), conflict.getVersionConstraint());
509                     conflictedBundle.setResolvable(false);
510                     conflictedBundle.clearRefs();
511                     setBundleUnresolved(conflictedBundle, false, developmentMode);
512                 }
513             }
514             if (conflictedBundles != null && conflictedBundles.size() > 0) {
515                 ArrayList remainingUnresolved = new ArrayList();
516                 for (int i = 0; i < bundles.length; i++) {
517                     if (!conflictedBundles.contains(bundles[i])) {
518                         setBundleUnresolved(bundles[i], false, developmentMode);
519                         remainingUnresolved.add(bundles[i]);
520                     }
521                 }
522                 resolveBundles0((ResolverBundle[]) remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties, rejectedSingletons);
523             }
524         }
525     }
526
527     private ArrayList findBestCombination(ResolverBundle[] bundles) {
528         HashSet bundleConstraints = new HashSet();
529         HashSet packageConstraints = new HashSet();
530         // first tryout the initial selections
531
ArrayList initialConflicts = getConflicts(bundles, packageConstraints, bundleConstraints);
532         if (initialConflicts == null) {
533             groupingChecker.clear();
534             return null; // the first selected have no conflicts; return without iterating over all combinations
535
}
536         ResolverConstraint[][] multipleSuppliers = getMultipleSuppliers(bundles, packageConstraints, bundleConstraints);
537         ArrayList conflicts = null;
538         if (multipleSuppliers.length > 0 && getNumCombinations(multipleSuppliers) < MAX_COMBINATIONS) {
539             int[] bestCombination = new int[multipleSuppliers.length];
540             conflicts = findBestCombination(bundles, multipleSuppliers, bestCombination, initialConflicts);
541             for (int i = 0; i < bestCombination.length; i++) {
542                 for (int j = 0; j < multipleSuppliers[i].length; j++)
543                     multipleSuppliers[i][j].setSelectedSupplier(bestCombination[i]);
544             }
545         } else {
546             // either there are no multiple suppliers or the multiple suppliers list is way to big to process in a timely manner.
547
conflicts = initialConflicts;
548         }
549         // do not need to keep uses data in memory
550
groupingChecker.clear();
551         return conflicts;
552     }
553
554     private long getNumCombinations(ResolverConstraint[][] multipleSuppliers) {
555         if (multipleSuppliers == null || multipleSuppliers.length == 0)
556             return 0;
557         long numCombinations = multipleSuppliers[0][0].getNumPossibleSuppliers();
558         for (int i = 1; i < multipleSuppliers.length; i++)
559             if (multipleSuppliers[i].length > 0)
560                 numCombinations *= multipleSuppliers[i][0].getNumPossibleSuppliers();
561         return numCombinations;
562     }
563
564     private int[] getCombination(ResolverConstraint[][] multipleSuppliers, int[] combination) {
565         for (int i = 0; i < combination.length; i++)
566             combination[i] = multipleSuppliers[i][0].getSelectedSupplierIndex();
567         return combination;
568     }
569
570     private ArrayList findBestCombination(ResolverBundle[] bundles, ResolverConstraint[][] multipleSuppliers, int[] bestCombination, ArrayList bestConflicts) {
571         // now iterate over every possible combination until either zero conflicts are found
572
// or we have run out of combinations
573
// if all combinations are tried then return the combination with the lowest number of conflicts
574
int bestConflictCount = getConflictCount(bestConflicts);
575         ResolverBundle[] bestConflictBundles = getConflictedBundles(bestConflicts);
576         while (bestConflictCount != 0 && getNextCombination(multipleSuppliers)) {
577             if (DEBUG_GROUPING)
578                 printCombination(getCombination(multipleSuppliers, new int[multipleSuppliers.length]));
579             // first count the conflicts for the bundles with conflicts from the best combination
580
// this significantly reduces the time it takes to populate the GroupingChecker for cases where
581
// the combination is no better.
582
ArrayList conflicts = getConflicts(bestConflictBundles, null, null);
583             int conflictCount = getConflictCount(conflicts);
584             if (conflictCount >= bestConflictCount)
585                 // no need to test the other bundles;
586
// this combination is no better for the bundles which conflict with the current best combination
587
continue;
588             // this combination improves upon the conflicts for the bundles which conflict with the current best combination;
589
// do an complete conflict count
590
conflicts = getConflicts(bundles, null, null);
591             conflictCount = getConflictCount(conflicts);
592             if (conflictCount < bestConflictCount) {
593                 // this combination is better that the current best combination; save this combination as the current best
594
bestConflictCount = conflictCount;
595                 bestConflicts = conflicts;
596                 getCombination(multipleSuppliers, bestCombination);
597                 bestConflictBundles = getConflictedBundles(bestConflicts);
598             }
599         }
600         return bestConflicts;
601     }
602
603     private void printCombination(int[] curCombination) {
604         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
605         sb.append('[');
606         for (int i = 0; i < curCombination.length; i++) {
607             sb.append(curCombination[i]);
608             if (i < curCombination.length - 1)
609                 sb.append(',');
610         }
611         sb.append(']');
612         System.out.println(sb.toString());
613     }
614
615     private ResolverBundle[] getConflictedBundles(ArrayList bestConflicts) {
616         if (bestConflicts == null)
617             return new ResolverBundle[0];
618         ArrayList conflictedBundles = new ArrayList(bestConflicts.size());
619         for (Iterator iConflicts = bestConflicts.iterator(); iConflicts.hasNext();) {
620             ResolverConstraint constraint = (ResolverConstraint) iConflicts.next();
621             if (!conflictedBundles.contains(constraint.getBundle()))
622                 conflictedBundles.add(constraint.getBundle());
623         }
624         return (ResolverBundle[]) conflictedBundles.toArray(new ResolverBundle[conflictedBundles.size()]);
625     }
626
627     private boolean getNextCombination(ResolverConstraint[][] multipleSuppliers) {
628         int current = 0;
629         while (current < multipleSuppliers.length) {
630             if (multipleSuppliers[current][0].selectNextSupplier()) {
631                 for (int i = 1; i < multipleSuppliers[current].length; i++)
632                     multipleSuppliers[current][i].selectNextSupplier();
633                 return true; // the current slot has a next supplier
634
}
635             for (int i = 0; i < multipleSuppliers[current].length; i++)
636                 multipleSuppliers[current][i].setSelectedSupplier(0); // reset the current slot
637
current++; // move to the next slot
638
}
639         return false;
640     }
641
642     // only count non-optional conflicts
643
private int getConflictCount(ArrayList conflicts) {
644         if (conflicts == null || conflicts.size() == 0)
645             return 0;
646         int result = 0;
647         for (Iterator iConflicts = conflicts.iterator(); iConflicts.hasNext();)
648             if (!((ResolverConstraint) iConflicts.next()).isOptional())
649                 result += 1;
650         return result;
651     }
652
653     private ArrayList getConflicts(ResolverBundle[] bundles, HashSet packageConstraints, HashSet bundleConstraints) {
654         groupingChecker.clear();
655         ArrayList conflicts = null;
656         for (int i = 0; i < bundles.length; i++)
657             conflicts = addConflicts(bundles[i], packageConstraints, bundleConstraints, conflicts);
658         return conflicts;
659     }
660
661     private ArrayList addConflicts(ResolverBundle bundle, HashSet packageConstraints, HashSet bundleConstraints, ArrayList conflicts) {
662         boolean foundConflict = false;
663         BundleConstraint[] requires = bundle.getRequires();
664         for (int i = 0; i < requires.length; i++) {
665             ResolverBundle selectedSupplier = (ResolverBundle) requires[i].getSelectedSupplier();
666             PackageRoots[][] conflict = selectedSupplier == null ? null : groupingChecker.isConsistent(bundle, selectedSupplier);
667             if (conflict != null) {
668                 addConflictNames(conflict, packageConstraints, bundleConstraints);
669                 if (!foundConflict) {
670                     if (conflicts == null)
671                         conflicts = new ArrayList(1);
672                     conflicts.add(requires[i]);
673                     foundConflict = !requires[i].isOptional(); // only record the conflicts upto the first non-optional
674
}
675             }
676         }
677         ResolverImport[] imports = bundle.getImportPackages();
678         for (int i = 0; i < imports.length; i++) {
679             ResolverExport selectedSupplier = (ResolverExport) imports[i].getSelectedSupplier();
680             PackageRoots[][] conflict = selectedSupplier == null ? null : groupingChecker.isConsistent(bundle, selectedSupplier);
681             if (conflict != null) {
682                 addConflictNames(conflict, packageConstraints, bundleConstraints);
683                 if (!foundConflict) {
684                     if (conflicts == null)
685                         conflicts = new ArrayList(1);
686                     conflicts.add(imports[i]);
687                     foundConflict = !imports[i].isOptional(); // only record the conflicts upto the first non-optional
688
}
689             }
690         }
691         return conflicts;
692     }
693
694     // records the conflict names we can use to scope down the list of multiple suppliers
695
private void addConflictNames(PackageRoots[][] conflict, HashSet packageConstraints, HashSet bundleConstraints) {
696         if (packageConstraints == null || bundleConstraints == null)
697             return;
698         for (int i = 0; i < conflict.length; i++) {
699             packageConstraints.add(conflict[i][0].getName());
700             packageConstraints.add(conflict[i][1].getName());
701             ResolverExport[] exports0 = conflict[i][0].getRoots();
702             if (exports0 != null)
703                 for (int j = 0; j < exports0.length; j++) {
704                     ResolverBundle exporter = exports0[j].getExporter();
705                     if (exporter != null && exporter.getName() != null)
706                         bundleConstraints.add(exporter.getName());
707                 }
708             ResolverExport[] exports1 = conflict[i][1].getRoots();
709             if (exports1 != null)
710                 for (int j = 0; j < exports1.length; j++) {
711                     ResolverBundle exporter = exports1[j].getExporter();
712                     if (exporter != null && exporter.getName() != null)
713                         bundleConstraints.add(exporter.getName());
714                 }
715         }
716     }
717
718     // get a list of resolver constraints that have multiple suppliers
719
// a 2 demensional array is used each entry is a list of identical constraints that have identical suppliers.
720
private ResolverConstraint[][] getMultipleSuppliers(ResolverBundle[] bundles, HashSet packageConstraints, HashSet bundleConstraints) {
721         ArrayList multipleImportSupplierList = new ArrayList(1);
722         ArrayList multipleRequireSupplierList = new ArrayList(1);
723         for (int i = 0; i < bundles.length; i++) {
724             BundleConstraint[] requires = bundles[i].getRequires();
725             for (int j = 0; j < requires.length; j++)
726                 if (requires[j].getNumPossibleSuppliers() > 1)
727                     multipleRequireSupplierList.add(requires[j]);
728             ResolverImport[] imports = bundles[i].getImportPackages();
729             for (int j = 0; j < imports.length; j++) {
730                 if (imports[j].getNumPossibleSuppliers() > 1) {
731                     Integer JavaDoc eeProfile = (Integer JavaDoc) ((ResolverExport) imports[j].getSelectedSupplier()).getExportPackageDescription().getDirective(ExportPackageDescriptionImpl.EQUINOX_EE);
732                     if (eeProfile.intValue() < 0) {
733                         // this is a normal package; always add it
734
multipleImportSupplierList.add(imports[j]);
735                     } else {
736                         // this is a system bunde export
737
// If other exporters of this package also require the system bundle
738
// then this package does not need to be added to the mix
739
// this is an optimization for bundles like org.eclipse.xerces
740
// that export lots of packages also exported by the system bundle on J2SE 1.4
741
VersionSupplier[] suppliers = imports[j].getPossibleSuppliers();
742                         for (int suppliersIndex = 1; suppliersIndex < suppliers.length; suppliersIndex++) {
743                             Integer JavaDoc ee = (Integer JavaDoc) ((ResolverExport) suppliers[suppliersIndex]).getExportPackageDescription().getDirective(ExportPackageDescriptionImpl.EQUINOX_EE);
744                             if (ee.intValue() >= 0)
745                                 continue;
746                             if (((ResolverExport) suppliers[suppliersIndex]).getExporter().getRequire(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME) == null)
747                                 if (((ResolverExport) suppliers[suppliersIndex]).getExporter().getRequire(Constants.SYSTEM_BUNDLE_SYMBOLICNAME) == null) {
748                                     multipleImportSupplierList.add(imports[j]);
749                                     break;
750                                 }
751                         }
752                     }
753                 }
754             }
755         }
756         ArrayList results = new ArrayList();
757         if (multipleImportSupplierList.size() + multipleRequireSupplierList.size() > MAX_MULTIPLE_SUPPLIERS_MERGE) {
758             // we have hit a max on the multiple suppliers in the lists without merging.
759
// first merge the identical constraints that have identical suppliers
760
HashMap multipleImportSupplierMaps = new HashMap(1);
761             for (Iterator iMultipleImportSuppliers = multipleImportSupplierList.iterator(); iMultipleImportSuppliers.hasNext();)
762                 addMutipleSupplierConstraint(multipleImportSupplierMaps, (ResolverConstraint) iMultipleImportSuppliers.next());
763             HashMap multipleRequireSupplierMaps = new HashMap(1);
764             for (Iterator iMultipleRequireSuppliers = multipleRequireSupplierList.iterator(); iMultipleRequireSuppliers.hasNext();)
765                 addMutipleSupplierConstraint(multipleRequireSupplierMaps, (ResolverConstraint) iMultipleRequireSuppliers.next());
766             addMergedSuppliers(results, multipleImportSupplierMaps);
767             addMergedSuppliers(results, multipleRequireSupplierMaps);
768             // check the results to see if we have reduced the number enough
769
if (results.size() > MAX_MULTIPLE_SUPPLIERS_MERGE && packageConstraints != null && bundleConstraints != null) {
770                 // we still have too big of a list; filter out constraints that are not in conflict
771
Iterator iResults = results.iterator();
772                 results = new ArrayList();
773                 while (iResults.hasNext()) {
774                     ResolverConstraint[] constraints = (ResolverConstraint[]) iResults.next();
775                     ResolverConstraint constraint = constraints.length > 0 ? constraints[0] : null;
776                     if (constraint instanceof ResolverImport) {
777                         if (packageConstraints.contains(constraint.getName()))
778                             results.add(constraints);
779                     } else if (constraint instanceof BundleConstraint) {
780                         if (bundleConstraints.contains(constraint.getName()))
781                             results.add(constraints);
782                     }
783                 }
784             }
785         } else {
786             // the size is acceptable; just copy the lists as-is
787
for (Iterator iMultipleImportSuppliers = multipleImportSupplierList.iterator(); iMultipleImportSuppliers.hasNext();)
788                 results.add(new ResolverConstraint[] {(ResolverConstraint) iMultipleImportSuppliers.next()});
789             for (Iterator iMultipleRequireSuppliers = multipleRequireSupplierList.iterator(); iMultipleRequireSuppliers.hasNext();)
790                 results.add(new ResolverConstraint[] {(ResolverConstraint) iMultipleRequireSuppliers.next()});
791         }
792         return (ResolverConstraint[][]) results.toArray(new ResolverConstraint[results.size()][]);
793     }
794
795     private void addMergedSuppliers(ArrayList mergedSuppliers, HashMap constraints) {
796         for (Iterator iConstraints = constraints.values().iterator(); iConstraints.hasNext();) {
797             ArrayList mergedConstraintLists = (ArrayList) iConstraints.next();
798             for (Iterator mergedLists = mergedConstraintLists.iterator(); mergedLists.hasNext();) {
799                 ArrayList constraintList = (ArrayList) mergedLists.next();
800                 mergedSuppliers.add(constraintList.toArray(new ResolverConstraint[constraintList.size()]));
801             }
802         }
803     }
804
805     private void addMutipleSupplierConstraint(HashMap constraints, ResolverConstraint constraint) {
806         ArrayList mergedConstraintLists = (ArrayList) constraints.get(constraint.getName());
807         if (mergedConstraintLists == null) {
808             mergedConstraintLists = new ArrayList(1);
809             ArrayList constraintList = new ArrayList(1);
810             constraintList.add(constraint);
811             mergedConstraintLists.add(constraintList);
812             constraints.put(constraint.getName(), mergedConstraintLists);
813             return;
814         }
815         for (Iterator mergedLists = mergedConstraintLists.iterator(); mergedLists.hasNext();) {
816             ArrayList constraintList = (ArrayList) mergedLists.next();
817             ResolverConstraint mergedConstraint = (ResolverConstraint) constraintList.get(0);
818             VersionSupplier[] suppliers1 = constraint.getPossibleSuppliers();
819             VersionSupplier[] suppliers2 = mergedConstraint.getPossibleSuppliers();
820             if (suppliers1.length != suppliers2.length)
821                 continue;
822             for (int i = 0; i < suppliers1.length; i++)
823                 if (suppliers1[i] != suppliers2[i])
824                     continue;
825             constraintList.add(constraint);
826             return;
827         }
828         ArrayList constraintList = new ArrayList(1);
829         constraintList.add(constraint);
830         mergedConstraintLists.add(constraintList);
831     }
832
833     private void checkCycle(ArrayList cycle) {
834         int cycleSize = cycle.size();
835         if (cycleSize == 0)
836             return;
837         cycleLoop: for (Iterator iCycle = cycle.iterator(); iCycle.hasNext();) {
838             ResolverBundle cycleBundle = (ResolverBundle) iCycle.next();
839             if (!cycleBundle.isResolvable()) {
840                 iCycle.remove(); // remove this bundle from the list of bundles that need re-resolved
841
continue cycleLoop;
842             }
843             // Check that we haven't wired to any dropped exports
844
ResolverImport[] imports = cycleBundle.getImportPackages();
845             for (int j = 0; j < imports.length; j++) {
846                 // check for dropped exports
847
while (imports[j].getSelectedSupplier() != null) {
848                     ResolverExport importSupplier = (ResolverExport) imports[j].getSelectedSupplier();
849                     if (importSupplier.isDropped())
850                         imports[j].selectNextSupplier();
851                     else
852                         break;
853                 }
854                 if (!imports[j].isDynamic() && !imports[j].isOptional() && imports[j].getSelectedSupplier() == null) {
855                     cycleBundle.setResolvable(false);
856                     cycleBundle.clearRefs();
857                     state.addResolverError(imports[j].getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, imports[j].getVersionConstraint().toString(), imports[j].getVersionConstraint());
858                     iCycle.remove();
859                     continue cycleLoop;
860                 }
861             }
862         }
863         if (cycle.size() != cycleSize) {
864             //we removed an un-resolvable bundle; must re-resolve remaining cycle
865
for (int i = 0; i < cycle.size(); i++) {
866                 ResolverBundle cycleBundle = (ResolverBundle) cycle.get(i);
867                 cycleBundle.clearWires();
868                 cycleBundle.clearRefs();
869             }
870             ArrayList innerCycle = new ArrayList(cycle.size());
871             for (int i = 0; i < cycle.size(); i++)
872                 resolveBundle((ResolverBundle) cycle.get(i), innerCycle);
873             checkCycle(innerCycle);
874         } else {
875             for (int i = 0; i < cycle.size(); i++) {
876                 if (DEBUG || DEBUG_CYCLES)
877                     ResolverImpl.log("Pushing " + cycle.get(i) + " to RESOLVED"); //$NON-NLS-1$ //$NON-NLS-2$
878
setBundleResolved((ResolverBundle) cycle.get(i));
879             }
880         }
881     }
882
883     private boolean selectSingletons(ResolverBundle[] bundles, ArrayList rejectedSingletons) {
884         if (developmentMode)
885             return false; // do no want to unresolve singletons in development mode
886
boolean result = false;
887         for (int i = 0; i < bundles.length; i++) {
888             BundleDescription bundleDesc = bundles[i].getBundle();
889             if (!bundleDesc.isSingleton() || !bundleDesc.isResolved() || rejectedSingletons.contains(bundleDesc))
890                 continue;
891             Object JavaDoc[] sameName = resolverBundles.get(bundleDesc.getName());
892             if (sameName.length > 1) { // Need to make a selection based off of num dependents
893
for (int j = 0; j < sameName.length; j++) {
894                     BundleDescription sameNameDesc = ((VersionSupplier) sameName[j]).getBundle();
895                     ResolverBundle sameNameBundle = (ResolverBundle) sameName[j];
896                     if (sameName[j] == bundles[i] || !sameNameDesc.isSingleton() || !sameNameDesc.isResolved() || rejectedSingletons.contains(sameNameDesc))
897                         continue; // Ignore the bundle we are selecting, non-singletons, and non-resolved
898
result = true;
899                     boolean rejectedPolicy = selectionPolicy != null ? selectionPolicy.compare(sameNameDesc, bundleDesc) < 0 : sameNameDesc.getVersion().compareTo(bundleDesc.getVersion()) > 0;
900                     int sameNameRefs = sameNameBundle.getRefs();
901                     int curRefs = bundles[i].getRefs();
902                     // TODO if the selection policy is set then number of references can override the selection policy; is that what we want?
903
// a bundle is always rejected if another bundle has more references to it;
904
// otherwise a bundle is rejected based on the selection policy (version) only if the number of references are equal
905
if ((sameNameRefs == curRefs && rejectedPolicy) || sameNameRefs > curRefs) {
906                         // this bundle is not selected; add it to the rejected list
907
if (!rejectedSingletons.contains(bundles[i].getBundle()))
908                             rejectedSingletons.add(bundles[i].getBundle());
909                         break;
910                     }
911                     // we did not select the sameNameDesc; add the bundle to the rejected list
912
if (!rejectedSingletons.contains(sameNameDesc))
913                         rejectedSingletons.add(sameNameDesc);
914                 }
915             }
916         }
917         // unresolve the rejected singletons
918
for (Iterator rejects = rejectedSingletons.iterator(); rejects.hasNext();)
919             unresolveBundle((ResolverBundle) bundleMapping.get(rejects.next()), false);
920         return result;
921     }
922
923     private void resolveFragment(ResolverBundle fragment) {
924         if (!fragment.isFragment())
925             return;
926         if (fragment.getHost().getNumPossibleSuppliers() > 0)
927             if (!developmentMode || state.getResolverErrors(fragment.getBundle()).length == 0)
928                 setBundleResolved(fragment);
929     }
930
931     // This method will attempt to resolve the supplied bundle and any bundles that it is dependent on
932
private boolean resolveBundle(ResolverBundle bundle, ArrayList cycle) {
933         if (bundle.isFragment())
934             return false;
935         if (!bundle.isResolvable()) {
936             if (DEBUG)
937                 ResolverImpl.log(" - " + bundle + " is unresolvable"); //$NON-NLS-1$ //$NON-NLS-2$
938
return false;
939         }
940         switch (bundle.getState()) {
941             case ResolverBundle.RESOLVED :
942                 // 'bundle' is already resolved so just return
943
if (DEBUG)
944                     ResolverImpl.log(" - " + bundle + " already resolved"); //$NON-NLS-1$ //$NON-NLS-2$
945
return true;
946             case ResolverBundle.UNRESOLVED :
947                 // 'bundle' is UNRESOLVED so move to RESOLVING
948
bundle.clearWires();
949                 setBundleResolving(bundle);
950                 break;
951             case ResolverBundle.RESOLVING :
952                 if (cycle.contains(bundle))
953                     return true;
954                 break;
955             default :
956                 break;
957         }
958
959         boolean failed = false;
960
961         if (!failed) {
962             GenericConstraint[] genericRequires = bundle.getGenericRequires();
963             for (int i = 0; i < genericRequires.length; i++) {
964                 if (!resolveGenericReq(genericRequires[i], cycle)) {
965                     if (DEBUG || DEBUG_GENERICS)
966                         ResolverImpl.log("** GENERICS " + genericRequires[i].getVersionConstraint().getName() + "[" + genericRequires[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
967
state.addResolverError(genericRequires[i].getVersionConstraint().getBundle(), ResolverError.MISSING_GENERIC_CAPABILITY, genericRequires[i].getVersionConstraint().toString(), genericRequires[i].getVersionConstraint());
968                     if (genericRequires[i].isFromFragment()) {
969                         if (!developmentMode) // only detach fragments when not in devmode
970
resolverExports.remove(bundle.detachFragment((ResolverBundle) bundleMapping.get(genericRequires[i].getVersionConstraint().getBundle()), null));
971                         continue;
972                     }
973                     if (!developmentMode) {
974                         // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
975
failed = true;
976                         break;
977                     }
978                 }
979             }
980         }
981
982         if (!failed) {
983             // Iterate thru required bundles of 'bundle' trying to find matching bundles.
984
BundleConstraint[] requires = bundle.getRequires();
985             for (int i = 0; i < requires.length; i++) {
986                 if (!resolveRequire(requires[i], cycle)) {
987                     if (DEBUG || DEBUG_REQUIRES)
988                         ResolverImpl.log("** REQUIRE " + requires[i].getVersionConstraint().getName() + "[" + requires[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
989
state.addResolverError(requires[i].getVersionConstraint().getBundle(), ResolverError.MISSING_REQUIRE_BUNDLE, requires[i].getVersionConstraint().toString(), requires[i].getVersionConstraint());
990                     // If the require has failed to resolve and it is from a fragment, then remove the fragment from the host
991
if (requires[i].isFromFragment()) {
992                         if (!developmentMode) // only detach fragments when not in devmode
993
resolverExports.remove(bundle.detachFragment((ResolverBundle) bundleMapping.get(requires[i].getVersionConstraint().getBundle()), requires[i]));
994                         continue;
995                     }
996                     if (!developmentMode) {
997                         // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
998
failed = true;
999                         break;
1000                    }
1001                }
1002            }
1003        }
1004
1005        if (!failed) {
1006            // Iterate thru imports of 'bundle' trying to find matching exports.
1007
ResolverImport[] imports = bundle.getImportPackages();
1008            for (int i = 0; i < imports.length; i++) {
1009                // Only resolve non-dynamic imports here
1010
if (!imports[i].isDynamic() && !resolveImport(imports[i], cycle)) {
1011                    if (DEBUG || DEBUG_IMPORTS)
1012                        ResolverImpl.log("** IMPORT " + imports[i].getName() + "[" + imports[i].getBundleDescription() + "] failed to resolve"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1013
// If the import has failed to resolve and it is from a fragment, then remove the fragment from the host
1014
state.addResolverError(imports[i].getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, imports[i].getVersionConstraint().toString(), imports[i].getVersionConstraint());
1015                    if (imports[i].isFromFragment()) {
1016                        if (!developmentMode) // only detach fragments when not in devmode
1017
resolverExports.remove(bundle.detachFragment((ResolverBundle) bundleMapping.get(imports[i].getVersionConstraint().getBundle()), imports[i]));
1018                        continue;
1019                    }
1020                    if (!developmentMode) {
1021                        // fail fast; otherwise we want to attempt to resolver other constraints in dev mode
1022
failed = true;
1023                        break;
1024                    }
1025                }
1026            }
1027        }
1028
1029        // check that fragment constraints are met by the constraints that got resolved to the host
1030
checkFragmentConstraints(bundle);
1031
1032        // do some extra checking when in development mode to see if other resolver error occurred
1033
if (developmentMode && !failed && state.getResolverErrors(bundle.getBundle()).length > 0)
1034            failed = true;
1035
1036        // Need to check that all mandatory imports are wired. If they are then
1037
// set the bundle RESOLVED, otherwise set it back to UNRESOLVED
1038
if (failed) {
1039            setBundleUnresolved(bundle, false, developmentMode);
1040            if (DEBUG)
1041                ResolverImpl.log(bundle + " NOT RESOLVED"); //$NON-NLS-1$
1042
} else if (!cycle.contains(bundle)) {
1043            setBundleResolved(bundle);
1044            if (DEBUG)
1045                ResolverImpl.log(bundle + " RESOLVED"); //$NON-NLS-1$
1046
}
1047
1048        if (bundle.getState() == ResolverBundle.UNRESOLVED)
1049            bundle.setResolvable(false); // Set it to unresolvable so we don't attempt to resolve it again in this round
1050

1051        return bundle.getState() != ResolverBundle.UNRESOLVED;
1052    }
1053
1054    private void checkFragmentConstraints(ResolverBundle bundle) {
1055        // get all currently attached fragments and ensure that any constraints
1056
// they have do not conflict with the constraints resolved to by the host
1057
ResolverBundle[] fragments = bundle.getFragments();
1058        for (int i = 0; i < fragments.length; i++) {
1059            BundleDescription fragment = fragments[i].getBundle();
1060            if (bundle.constraintsConflict(fragment, fragment.getImportPackages(), fragment.getRequiredBundles(), fragment.getGenericRequires()) && !developmentMode)
1061                // found some conflicts; detach the fragment
1062
resolverExports.remove(bundle.detachFragment(fragments[i], null));
1063        }
1064    }
1065
1066    private boolean resolveGenericReq(GenericConstraint constraint, ArrayList cycle) {
1067        if (DEBUG_REQUIRES)
1068            ResolverImpl.log("Trying to resolve: " + constraint.getBundle() + ", " + constraint.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$
1069
GenericCapability[] matchingCapabilities = constraint.getMatchingCapabilities();
1070        if (matchingCapabilities != null) {
1071            // Check for unrecorded cyclic dependency
1072
for (int i = 0; i < matchingCapabilities.length; i++)
1073                if (matchingCapabilities[i].getResolverBundle().getState() == ResolverBundle.RESOLVING)
1074                    if (!cycle.contains(constraint.getBundle()))
1075                        cycle.add(constraint.getBundle());
1076            if (DEBUG_REQUIRES)
1077                ResolverImpl.log(" - already wired"); //$NON-NLS-1$
1078
return true; // Already wired (due to grouping dependencies) so just return
1079
}
1080        Object JavaDoc[] capabilities = resolverGenerics.get(constraint.getVersionConstraint().getName());
1081        boolean result = false;
1082        for (int i = 0; i < capabilities.length; i++) {
1083            GenericCapability capability = (GenericCapability) capabilities[i];
1084            if (DEBUG_GENERICS)
1085                ResolverImpl.log("CHECKING GENERICS: " + capability.getBaseDescription()); //$NON-NLS-1$
1086
// Check if capability matches
1087
if (constraint.isSatisfiedBy(capability)) {
1088                capability.getResolverBundle().addRef(constraint.getBundle());
1089                if (result && (((GenericSpecification) constraint.getVersionConstraint()).getResolution() & GenericSpecification.RESOLUTION_MULTIPLE) == 0)
1090                    continue; // found a match already and this is not a multiple constraint
1091
constraint.setMatchingCapability(capability); // Wire to the capability
1092
if (constraint.getBundle() == capability.getResolverBundle()) {
1093                    result = true; // Wired to ourselves
1094
continue;
1095                }
1096                VersionSupplier[] capabilityHosts = capability.isFromFragment() ? capability.getResolverBundle().getHost().getPossibleSuppliers() : new ResolverBundle[] {capability.getResolverBundle()};
1097                boolean foundResolvedMatch = false;
1098                for (int j = 0; capabilityHosts != null && j < capabilityHosts.length; j++) {
1099                    ResolverBundle capabilitySupplier = (ResolverBundle) capabilityHosts[j];
1100                    if (capabilitySupplier == constraint.getBundle()) {
1101                        // the capability is from a fragment attached to this host do not recursively resolve the host again
1102
foundResolvedMatch = true;
1103                        continue;
1104                    }
1105                    // if in dev mode then allow a constraint to resolve to an unresolved bundle
1106
if (capabilitySupplier.getState() == ResolverBundle.RESOLVED || (resolveBundle(capabilitySupplier, cycle) || developmentMode)) {
1107                        foundResolvedMatch |= !capability.isFromFragment() ? true : capability.getResolverBundle().getHost().getPossibleSuppliers() != null;
1108                        // Check cyclic dependencies
1109
if (capabilitySupplier.getState() == ResolverBundle.RESOLVING)
1110                            if (!cycle.contains(capabilitySupplier))
1111                                cycle.add(capabilitySupplier);
1112                    }
1113                }
1114                if (!foundResolvedMatch) {
1115                    constraint.removeMatchingCapability(capability);
1116                    continue; // constraint hasn't resolved
1117
}
1118                if (DEBUG_GENERICS)
1119                    ResolverImpl.log("Found match: " + capability.getBaseDescription() + ". Wiring"); //$NON-NLS-1$ //$NON-NLS-2$
1120
result = true;
1121            }
1122        }
1123        return result ? true : (((GenericSpecification) constraint.getVersionConstraint()).getResolution() & GenericSpecification.RESOLUTION_OPTIONAL) != 0;
1124    }
1125
1126    // Resolve the supplied import. Returns true if the import can be resolved, false otherwise
1127
private boolean resolveRequire(BundleConstraint req, ArrayList cycle) {
1128        if (DEBUG_REQUIRES)
1129            ResolverImpl.log("Trying to resolve: " + req.getBundle() + ", " + req.getVersionConstraint()); //$NON-NLS-1$ //$NON-NLS-2$
1130
if (req.getSelectedSupplier() != null) {
1131            // Check for unrecorded cyclic dependency
1132
if (!cycle.contains(req.getBundle())) {
1133                cycle.add(req.getBundle());
1134                if (DEBUG_CYCLES)
1135                    ResolverImpl.log("require-bundle cycle: " + req.getBundle() + " -> " + req.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
1136
}
1137            if (DEBUG_REQUIRES)
1138                ResolverImpl.log(" - already wired"); //$NON-NLS-1$
1139
return true; // Already wired (due to grouping dependencies) so just return
1140
}
1141        Object JavaDoc[] bundles = resolverBundles.get(req.getVersionConstraint().getName());
1142        boolean result = false;
1143        for (int i = 0; i < bundles.length; i++) {
1144            ResolverBundle bundle = (ResolverBundle) bundles[i];
1145            if (DEBUG_REQUIRES)
1146                ResolverImpl.log("CHECKING: " + bundle.getBundle()); //$NON-NLS-1$
1147
// Check if export matches
1148
if (req.isSatisfiedBy(bundle)) {
1149                bundle.addRef(req.getBundle());
1150                // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
1151
req.addPossibleSupplier(bundle);
1152                if (req.getBundle() != bundle) {
1153                    // if in dev mode then allow a constraint to resolve to an unresolved bundle
1154
if (bundle.getState() != ResolverBundle.RESOLVED && !resolveBundle(bundle, cycle) && !developmentMode) {
1155                        req.removePossibleSupplier(bundle);
1156                        continue; // Bundle hasn't resolved
1157
}
1158                }
1159                // Check cyclic dependencies
1160
if (req.getBundle() != bundle) {
1161                    if (bundle.getState() == ResolverBundle.RESOLVING)
1162                        // If the bundle is RESOLVING, we have a cyclic dependency
1163
if (!cycle.contains(req.getBundle())) {
1164                            cycle.add(req.getBundle());
1165                            if (DEBUG_CYCLES)
1166                                ResolverImpl.log("require-bundle cycle: " + req.getBundle() + " -> " + req.getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
1167
}
1168                }
1169                if (DEBUG_REQUIRES)
1170                    ResolverImpl.log("Found match: " + bundle.getBundle() + ". Wiring"); //$NON-NLS-1$ //$NON-NLS-2$
1171
result = true;
1172            }
1173        }
1174        if (result || req.isOptional())
1175            return true; // If the req is optional then just return true
1176

1177        return false;
1178    }
1179
1180    // Resolve the supplied import. Returns true if the import can be resolved, false otherwise
1181
private boolean resolveImport(ResolverImport imp, ArrayList cycle) {
1182        if (DEBUG_IMPORTS)
1183            ResolverImpl.log("Trying to resolve: " + imp.getBundle() + ", " + imp.getName()); //$NON-NLS-1$ //$NON-NLS-2$
1184
if (imp.getSelectedSupplier() != null) {
1185            // Check for unrecorded cyclic dependency
1186
if (!cycle.contains(imp.getBundle())) {
1187                cycle.add(imp.getBundle());
1188                if (DEBUG_CYCLES)
1189                    ResolverImpl.log("import-package cycle: " + imp.getBundle() + " -> " + imp.getSelectedSupplier() + " from " + imp.getSelectedSupplier().getBundle()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1190
}
1191            if (DEBUG_IMPORTS)
1192                ResolverImpl.log(" - already wired"); //$NON-NLS-1$
1193
return true; // Already wired (due to grouping dependencies) so just return
1194
}
1195        boolean result = false;
1196        Object JavaDoc[] exports = resolverExports.get(imp.getName());
1197        exportsloop: for (int i = 0; i < exports.length; i++) {
1198            ResolverExport export = (ResolverExport) exports[i];
1199            if (DEBUG_IMPORTS)
1200                ResolverImpl.log("CHECKING: " + export.getExporter().getBundle() + ", " + export.getName()); //$NON-NLS-1$ //$NON-NLS-2$
1201
// Check if export matches
1202
if (imp.isSatisfiedBy(export)) {
1203                int originalState = export.getExporter().getState();
1204                if (imp.isDynamic() && originalState != ResolverBundle.RESOLVED)
1205                    continue; // Must not attempt to resolve an exporter when dynamic
1206
if (imp.getBundle() == export.getExporter() && !export.getExportPackageDescription().isRoot())
1207                    continue; // Can't wire to our own re-export
1208
if (imp.getSelectedSupplier() != null && ((ResolverExport) imp.getSelectedSupplier()).getExporter() == imp.getBundle())
1209                    break; // We wired to ourselves; nobody else matters
1210
export.getExporter().addRef(imp.getBundle());
1211                // first add the possible supplier; this is done before resolving the supplier bundle to prevent endless cycle loops.
1212
imp.addPossibleSupplier(export);
1213                ResolverExport[] importerExps = null;
1214                if (imp.getBundle() != export.getExporter()) {
1215                    // Save the exports of this package from the importer in case we need to add them back
1216
importerExps = imp.getBundle().getExports(imp.getName());
1217                    for (int j = 0; j < importerExps.length; j++) {
1218                        if (importerExps[j].getExportPackageDescription().isRoot() && !export.getExportPackageDescription().isRoot())
1219                            continue exportsloop; // to prevent imports from getting wired to re-exports if we offer a root export
1220
if (importerExps[j].getExportPackageDescription().isRoot()) // do not drop reexports when import wins
1221
resolverExports.remove(importerExps[j]); // Import wins, remove export
1222
}
1223                    // if in dev mode then allow a constraint to resolve to an unresolved bundle
1224
if ((originalState != ResolverBundle.RESOLVED && !resolveBundle(export.getExporter(), cycle) && !developmentMode) || export.isDropped()) {
1225                        // remove the possible supplier
1226
imp.removePossibleSupplier(export);
1227                        // add back the exports of this package from the importer
1228
for (int j = 0; j < importerExps.length; j++)
1229                            resolverExports.put(importerExps[j].getName(), importerExps[j]);
1230                        continue; // Bundle hasn't resolved || export has not been selected and is unavailable
1231
}
1232                } else if (export.isDropped())
1233                    continue; // we already found a possible import that satisifies us; our export is dropped
1234

1235                // Record any cyclic dependencies
1236
if (imp.getBundle() != export.getExporter())
1237                    if (export.getExporter().getState() == ResolverBundle.RESOLVING) {
1238                        // If the exporter is RESOLVING, we have a cyclic dependency
1239
if (!cycle.contains(imp.getBundle())) {
1240                            cycle.add(imp.getBundle());
1241                            if (DEBUG_CYCLES)
1242                                ResolverImpl.log("import-package cycle: " + imp.getBundle() + " -> " + imp.getSelectedSupplier() + " from " + imp.getSelectedSupplier().getBundle()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1243
}
1244                    }
1245                if (DEBUG_IMPORTS)
1246                    ResolverImpl.log("Found match: " + export.getExporter() + ". Wiring " + imp.getBundle() + ":" + imp.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1247
result = true;
1248            }
1249        }
1250
1251        if (result)
1252            return true;
1253        if (resolveImportReprovide(imp, cycle))
1254            return true;
1255        if (imp.isOptional())
1256            return true; // If the import is optional then just return true
1257
return false;
1258    }
1259
1260    // Check if the import can be resolved to a re-exported package (has no export object to match to)
1261
private boolean resolveImportReprovide(ResolverImport imp, ArrayList cycle) {
1262        String JavaDoc bsn = ((ImportPackageSpecification) imp.getVersionConstraint()).getBundleSymbolicName();
1263        // If no symbolic name specified then just return (since this is a
1264
// re-export an import not specifying a bsn will wire to the root)
1265
if (bsn == null)
1266            return false;
1267        if (DEBUG_IMPORTS)
1268            ResolverImpl.log("Checking reprovides: " + imp.getName()); //$NON-NLS-1$
1269
// Find bundle with specified bsn
1270
Object JavaDoc[] bundles = resolverBundles.get(bsn);
1271        for (int i = 0; i < bundles.length; i++)
1272            if (resolveBundle((ResolverBundle) bundles[i], cycle))
1273                if (resolveImportReprovide0(imp, (ResolverBundle) bundles[i], (ResolverBundle) bundles[i], cycle, new ArrayList(5)))
1274                    return true;
1275        return false;
1276    }
1277
1278    private boolean resolveImportReprovide0(ResolverImport imp, ResolverBundle reexporter, ResolverBundle rb, ArrayList cycle, ArrayList visited) {
1279        if (visited.contains(rb))
1280            return false; // make sure we don't endless recurse cycles
1281
visited.add(rb);
1282        BundleConstraint[] requires = rb.getRequires();
1283        for (int i = 0; i < requires.length; i++) {
1284            if (!((BundleSpecification) requires[i].getVersionConstraint()).isExported())
1285                continue; // Skip require if it doesn't re-export the packages
1286
// Check exports to see if we've found the root
1287
if (requires[i].getSelectedSupplier() == null)
1288                continue;
1289            ResolverExport[] exports = ((ResolverBundle) requires[i].getSelectedSupplier()).getExports(imp.getName());
1290            for (int j = 0; j < exports.length; j++) {
1291                Map directives = exports[j].getExportPackageDescription().getDirectives();
1292                directives.remove(Constants.USES_DIRECTIVE);
1293                ExportPackageDescription epd = state.getFactory().createExportPackageDescription(exports[j].getName(), exports[j].getVersion(), directives, exports[j].getExportPackageDescription().getAttributes(), false, reexporter.getBundle());
1294                if (imp.getVersionConstraint().isSatisfiedBy(epd)) {
1295                    // Create reexport and add to bundle and resolverExports
1296
if (DEBUG_IMPORTS)
1297                        ResolverImpl.log(" - Creating re-export for reprovide: " + reexporter + ":" + epd.getName()); //$NON-NLS-1$ //$NON-NLS-2$
1298
ResolverExport re = new ResolverExport(reexporter, epd);
1299                    reexporter.addExport(re);
1300                    resolverExports.put(re.getName(), re);
1301                    // Resolve import
1302
imp.addPossibleSupplier(re);
1303                    return true;
1304                }
1305            }
1306            // Check requires of matching bundle (recurse down the chain)
1307
if (resolveImportReprovide0(imp, reexporter, (ResolverBundle) requires[i].getSelectedSupplier(), cycle, visited))
1308                return true;
1309        }
1310        return false;
1311    }
1312
1313    // Move a bundle to UNRESOLVED
1314
private void setBundleUnresolved(ResolverBundle bundle, boolean removed, boolean keepFragsAttached) {
1315        if (bundle.getState() == ResolverBundle.UNRESOLVED && !developmentMode)
1316            // in this case there is nothing more to do
1317
return;
1318        // Note that when in dev mode we only want to force the fragment detach if asked to;
1319
// this would be done only when forcing a dependency chain to unresolve from unresolveBundle method
1320
if (removed || !keepFragsAttached) {
1321            // Force the initialization of the bundle, its exports and its capabilities. This is needed to force proper attachment of fragments.
1322
resolverExports.remove(bundle.getExportPackages());
1323            resolverGenerics.remove(bundle.getGenericCapabilities());
1324            bundle.detachAllFragments();
1325            bundle.initialize(false);
1326            if (!removed) {
1327                // add back the available exports/capabilities
1328
resolverExports.put(bundle.getExportPackages());
1329                resolverGenerics.put(bundle.getGenericCapabilities());
1330            }
1331        }
1332        // TODO unresolvedBundles should be a set; for now only need to do a contains check in devMode.
1333
if (!removed && (!developmentMode || !unresolvedBundles.contains(bundle)))
1334            unresolvedBundles.add(bundle);
1335        bundle.setState(ResolverBundle.UNRESOLVED);
1336    }
1337
1338    // Move a bundle to RESOLVED
1339
private void setBundleResolved(ResolverBundle bundle) {
1340        if (bundle.getState() == ResolverBundle.RESOLVED)
1341            return;
1342        unresolvedBundles.remove(bundle);
1343        bundle.setState(ResolverBundle.RESOLVED);
1344    }
1345
1346    // Move a bundle to RESOLVING
1347
private void setBundleResolving(ResolverBundle bundle) {
1348        if (bundle.getState() == ResolverBundle.RESOLVING)
1349            return;
1350        unresolvedBundles.remove(bundle);
1351        bundle.setState(ResolverBundle.RESOLVING);
1352    }
1353
1354    // Resolves the bundles in the State
1355
private void stateResolveBundles(ResolverBundle[] resolvedBundles) {
1356        for (int i = 0; i < resolvedBundles.length; i++) {
1357            if (!resolvedBundles[i].getBundle().isResolved())
1358                stateResolveBundle(resolvedBundles[i]);
1359        }
1360    }
1361
1362    private void stateResolveConstraints(ResolverBundle rb) {
1363        ResolverImport[] imports = rb.getImportPackages();
1364        for (int i = 0; i < imports.length; i++) {
1365            ResolverExport export = (ResolverExport) imports[i].getSelectedSupplier();
1366            BaseDescription supplier = export == null ? null : export.getExportPackageDescription();
1367            state.resolveConstraint(imports[i].getVersionConstraint(), supplier);
1368        }
1369        BundleConstraint[] requires = rb.getRequires();
1370        for (int i = 0; i < requires.length; i++) {
1371            ResolverBundle bundle = (ResolverBundle) requires[i].getSelectedSupplier();
1372            BaseDescription supplier = bundle == null ? null : bundle.getBundle();
1373            state.resolveConstraint(requires[i].getVersionConstraint(), supplier);
1374        }
1375        GenericConstraint[] genericRequires = rb.getGenericRequires();
1376        for (int i = 0; i < genericRequires.length; i++) {
1377            GenericCapability[] matchingCapabilities = genericRequires[i].getMatchingCapabilities();
1378            if (matchingCapabilities == null)
1379                state.resolveConstraint(genericRequires[i].getVersionConstraint(), null);
1380            else
1381                for (int j = 0; j < matchingCapabilities.length; j++)
1382                    state.resolveConstraint(genericRequires[i].getVersionConstraint(), matchingCapabilities[j].getBaseDescription());
1383        }
1384    }
1385
1386    private void stateResolveFragConstraints(ResolverBundle rb) {
1387        ResolverBundle host = (ResolverBundle) rb.getHost().getSelectedSupplier();
1388        ImportPackageSpecification[] imports = rb.getBundle().getImportPackages();
1389        for (int i = 0; i < imports.length; i++) {
1390            ResolverImport hostImport = host == null ? null : host.getImport(imports[i].getName());
1391            ResolverExport export = (ResolverExport) (hostImport == null ? null : hostImport.getSelectedSupplier());
1392            BaseDescription supplier = export == null ? null : export.getExportPackageDescription();
1393            state.resolveConstraint(imports[i], supplier);
1394        }
1395        BundleSpecification[] requires = rb.getBundle().getRequiredBundles();
1396        for (int i = 0; i < requires.length; i++) {
1397            BundleConstraint hostRequire = host == null ? null : host.getRequire(requires[i].getName());
1398            ResolverBundle bundle = (ResolverBundle) (hostRequire == null ? null : hostRequire.getSelectedSupplier());
1399            BaseDescription supplier = bundle == null ? null : bundle.getBundle();
1400            state.resolveConstraint(requires[i], supplier);
1401        }
1402    }
1403
1404    private void stateResolveBundle(ResolverBundle rb) {
1405        // if in dev mode then we want to tell the state about the constraints we were able to resolve
1406
if (!rb.isResolved() && !developmentMode)
1407            return;
1408        if (rb.isFragment())
1409            stateResolveFragConstraints(rb);
1410        else
1411            stateResolveConstraints(rb);
1412        // Gather selected exports
1413
ResolverExport[] exports = rb.getSelectedExports();
1414        ArrayList selectedExports = new ArrayList(exports.length);
1415        for (int i = 0; i < exports.length; i++) {
1416            selectedExports.add(exports[i].getExportPackageDescription());
1417        }
1418        ExportPackageDescription[] selectedExportsArray = (ExportPackageDescription[]) selectedExports.toArray(new ExportPackageDescription[selectedExports.size()]);
1419
1420        // Gather exports that have been wired to
1421
ResolverImport[] imports = rb.getImportPackages();
1422        ArrayList exportsWiredTo = new ArrayList(imports.length);
1423        for (int i = 0; i < imports.length; i++)
1424            if (imports[i].getSelectedSupplier() != null)
1425                exportsWiredTo.add(imports[i].getSelectedSupplier().getBaseDescription());
1426        ExportPackageDescription[] exportsWiredToArray = (ExportPackageDescription[]) exportsWiredTo.toArray(new ExportPackageDescription[exportsWiredTo.size()]);
1427
1428        // Gather bundles that have been wired to
1429
BundleConstraint[] requires = rb.getRequires();
1430        ArrayList bundlesWiredTo = new ArrayList(requires.length);
1431        for (int i = 0; i < requires.length; i++)
1432            if (requires[i].getSelectedSupplier() != null)
1433                bundlesWiredTo.add(requires[i].getSelectedSupplier().getBaseDescription());
1434        BundleDescription[] bundlesWiredToArray = (BundleDescription[]) bundlesWiredTo.toArray(new BundleDescription[bundlesWiredTo.size()]);
1435
1436        BundleDescription[] hostBundles = null;
1437        if (rb.isFragment()) {
1438            VersionSupplier[] matchingBundles = rb.getHost().getPossibleSuppliers();
1439            if (matchingBundles != null && matchingBundles.length > 0) {
1440                hostBundles = new BundleDescription[matchingBundles.length];
1441                for (int i = 0; i < matchingBundles.length; i++) {
1442                    hostBundles[i] = matchingBundles[i].getBundle();
1443                    if (rb.isNewFragmentExports() && hostBundles[i].isResolved()) {
1444                        // update the host's set of selected exports
1445
ResolverExport[] hostExports = ((ResolverBundle) matchingBundles[i]).getSelectedExports();
1446                        ExportPackageDescription[] hostExportsArray = new ExportPackageDescription[hostExports.length];
1447                        for (int j = 0; j < hostExports.length; j++)
1448                            hostExportsArray[j] = hostExports[j].getExportPackageDescription();
1449                        state.resolveBundle(hostBundles[i], true, null, hostExportsArray, hostBundles[i].getResolvedRequires(), hostBundles[i].getResolvedImports());
1450                    }
1451                }
1452            }
1453        }
1454
1455        // Resolve the bundle in the state
1456
state.resolveBundle(rb.getBundle(), rb.isResolved(), hostBundles, selectedExportsArray, bundlesWiredToArray, exportsWiredToArray);
1457    }
1458
1459    // Resolve dynamic import
1460
public synchronized ExportPackageDescription resolveDynamicImport(BundleDescription importingBundle, String JavaDoc requestedPackage) {
1461        if (state == null)
1462            throw new IllegalStateException JavaDoc("RESOLVER_NO_STATE"); //$NON-NLS-1$
1463

1464        // Make sure the resolver is initialized
1465
if (!initialized)
1466            initialize();
1467
1468        ResolverBundle rb = (ResolverBundle) bundleMapping.get(importingBundle);
1469        if (rb.getExport(requestedPackage) != null)
1470            return null; // do not allow dynamic wires for packages which this bundle exports
1471
ResolverImport[] resolverImports = rb.getImportPackages();
1472        // Check through the ResolverImports of this bundle.
1473
// If there is a matching one then pass it into resolveImport()
1474
boolean found = false;
1475        for (int j = 0; j < resolverImports.length; j++) {
1476            // Make sure it is a dynamic import
1477
if (!resolverImports[j].isDynamic())
1478                continue;
1479            String JavaDoc importName = resolverImports[j].getName();
1480            // If the import uses a wildcard, then temporarily replace this with the requested package
1481
if (importName.equals("*") || //$NON-NLS-1$
1482
(importName.endsWith(".*") && requestedPackage.startsWith(importName.substring(0, importName.length() - 2)))) { //$NON-NLS-1$
1483
resolverImports[j].setName(requestedPackage);
1484            }
1485            // Resolve the import
1486
if (requestedPackage.equals(resolverImports[j].getName())) {
1487                found = true;
1488                // populate the grouping checker with current imports
1489
groupingChecker.populateRoots(resolverImports[j].getBundle());
1490                if (resolveImport(resolverImports[j], new ArrayList())) {
1491                    found = false;
1492                    while (!found && resolverImports[j].getSelectedSupplier() != null) {
1493                        if (groupingChecker.isDynamicConsistent(resolverImports[j].getBundle(), (ResolverExport) resolverImports[j].getSelectedSupplier()) != null)
1494                            resolverImports[j].selectNextSupplier(); // not consistent; try the next
1495
else
1496                            found = true; // found a valid wire
1497
}
1498                    resolverImports[j].setName(null);
1499                    if (!found) {
1500                        // not found or there was a conflict; reset the suppliers and return null
1501
resolverImports[j].setPossibleSuppliers(null);
1502                        return null;
1503                    }
1504                    // If the import resolved then return it's matching export
1505
if (DEBUG_IMPORTS)
1506                        ResolverImpl.log("Resolved dynamic import: " + rb + ":" + resolverImports[j].getName() + " -> " + ((ResolverExport) resolverImports[j].getSelectedSupplier()).getExporter() + ":" + requestedPackage); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
1507
ExportPackageDescription matchingExport = ((ResolverExport) resolverImports[j].getSelectedSupplier()).getExportPackageDescription();
1508                    // If it is a wildcard import then clear the wire, so other
1509
// exported packages can be found for it
1510
if (importName.endsWith("*")) //$NON-NLS-1$
1511
resolverImports[j].setPossibleSuppliers(null);
1512                    return matchingExport;
1513                }
1514            }
1515            // Reset the import package name
1516
resolverImports[j].setName(null);
1517        }
1518        // this is to support adding dynamic imports on the fly.
1519
if (!found) {
1520            Map directives = new HashMap(1);
1521            directives.put(Constants.RESOLUTION_DIRECTIVE, ImportPackageSpecification.RESOLUTION_DYNAMIC);
1522            ImportPackageSpecification packageSpec = state.getFactory().createImportPackageSpecification(requestedPackage, null, null, null, directives, null, importingBundle);
1523            ResolverImport newImport = new ResolverImport(rb, packageSpec);
1524            if (resolveImport(newImport, new ArrayList())) {
1525                while (newImport.getSelectedSupplier() != null) {
1526                    if (groupingChecker.isDynamicConsistent(rb, (ResolverExport) newImport.getSelectedSupplier()) != null)
1527                        newImport.selectNextSupplier();
1528                    else
1529                        break;
1530                }
1531                return ((ResolverExport) newImport.getSelectedSupplier()).getExportPackageDescription();
1532            }
1533        }
1534        if (DEBUG || DEBUG_IMPORTS)
1535            ResolverImpl.log("Failed to resolve dynamic import: " + requestedPackage); //$NON-NLS-1$
1536
return null; // Couldn't resolve the import, so return null
1537
}
1538
1539    public void bundleAdded(BundleDescription bundle) {
1540        if (!initialized)
1541            return;
1542
1543        boolean alreadyThere = false;
1544        for (int i = 0; i < unresolvedBundles.size(); i++) {
1545            ResolverBundle rb = (ResolverBundle) unresolvedBundles.get(i);
1546            if (rb.getBundle() == bundle) {
1547                alreadyThere = true;
1548            }
1549        }
1550        if (!alreadyThere) {
1551            ResolverBundle rb = new ResolverBundle(bundle, this);
1552            bundleMapping.put(bundle, rb);
1553            unresolvedBundles.add(rb);
1554            resolverExports.put(rb.getExportPackages());
1555            resolverBundles.put(rb.getName(), rb);
1556            resolverGenerics.put(rb.getGenericCapabilities());
1557        }
1558    }
1559
1560    public void bundleRemoved(BundleDescription bundle, boolean pending) {
1561        // check if there are any dependants
1562
if (pending)
1563            removalPending.put(new Long JavaDoc(bundle.getBundleId()), bundle);
1564        if (!initialized)
1565            return;
1566        ResolverBundle rb = (ResolverBundle) bundleMapping.get(bundle);
1567        if (rb == null)
1568            return;
1569
1570        if (!pending) {
1571            bundleMapping.remove(bundle);
1572            groupingChecker.clear(rb);
1573        }
1574        if (!pending || !bundle.isResolved()) {
1575            resolverExports.remove(rb.getExportPackages());
1576            resolverBundles.remove(rb);
1577            resolverGenerics.remove(rb.getGenericCapabilities());
1578        }
1579        unresolvedBundles.remove(rb);
1580    }
1581
1582    private void unresolveBundle(ResolverBundle bundle, boolean removed) {
1583        if (bundle == null)
1584            return;
1585        // check the removed list if unresolving then remove from the removed list
1586
Object JavaDoc[] removedBundles = removalPending.remove(new Long JavaDoc(bundle.getBundle().getBundleId()));
1587        for (int i = 0; i < removedBundles.length; i++) {
1588            ResolverBundle re = (ResolverBundle) bundleMapping.get(removedBundles[i]);
1589            unresolveBundle(re, true);
1590            state.removeBundleComplete((BundleDescription) removedBundles[i]);
1591            resolverExports.remove(re.getExportPackages());
1592            resolverBundles.remove(re);
1593            resolverGenerics.remove(re.getGenericCapabilities());
1594            bundleMapping.remove(removedBundles[i]);
1595            groupingChecker.clear(re);
1596            // the bundle is removed
1597
if (removedBundles[i] == bundle.getBundle())
1598                removed = true;
1599        }
1600
1601        if (!bundle.getBundle().isResolved() && !developmentMode)
1602            return;
1603        // if not removed then add to the list of unresolvedBundles,
1604
// passing false for devmode because we need all fragments detached
1605
setBundleUnresolved(bundle, removed, false);
1606        // Get bundles dependent on 'bundle'
1607
BundleDescription[] dependents = bundle.getBundle().getDependents();
1608        state.resolveBundle(bundle.getBundle(), false, null, null, null, null);
1609        // Unresolve dependents of 'bundle'
1610
for (int i = 0; i < dependents.length; i++)
1611            unresolveBundle((ResolverBundle) bundleMapping.get(dependents[i]), false);
1612    }
1613
1614    public void bundleUpdated(BundleDescription newDescription, BundleDescription existingDescription, boolean pending) {
1615        bundleRemoved(existingDescription, pending);
1616        bundleAdded(newDescription);
1617    }
1618
1619    public void flush() {
1620        resolverExports = null;
1621        resolverBundles = null;
1622        resolverGenerics = null;
1623        unresolvedBundles = null;
1624        bundleMapping = null;
1625        Object JavaDoc[] removed = removalPending.getAllValues();
1626        for (int i = 0; i < removed.length; i++)
1627            state.removeBundleComplete((BundleDescription) removed[i]);
1628        removalPending.clear();
1629        initialized = false;
1630    }
1631
1632    public State getState() {
1633        return state;
1634    }
1635
1636    public void setState(State newState) {
1637        state = newState;
1638        flush();
1639    }
1640
1641    private void setDebugOptions() {
1642        FrameworkDebugOptions options = FrameworkDebugOptions.getDefault();
1643        // may be null if debugging is not enabled
1644
if (options == null)
1645            return;
1646        DEBUG = options.getBooleanOption(OPTION_DEBUG, false);
1647        DEBUG_WIRING = options.getBooleanOption(OPTION_WIRING, false);
1648        DEBUG_IMPORTS = options.getBooleanOption(OPTION_IMPORTS, false);
1649        DEBUG_REQUIRES = options.getBooleanOption(OPTION_REQUIRES, false);
1650        DEBUG_GENERICS = options.getBooleanOption(OPTION_GENERICS, false);
1651        DEBUG_GROUPING = options.getBooleanOption(OPTION_GROUPING, false);
1652        DEBUG_CYCLES = options.getBooleanOption(OPTION_CYCLES, false);
1653    }
1654
1655    // LOGGING METHODS
1656
private void printWirings() {
1657        ResolverImpl.log("****** Result Wirings ******"); //$NON-NLS-1$
1658
Object JavaDoc[] bundles = resolverBundles.getAllValues();
1659        for (int j = 0; j < bundles.length; j++) {
1660            ResolverBundle rb = (ResolverBundle) bundles[j];
1661            if (rb.getBundle().isResolved()) {
1662                continue;
1663            }
1664            ResolverImpl.log(" * WIRING for " + rb); //$NON-NLS-1$
1665
// Require bundles
1666
BundleConstraint[] requireBundles = rb.getRequires();
1667            if (requireBundles.length == 0) {
1668                ResolverImpl.log(" (r) no requires"); //$NON-NLS-1$
1669
} else {
1670                for (int i = 0; i < requireBundles.length; i++) {
1671                    if (requireBundles[i].getSelectedSupplier() == null) {
1672                        ResolverImpl.log(" (r) " + rb.getBundle() + " -> NULL!!!"); //$NON-NLS-1$ //$NON-NLS-2$
1673
} else {
1674                        ResolverImpl.log(" (r) " + rb.getBundle() + " -> " + requireBundles[i].getSelectedSupplier()); //$NON-NLS-1$ //$NON-NLS-2$
1675
}
1676                }
1677            }
1678            // Hosts
1679
BundleConstraint hostSpec = rb.getHost();
1680            if (hostSpec != null) {
1681                VersionSupplier[] hosts = hostSpec.getPossibleSuppliers();
1682                if (hosts != null)
1683                    for (int i = 0; i < hosts.length; i++) {
1684                        ResolverImpl.log(" (h) " + rb.getBundle() + " -> " + hosts[i].getBundle()); //$NON-NLS-1$ //$NON-NLS-2$
1685
}
1686            }
1687            // Imports
1688
ResolverImport[] imports = rb.getImportPackages();
1689            if (imports.length == 0) {
1690                ResolverImpl.log(" (w) no imports"); //$NON-NLS-1$
1691
continue;
1692            }
1693            for (int i = 0; i < imports.length; i++) {
1694                if (imports[i].isDynamic() && imports[i].getSelectedSupplier() == null) {
1695                    ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> DYNAMIC"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1696
} else if (imports[i].isOptional() && imports[i].getSelectedSupplier() == null) {
1697                    ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> OPTIONAL (could not be wired)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1698
} else if (imports[i].getSelectedSupplier() == null) {
1699                    ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> NULL!!!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1700
} else {
1701                    ResolverImpl.log(" (w) " + imports[i].getBundle() + ":" + imports[i].getName() + " -> " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1702
((ResolverExport) imports[i].getSelectedSupplier()).getExporter() + ":" + imports[i].getSelectedSupplier().getName()); //$NON-NLS-1$
1703
}
1704            }
1705        }
1706    }
1707
1708    static void log(String JavaDoc message) {
1709        Debug.println(message);
1710    }
1711
1712    VersionHashMap getResolverExports() {
1713        return resolverExports;
1714    }
1715
1716    public void setSelectionPolicy(Comparator selectionPolicy) {
1717        this.selectionPolicy = selectionPolicy;
1718    }
1719
1720    public Comparator getSelectionPolicy() {
1721        return selectionPolicy;
1722    }
1723}
1724
Popular Tags