1 /*******************************************************************************
2  * Copyright (c) 2003, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Danail Nachev -  ProSyst - bug 218625
14  *     Rob Harrop - SpringSource Inc. (bug 247522)
15  *     Karsten Thoms (itemis) - bug 535341
16  *******************************************************************************/
17 package org.eclipse.osgi.internal.resolver;
18 
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Dictionary;
24 import java.util.Enumeration;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Hashtable;
28 import java.util.Iterator;
29 import java.util.LinkedHashSet;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap;
35 import org.eclipse.osgi.internal.framework.EquinoxContainer;
36 import org.eclipse.osgi.internal.framework.FilterImpl;
37 import org.eclipse.osgi.service.resolver.BaseDescription;
38 import org.eclipse.osgi.service.resolver.BundleDescription;
39 import org.eclipse.osgi.service.resolver.BundleSpecification;
40 import org.eclipse.osgi.service.resolver.DisabledInfo;
41 import org.eclipse.osgi.service.resolver.ExportPackageDescription;
42 import org.eclipse.osgi.service.resolver.GenericDescription;
43 import org.eclipse.osgi.service.resolver.GenericSpecification;
44 import org.eclipse.osgi.service.resolver.HostSpecification;
45 import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
46 import org.eclipse.osgi.service.resolver.NativeCodeDescription;
47 import org.eclipse.osgi.service.resolver.NativeCodeSpecification;
48 import org.eclipse.osgi.service.resolver.Resolver;
49 import org.eclipse.osgi.service.resolver.ResolverError;
50 import org.eclipse.osgi.service.resolver.ResolverHookException;
51 import org.eclipse.osgi.service.resolver.State;
52 import org.eclipse.osgi.service.resolver.StateDelta;
53 import org.eclipse.osgi.service.resolver.StateHelper;
54 import org.eclipse.osgi.service.resolver.StateObjectFactory;
55 import org.eclipse.osgi.service.resolver.StateWire;
56 import org.eclipse.osgi.service.resolver.VersionConstraint;
57 import org.eclipse.osgi.util.ManifestElement;
58 import org.eclipse.osgi.util.NLS;
59 import org.osgi.framework.Bundle;
60 import org.osgi.framework.BundleException;
61 import org.osgi.framework.Constants;
62 import org.osgi.framework.InvalidSyntaxException;
63 import org.osgi.framework.Version;
64 import org.osgi.framework.hooks.resolver.ResolverHook;
65 import org.osgi.framework.hooks.resolver.ResolverHookFactory;
66 import org.osgi.framework.wiring.BundleRevision;
67 
68 public abstract class StateImpl implements State {
69 
70 	public static final String ECLIPSE_PLATFORMFILTER = "Eclipse-PlatformFilter"; //$NON-NLS-1$
71 	public static final String Eclipse_JREBUNDLE = "Eclipse-JREBundle"; //$NON-NLS-1$
72 	/**
73 	 * Manifest Export-Package directive indicating that the exported package should only
74 	 * be made available when the resolver is not in strict mode.
75 	 */
76 	public static final String INTERNAL_DIRECTIVE = "x-internal"; //$NON-NLS-1$
77 
78 	/**
79 	 * Manifest Export-Package directive indicating that the exported package should only
80 	 * be made available to friends of the exporting bundle.
81 	 */
82 	public static final String FRIENDS_DIRECTIVE = "x-friends"; //$NON-NLS-1$
83 
84 	/**
85 	 * Manifest header (named "Provide-Package")
86 	 * identifying the packages name
87 	 * provided to other bundles which require the bundle.
88 	 *
89 	 * <p>
90 	 * NOTE: this is only used for backwards compatibility, bundles manifest using
91 	 * syntax version 2 will not recognize this header.
92 	 *
93 	 * <p>The attribute value may be retrieved from the
94 	 * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
95 	 * @deprecated
96 	 */
97 	public final static String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$
98 
99 	/**
100 	 * Manifest header attribute (named &quot;reprovide&quot;)
101 	 * for Require-Bundle
102 	 * identifying that any packages that are provided
103 	 * by the required bundle must be reprovided by the requiring bundle.
104 	 * The default value is <tt>false</tt>.
105 	 * <p>
106 	 * The attribute value is encoded in the Require-Bundle manifest
107 	 * header like:
108 	 * <pre>
109 	 * Require-Bundle: com.acme.module.test; reprovide="true"
110 	 * </pre>
111 	 * <p>
112 	 * NOTE: this is only used for backwards compatibility, bundles manifest using
113 	 * syntax version 2 will not recognize this attribute.
114 	 * @deprecated
115 	 */
116 	public final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$
117 
118 	/**
119 	 * Manifest header attribute (named &quot;optional&quot;)
120 	 * for Require-Bundle
121 	 * identifying that a required bundle is optional and that
122 	 * the requiring bundle can be resolved if there is no
123 	 * suitable required bundle.
124 	 * The default value is <tt>false</tt>.
125 	 *
126 	 * <p>The attribute value is encoded in the Require-Bundle manifest
127 	 * header like:
128 	 * <pre>
129 	 * Require-Bundle: com.acme.module.test; optional="true"
130 	 * </pre>
131 	 * <p>
132 	 * NOTE: this is only used for backwards compatibility, bundles manifest using
133 	 * syntax version 2 will not recognize this attribute.
134 	 * @since 1.3 <b>EXPERIMENTAL</b>
135 	 * @deprecated
136 	 */
137 	public final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$
138 
139 	public static final String OSGI_RESOLVER_MODE = "osgi.resolverMode"; //$NON-NLS-1$
140 	public static final String STRICT_MODE = "strict"; //$NON-NLS-1$
141 	public static final String DEVELOPMENT_MODE = "development"; //$NON-NLS-1$
142 
143 	public static final String STATE_SYSTEM_BUNDLE = "osgi.system.bundle"; //$NON-NLS-1$
144 
145 	private static final String OSGI_OS = "osgi.os"; //$NON-NLS-1$
146 	private static final String OSGI_WS = "osgi.ws"; //$NON-NLS-1$
147 	private static final String OSGI_NL = "osgi.nl"; //$NON-NLS-1$
148 	private static final String OSGI_ARCH = "osgi.arch"; //$NON-NLS-1$
149 	public static final String[] PROPS = {OSGI_OS, OSGI_WS, OSGI_NL, OSGI_ARCH, Constants.FRAMEWORK_SYSTEMPACKAGES, Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, OSGI_RESOLVER_MODE, Constants.FRAMEWORK_EXECUTIONENVIRONMENT, "osgi.resolveOptional", "osgi.genericAliases", Constants.FRAMEWORK_OS_NAME, Constants.FRAMEWORK_OS_VERSION, Constants.FRAMEWORK_PROCESSOR, Constants.FRAMEWORK_LANGUAGE, STATE_SYSTEM_BUNDLE, Constants.FRAMEWORK_SYSTEMCAPABILITIES, Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA}; //$NON-NLS-1$ //$NON-NLS-2$
150 	private static final DisabledInfo[] EMPTY_DISABLEDINFOS = new DisabledInfo[0];
151 	public static final String OSGI_EE_NAMESPACE = "osgi.ee"; //$NON-NLS-1$
152 
153 	transient private Resolver resolver;
154 	transient private StateDeltaImpl changes;
155 	transient private boolean resolving = false;
156 	transient private LinkedList<BundleDescription> removalPendings = new LinkedList<>();
157 
158 	private boolean resolved = true;
159 	private long timeStamp = System.currentTimeMillis();
160 	private final Map<Long, BundleDescription> bundleDescriptions = new HashMap<>();
161 	private final Map<BundleDescription, List<ResolverError>> resolverErrors = new HashMap<>();
162 	private StateObjectFactory factory;
163 	private final Map<Long, BundleDescription> resolvedBundles = new HashMap<>();
164 	private final Map<BundleDescription, List<DisabledInfo>> disabledBundles = new HashMap<>();
165 	private final Map<String, Set<BundleDescription>> bundleNameCache = new HashMap<>();
166 	private boolean fullyLoaded = false;
167 	private boolean dynamicCacheChanged = false;
168 	// only used for lazy loading of BundleDescriptions
169 	private StateReader reader;
170 	@SuppressWarnings("unchecked")
171 	private Dictionary<Object, Object>[] platformProperties = new Dictionary[] {new Hashtable<String, String>(PROPS.length)}; // Dictionary here because of Filter API
172 	private long highestBundleId = -1;
173 	private final Set<String> platformPropertyKeys = new HashSet<>(PROPS.length);
174 	private ResolverHookFactory hookFactory;
175 	private ResolverHook hook;
176 	private boolean developmentMode = false;
177 
178 	final Object monitor = new Object();
179 
180 	// to prevent extra-package instantiation
StateImpl()181 	protected StateImpl() {
182 		// always add the default platform property keys.
183 		addPlatformPropertyKeys(PROPS);
184 	}
185 
add(BundleDescription bundle, Map<Long, BundleDescription> map, boolean replace)186 	private static boolean add(BundleDescription bundle, Map<Long, BundleDescription> map, boolean replace) {
187 		if (!replace && map.containsKey(bundle.getBundleId())) {
188 			return false;
189 		}
190 		map.put(bundle.getBundleId(), bundle);
191 		return true;
192 	}
193 
addBundle(BundleDescription description)194 	public boolean addBundle(BundleDescription description) {
195 		synchronized (this.monitor) {
196 			if (!basicAddBundle(description))
197 				return false;
198 			String platformFilter = description.getPlatformFilter();
199 			if (platformFilter != null) {
200 				try {
201 					// add any new platform filter propery keys this bundle is using
202 					FilterImpl filter = FilterImpl.newInstance(platformFilter);
203 					addPlatformPropertyKeys(filter.getAttributes());
204 				} catch (InvalidSyntaxException e) {
205 					// ignore this is handled in another place
206 				}
207 			}
208 			NativeCodeSpecification nativeCode = description.getNativeCodeSpecification();
209 			if (nativeCode != null) {
210 				NativeCodeDescription[] suppliers = nativeCode.getPossibleSuppliers();
211 				for (NativeCodeDescription supplier : suppliers) {
212 					FilterImpl filter = (FilterImpl) supplier.getFilter();
213 					if (filter != null)
214 						addPlatformPropertyKeys(filter.getAttributes());
215 				}
216 			}
217 			resolved = false;
218 			getDelta().recordBundleAdded((BundleDescriptionImpl) description);
219 			if (getSystemBundle().equals(description.getSymbolicName()))
220 				resetAllSystemCapabilities();
221 			if (resolver != null)
222 				resolver.bundleAdded(description);
223 			updateTimeStamp();
224 			return true;
225 		}
226 	}
227 
updateBundle(BundleDescription newDescription)228 	public boolean updateBundle(BundleDescription newDescription) {
229 		synchronized (this.monitor) {
230 			BundleDescriptionImpl existing = (BundleDescriptionImpl) bundleDescriptions.get(newDescription.getBundleId());
231 			if (existing == null)
232 				return false;
233 			if (bundleDescriptions.remove(existing.getBundleId()) == null)
234 				return false;
235 			removeBundleNameCacheEntry(existing);
236 			resolvedBundles.remove(existing.getBundleId());
237 			List<DisabledInfo> infos = disabledBundles.remove(existing);
238 			if (infos != null) {
239 				List<DisabledInfo> newInfos = new ArrayList<>(infos.size());
240 				for (Iterator<DisabledInfo> iInfos = infos.iterator(); iInfos.hasNext();) {
241 					DisabledInfo info = iInfos.next();
242 					newInfos.add(new DisabledInfo(info.getPolicyName(), info.getMessage(), newDescription));
243 				}
244 				disabledBundles.put(newDescription, newInfos);
245 			}
246 			existing.setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true);
247 			if (!basicAddBundle(newDescription))
248 				return false;
249 			resolved = false;
250 			getDelta().recordBundleUpdated((BundleDescriptionImpl) newDescription);
251 			if (getSystemBundle().equals(newDescription.getSymbolicName()))
252 				resetAllSystemCapabilities();
253 			if (resolver != null) {
254 				boolean pending = isInUse(existing);
255 				resolver.bundleUpdated(newDescription, existing, pending);
256 				if (pending) {
257 					getDelta().recordBundleRemovalPending(existing);
258 					addRemovalPending(existing);
259 				} else {
260 					// an existing bundle has been updated with no dependents it can safely be unresolved now
261 					try {
262 						resolving = true;
263 						resolverErrors.remove(existing);
264 						resolveBundle(existing, false, null, null, null, null, null, null, null, null);
265 					} finally {
266 						resolving = false;
267 					}
268 				}
269 			}
270 			updateTimeStamp();
271 			return true;
272 		}
273 	}
274 
removeBundle(long bundleId)275 	public BundleDescription removeBundle(long bundleId) {
276 		synchronized (this.monitor) {
277 			BundleDescription toRemove = getBundle(bundleId);
278 			if (toRemove == null || !removeBundle(toRemove))
279 				return null;
280 			return toRemove;
281 		}
282 	}
283 
removeBundle(BundleDescription toRemove)284 	public boolean removeBundle(BundleDescription toRemove) {
285 		synchronized (this.monitor) {
286 			toRemove = bundleDescriptions.get(toRemove.getBundleId());
287 			if (toRemove == null || bundleDescriptions.remove(toRemove.getBundleId()) == null)
288 				return false;
289 			resolvedBundles.remove(toRemove.getBundleId());
290 			disabledBundles.remove(toRemove);
291 			removeBundleNameCacheEntry(toRemove);
292 			resolved = false;
293 			getDelta().recordBundleRemoved((BundleDescriptionImpl) toRemove);
294 			((BundleDescriptionImpl) toRemove).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true);
295 			if (resolver != null) {
296 				boolean pending = isInUse(toRemove);
297 				resolver.bundleRemoved(toRemove, pending);
298 				if (pending) {
299 					getDelta().recordBundleRemovalPending((BundleDescriptionImpl) toRemove);
300 					addRemovalPending(toRemove);
301 				} else {
302 					// a bundle has been removed with no dependents it can safely be unresolved now
303 					try {
304 						resolving = true;
305 						resolverErrors.remove(toRemove);
306 						resolveBundle(toRemove, false, null, null, null, null, null);
307 					} finally {
308 						resolving = false;
309 					}
310 				}
311 			}
312 			updateTimeStamp();
313 			return true;
314 		}
315 	}
316 
isInUse(BundleDescription bundle)317 	private boolean isInUse(BundleDescription bundle) {
318 		return bundle.getDependents().length > 0;
319 	}
320 
getChanges()321 	public StateDelta getChanges() {
322 		synchronized (this.monitor) {
323 			return getDelta();
324 		}
325 	}
326 
getDelta()327 	private StateDeltaImpl getDelta() {
328 		if (changes == null)
329 			changes = new StateDeltaImpl(this);
330 		return changes;
331 	}
332 
getBundles(String symbolicName)333 	public BundleDescription[] getBundles(String symbolicName) {
334 		synchronized (this.monitor) {
335 			if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName))
336 				symbolicName = getSystemBundle();
337 			final Set<BundleDescription> bundles = bundleNameCache.get(symbolicName);
338 			if (bundles == null) {
339 				return new BundleDescription[0];
340 			}
341 			return bundles.toArray(new BundleDescription[bundles.size()]);
342 		}
343 	}
344 
getBundles()345 	public BundleDescription[] getBundles() {
346 		synchronized (this.monitor) {
347 			return bundleDescriptions.values().toArray(new BundleDescription[0]);
348 		}
349 	}
350 
getBundle(long id)351 	public BundleDescription getBundle(long id) {
352 		synchronized (this.monitor) {
353 			BundleDescription result = bundleDescriptions.get(id);
354 			if (result != null)
355 				return result;
356 			// need to look in removal pending bundles;
357 			for (Iterator<BundleDescription> iter = removalPendings.iterator(); iter.hasNext();) {
358 				BundleDescription removedBundle = iter.next();
359 				if (removedBundle.getBundleId() == id) // just return the first matching id
360 					return removedBundle;
361 			}
362 			return null;
363 		}
364 	}
365 
getBundle(String name, Version version)366 	public BundleDescription getBundle(String name, Version version) {
367 		synchronized (this.monitor) {
368 			BundleDescription[] allBundles = getBundles(name);
369 			if (allBundles.length == 1)
370 				return version == null || allBundles[0].getVersion().equals(version) ? allBundles[0] : null;
371 			if (allBundles.length == 0)
372 				return null;
373 			BundleDescription unresolvedFound = null;
374 			BundleDescription resolvedFound = null;
375 			for (BundleDescription current : allBundles) {
376 				BundleDescription base;
377 
378 				if (current.isResolved())
379 					base = resolvedFound;
380 				else
381 					base = unresolvedFound;
382 
383 				if (version == null || current.getVersion().equals(version)) {
384 					if (base != null && (base.getVersion().compareTo(current.getVersion()) <= 0 || base.getBundleId() > current.getBundleId())) {
385 						if (base == resolvedFound)
386 							resolvedFound = current;
387 						else
388 							unresolvedFound = current;
389 					} else {
390 						if (current.isResolved())
391 							resolvedFound = current;
392 						else
393 							unresolvedFound = current;
394 					}
395 
396 				}
397 			}
398 			if (resolvedFound != null)
399 				return resolvedFound;
400 			return unresolvedFound;
401 		}
402 	}
403 
getTimeStamp()404 	public long getTimeStamp() {
405 		synchronized (this.monitor) {
406 			return timeStamp;
407 		}
408 	}
409 
isResolved()410 	public boolean isResolved() {
411 		synchronized (this.monitor) {
412 			return resolved || isEmpty();
413 		}
414 	}
415 
resolveConstraint(VersionConstraint constraint, BaseDescription supplier)416 	public void resolveConstraint(VersionConstraint constraint, BaseDescription supplier) {
417 		((VersionConstraintImpl) constraint).setSupplier(supplier);
418 	}
419 
420 	/**
421 	 * @deprecated
422 	 */
resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports)423 	public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) {
424 		resolveBundle(bundle, status, hosts, selectedExports, null, resolvedRequires, resolvedImports);
425 	}
426 
427 	/**
428 	 * @deprecated
429 	 */
resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports)430 	public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) {
431 		resolveBundle(bundle, status, hosts, selectedExports, substitutedExports, null, resolvedRequires, resolvedImports, null, null);
432 	}
433 
resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires)434 	public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires) {
435 		synchronized (this.monitor) {
436 			if (!resolving)
437 				throw new IllegalStateException(); // TODO need error message here!
438 			BundleDescriptionImpl modifiable = (BundleDescriptionImpl) bundle;
439 			// must record the change before setting the resolve state to
440 			// accurately record if a change has happened.
441 			getDelta().recordBundleResolved(modifiable, status);
442 			// force the new resolution data to stay in memory; we will not read this from disk anymore
443 			modifiable.setLazyLoaded(false);
444 			modifiable.setStateBit(BundleDescriptionImpl.RESOLVED, status);
445 			if (status) {
446 				resolverErrors.remove(modifiable);
447 				add(modifiable, resolvedBundles, true);
448 			} else {
449 				// remove the bundle from the resolved pool
450 				resolvedBundles.remove(modifiable.getBundleId());
451 				modifiable.removeDependencies();
452 			}
453 			// to support development mode we will resolveConstraints even if the resolve status == false
454 			// we only do this if the resolved constraints are not null
455 			if (selectedExports == null || resolvedRequires == null || resolvedImports == null)
456 				unresolveConstraints(modifiable);
457 			else
458 				resolveConstraints(modifiable, hosts, selectedExports, substitutedExports, selectedCapabilities, resolvedRequires, resolvedImports, resolvedCapabilities, resolvedWires);
459 		}
460 	}
461 
removeBundleComplete(BundleDescription bundle)462 	public void removeBundleComplete(BundleDescription bundle) {
463 		synchronized (this.monitor) {
464 			if (!resolving)
465 				throw new IllegalStateException(); // TODO need error message here!
466 			getDelta().recordBundleRemovalComplete((BundleDescriptionImpl) bundle);
467 			removalPendings.remove(bundle);
468 		}
469 	}
470 
resolveConstraints(BundleDescriptionImpl bundle, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires)471 	private void resolveConstraints(BundleDescriptionImpl bundle, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires) {
472 		HostSpecificationImpl hostSpec = (HostSpecificationImpl) bundle.getHost();
473 		if (hostSpec != null) {
474 			if (hosts != null) {
475 				hostSpec.setHosts(hosts);
476 				for (BundleDescription host : hosts) {
477 					((BundleDescriptionImpl) host).addDependency(bundle, true);
478 					checkHostForSubstitutedExports((BundleDescriptionImpl) host, bundle);
479 				}
480 			}
481 		}
482 
483 		bundle.setSelectedExports(selectedExports);
484 		bundle.setResolvedRequires(resolvedRequires);
485 		bundle.setResolvedImports(resolvedImports);
486 		bundle.setSubstitutedExports(substitutedExports);
487 		bundle.setSelectedCapabilities(selectedCapabilities);
488 		bundle.setResolvedCapabilities(resolvedCapabilities);
489 		bundle.setStateWires(resolvedWires);
490 
491 		bundle.addDependencies(hosts, true);
492 		bundle.addDependencies(resolvedRequires, true);
493 		bundle.addDependencies(resolvedImports, true);
494 		bundle.addDependencies(resolvedCapabilities, true);
495 	}
496 
checkHostForSubstitutedExports(BundleDescriptionImpl host, BundleDescriptionImpl fragment)497 	private void checkHostForSubstitutedExports(BundleDescriptionImpl host, BundleDescriptionImpl fragment) {
498 		// TODO need to handle this case where a fragment has its own export substituted
499 		// there are issues here because the order in which fragments are resolved is not always the same ...
500 	}
501 
unresolveConstraints(BundleDescriptionImpl bundle)502 	private void unresolveConstraints(BundleDescriptionImpl bundle) {
503 		HostSpecificationImpl host = (HostSpecificationImpl) bundle.getHost();
504 		if (host != null)
505 			host.setHosts(null);
506 
507 		bundle.setSelectedExports(null);
508 		bundle.setResolvedImports(null);
509 		bundle.setResolvedRequires(null);
510 		bundle.setSubstitutedExports(null);
511 		bundle.setSelectedCapabilities(null);
512 		bundle.setResolvedCapabilities(null);
513 		bundle.setStateWires(null);
514 		bundle.clearAddedDynamicImportPackages();
515 
516 		// remove the constraint suppliers
517 		NativeCodeSpecificationImpl nativeCode = (NativeCodeSpecificationImpl) bundle.getNativeCodeSpecification();
518 		if (nativeCode != null)
519 			nativeCode.setSupplier(null);
520 		ImportPackageSpecification[] imports = bundle.getImportPackages();
521 		for (ImportPackageSpecification importSpecification : imports) {
522 			((ImportPackageSpecificationImpl) importSpecification).setSupplier(null);
523 		}
524 		BundleSpecification[] requires = bundle.getRequiredBundles();
525 		for (BundleSpecification require : requires) {
526 			((BundleSpecificationImpl) require).setSupplier(null);
527 		}
528 		GenericSpecification[] genericRequires = bundle.getGenericRequires();
529 		if (genericRequires.length > 0)
530 			for (GenericSpecification genericRequire : genericRequires) {
531 				((GenericSpecificationImpl) genericRequire).setSupplers(null);
532 			}
533 
534 		bundle.removeDependencies();
535 	}
536 
resolve(boolean incremental, BundleDescription[] reResolve, BundleDescription[] triggers)537 	private StateDelta resolve(boolean incremental, BundleDescription[] reResolve, BundleDescription[] triggers) {
538 		fullyLoad();
539 		synchronized (this.monitor) {
540 			if (resolver == null)
541 				throw new IllegalStateException("no resolver set"); //$NON-NLS-1$
542 			if (resolving == true)
543 				throw new IllegalStateException("An attempt to start a nested resolve process has been detected."); //$NON-NLS-1$
544 			try {
545 				resolving = true;
546 				if (!incremental) {
547 					resolved = false;
548 					reResolve = getBundles();
549 					// need to get any removal pendings before flushing
550 					if (removalPendings.size() > 0) {
551 						BundleDescription[] removed = internalGetRemovalPending();
552 						reResolve = mergeBundles(reResolve, removed);
553 					}
554 					flush(reResolve);
555 				} else {
556 					if (resolved && reResolve == null)
557 						return new StateDeltaImpl(this);
558 					if (developmentMode) {
559 						// in dev mode we need to aggressively flush removal pendings
560 						if (removalPendings.size() > 0) {
561 							BundleDescription[] removed = internalGetRemovalPending();
562 							reResolve = mergeBundles(reResolve, removed);
563 						}
564 					}
565 					if (reResolve == null)
566 						reResolve = internalGetRemovalPending();
567 					if (triggers == null) {
568 						Set<BundleDescription> triggerSet = new HashSet<>();
569 						Collection<BundleDescription> closure = getDependencyClosure(Arrays.asList(reResolve));
570 						for (BundleDescription toRefresh : closure) {
571 							Bundle bundle = toRefresh.getBundle();
572 							if (bundle != null && (bundle.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED | Bundle.RESOLVED)) == 0)
573 								triggerSet.add(toRefresh);
574 						}
575 						triggers = triggerSet.toArray(new BundleDescription[triggerSet.size()]);
576 					}
577 				}
578 				// use the Headers class to handle ignoring case while matching keys (bug 180817)
579 				@SuppressWarnings("unchecked")
580 				CaseInsensitiveDictionaryMap<Object, Object>[] tmpPlatformProperties = new CaseInsensitiveDictionaryMap[platformProperties.length];
581 				for (int i = 0; i < platformProperties.length; i++) {
582 					tmpPlatformProperties[i] = new CaseInsensitiveDictionaryMap<>(platformProperties[i].size());
583 					for (Enumeration<Object> keys = platformProperties[i].keys(); keys.hasMoreElements();) {
584 						Object key = keys.nextElement();
585 						tmpPlatformProperties[i].put(key, platformProperties[i].get(key));
586 					}
587 				}
588 
589 				ResolverHookFactory currentFactory = hookFactory;
590 				if (currentFactory != null) {
591 					@SuppressWarnings("unchecked")
592 					Collection<BundleRevision> triggerRevisions = Collections.unmodifiableCollection(triggers == null ? Collections.EMPTY_LIST : Arrays.asList((BundleRevision[]) triggers));
593 					begin(triggerRevisions);
594 				}
595 				ResolverHookException error = null;
596 				try {
597 					resolver.resolve(reResolve, tmpPlatformProperties);
598 				} catch (ResolverHookException e) {
599 					error = e;
600 					resolverErrors.clear();
601 				}
602 				resolved = removalPendings.size() == 0;
603 
604 				StateDeltaImpl savedChanges = changes == null ? new StateDeltaImpl(this) : changes;
605 				savedChanges.setResolverHookException(error);
606 				changes = new StateDeltaImpl(this);
607 
608 				if (savedChanges.getChanges().length > 0)
609 					updateTimeStamp();
610 				return savedChanges;
611 			} finally {
612 				resolving = false;
613 			}
614 		}
615 	}
616 
mergeBundles(BundleDescription[] reResolve, BundleDescription[] removed)617 	private BundleDescription[] mergeBundles(BundleDescription[] reResolve, BundleDescription[] removed) {
618 		if (reResolve == null)
619 			return removed; // just return all the removed bundles
620 		if (reResolve.length == 0)
621 			return reResolve; // if reResolve length==0 then we want to prevent pending removal
622 		// merge in all removal pending bundles that are not already in the list
623 		List<BundleDescription> result = new ArrayList<>(reResolve.length + removed.length);
624 		Collections.addAll(result, reResolve);
625 		for (BundleDescription removedDescription : removed) {
626 			boolean found = false;
627 			for (BundleDescription toRefresh : reResolve) {
628 				if (removedDescription == toRefresh) {
629 					found = true;
630 					break;
631 				}
632 			}
633 			if (!found) {
634 				result.add(removedDescription);
635 			}
636 		}
637 		return result.toArray(new BundleDescription[result.size()]);
638 	}
639 
flush(BundleDescription[] bundles)640 	private void flush(BundleDescription[] bundles) {
641 		resolver.flush();
642 		resolved = false;
643 		resolverErrors.clear();
644 		if (resolvedBundles.isEmpty())
645 			return;
646 		for (BundleDescription bundle : bundles) {
647 			resolveBundle(bundle, false, null, null, null, null, null);
648 		}
649 		resolvedBundles.clear();
650 	}
651 
resolve()652 	public StateDelta resolve() {
653 		return resolve(true, null, null);
654 	}
655 
resolve(boolean incremental)656 	public StateDelta resolve(boolean incremental) {
657 		return resolve(incremental, null, null);
658 	}
659 
resolve(BundleDescription[] reResolve)660 	public StateDelta resolve(BundleDescription[] reResolve) {
661 		return resolve(true, reResolve, null);
662 	}
663 
resolve(BundleDescription[] resolve, boolean discard)664 	public StateDelta resolve(BundleDescription[] resolve, boolean discard) {
665 		BundleDescription[] reResolve = discard ? resolve : new BundleDescription[0];
666 		BundleDescription[] triggers = discard ? null : resolve;
667 		return resolve(true, reResolve, triggers);
668 	}
669 
670 	@SuppressWarnings("deprecation")
setOverrides(Object value)671 	public void setOverrides(Object value) {
672 		throw new UnsupportedOperationException();
673 	}
674 
setResolverHookFactory(ResolverHookFactory hookFactory)675 	public void setResolverHookFactory(ResolverHookFactory hookFactory) {
676 		synchronized (this.monitor) {
677 			if (this.hookFactory != null)
678 				throw new IllegalStateException("Resolver hook factory is already set."); //$NON-NLS-1$
679 			this.hookFactory = hookFactory;
680 		}
681 	}
682 
begin(Collection<BundleRevision> triggers)683 	private ResolverHook begin(Collection<BundleRevision> triggers) {
684 		ResolverHookFactory current;
685 		synchronized (this.monitor) {
686 			current = this.hookFactory;
687 		}
688 		ResolverHook newHook = current.begin(triggers);
689 		synchronized (this.monitor) {
690 			this.hook = newHook;
691 		}
692 		return newHook;
693 	}
694 
getResolverHookFactory()695 	ResolverHookFactory getResolverHookFactory() {
696 		synchronized (this.monitor) {
697 			return this.hookFactory;
698 		}
699 	}
700 
getResolverHook()701 	public ResolverHook getResolverHook() {
702 		synchronized (this.monitor) {
703 			return this.hook;
704 		}
705 	}
706 
getResolvedBundles()707 	public BundleDescription[] getResolvedBundles() {
708 		synchronized (this.monitor) {
709 			return resolvedBundles.values().toArray(new BundleDescription[0]);
710 		}
711 	}
712 
isEmpty()713 	public boolean isEmpty() {
714 		synchronized (this.monitor) {
715 			return bundleDescriptions.isEmpty();
716 		}
717 	}
718 
setResolved(boolean resolved)719 	void setResolved(boolean resolved) {
720 		synchronized (this.monitor) {
721 			this.resolved = resolved;
722 		}
723 	}
724 
basicAddBundle(BundleDescription description)725 	boolean basicAddBundle(BundleDescription description) {
726 		synchronized (this.monitor) {
727 			StateImpl origState = (StateImpl) description.getContainingState();
728 			if (origState != null && origState != this) {
729 				if (origState.removalPendings.contains(description))
730 					throw new IllegalStateException(NLS.bind(StateMsg.BUNDLE_PENDING_REMOVE_STATE, description.toString()));
731 				if (origState.getBundle(description.getBundleId()) == description)
732 					throw new IllegalStateException(NLS.bind(StateMsg.BUNDLE_IN_OTHER_STATE, description.toString()));
733 			}
734 			((BundleDescriptionImpl) description).setContainingState(this);
735 			((BundleDescriptionImpl) description).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, false);
736 			if (add(description, bundleDescriptions, false)) {
737 				if (description.getBundleId() > getHighestBundleId())
738 					highestBundleId = description.getBundleId();
739 				addBundleNameCacheEntry(description);
740 				return true;
741 			}
742 			return false;
743 		}
744 	}
745 
addResolvedBundle(BundleDescriptionImpl resolvedBundle)746 	void addResolvedBundle(BundleDescriptionImpl resolvedBundle) {
747 		synchronized (this.monitor) {
748 			add(resolvedBundle, resolvedBundles, true);
749 		}
750 	}
751 
getExportedPackages()752 	public ExportPackageDescription[] getExportedPackages() {
753 		fullyLoad();
754 		synchronized (this.monitor) {
755 			List<ExportPackageDescription> allExportedPackages = new ArrayList<>();
756 			for (Iterator<BundleDescription> iter = resolvedBundles.values().iterator(); iter.hasNext();) {
757 				BundleDescription bundle = iter.next();
758 				ExportPackageDescription[] bundlePackages = bundle.getSelectedExports();
759 				if (bundlePackages == null)
760 					continue;
761 				Collections.addAll(allExportedPackages, bundlePackages);
762 			}
763 			for (Iterator<BundleDescription> iter = removalPendings.iterator(); iter.hasNext();) {
764 				BundleDescription bundle = iter.next();
765 				ExportPackageDescription[] bundlePackages = bundle.getSelectedExports();
766 				if (bundlePackages == null)
767 					continue;
768 				Collections.addAll(allExportedPackages, bundlePackages);
769 			}
770 			return allExportedPackages.toArray(new ExportPackageDescription[allExportedPackages.size()]);
771 		}
772 	}
773 
getFragments(final BundleDescription host)774 	BundleDescription[] getFragments(final BundleDescription host) {
775 		final List<BundleDescription> fragments = new ArrayList<>();
776 		synchronized (this.monitor) {
777 			for (Iterator<BundleDescription> iter = bundleDescriptions.values().iterator(); iter.hasNext();) {
778 				BundleDescription bundle = iter.next();
779 				HostSpecification hostSpec = bundle.getHost();
780 
781 				if (hostSpec != null) {
782 					BundleDescription[] hosts = hostSpec.getHosts();
783 					if (hosts != null)
784 						for (BundleDescription hostCandidate : hosts) {
785 							if (hostCandidate == host) {
786 								fragments.add(bundle);
787 								break;
788 							}
789 						}
790 				}
791 			}
792 		}
793 		return fragments.toArray(new BundleDescription[fragments.size()]);
794 	}
795 
setTimeStamp(long newTimeStamp)796 	public void setTimeStamp(long newTimeStamp) {
797 		synchronized (this.monitor) {
798 			timeStamp = newTimeStamp;
799 		}
800 	}
801 
updateTimeStamp()802 	private void updateTimeStamp() {
803 		synchronized (this.monitor) {
804 			if (getTimeStamp() == Long.MAX_VALUE)
805 				setTimeStamp(0);
806 			setTimeStamp(getTimeStamp() + 1);
807 		}
808 	}
809 
getFactory()810 	public StateObjectFactory getFactory() {
811 		return factory;
812 	}
813 
setFactory(StateObjectFactory factory)814 	void setFactory(StateObjectFactory factory) {
815 		this.factory = factory;
816 	}
817 
getBundleByLocation(String location)818 	public BundleDescription getBundleByLocation(String location) {
819 		synchronized (this.monitor) {
820 			for (Iterator<BundleDescription> i = bundleDescriptions.values().iterator(); i.hasNext();) {
821 				BundleDescription current = i.next();
822 				if (location.equals(current.getLocation()))
823 					return current;
824 			}
825 			return null;
826 		}
827 	}
828 
getResolver()829 	public Resolver getResolver() {
830 		synchronized (this.monitor) {
831 			return resolver;
832 		}
833 	}
834 
setResolver(Resolver newResolver)835 	public void setResolver(Resolver newResolver) {
836 		if (resolver == newResolver)
837 			return;
838 		if (resolver != null) {
839 			Resolver oldResolver = resolver;
840 			resolver = null;
841 			oldResolver.setState(null);
842 		}
843 		synchronized (this.monitor) {
844 			resolver = newResolver;
845 		}
846 		if (resolver == null)
847 			return;
848 		resolver.setState(this);
849 	}
850 
setPlatformProperties(Dictionary<?, ?> platformProperties)851 	public boolean setPlatformProperties(Dictionary<?, ?> platformProperties) {
852 		return setPlatformProperties(new Dictionary[] {platformProperties});
853 	}
854 
setPlatformProperties(Dictionary<?, ?>[] platformProperties)855 	public boolean setPlatformProperties(Dictionary<?, ?>[] platformProperties) {
856 		return setPlatformProperties(platformProperties, true);
857 	}
858 
setPlatformProperties(Dictionary<?, ?>[] platformProperties, boolean resetSystemExports)859 	synchronized boolean setPlatformProperties(Dictionary<?, ?>[] platformProperties, boolean resetSystemExports) {
860 		if (platformProperties.length == 0)
861 			throw new IllegalArgumentException();
862 		// copy the properties for our use internally;
863 		// only copy String and String[] values
864 		@SuppressWarnings("unchecked")
865 		Dictionary<Object, Object>[] newPlatformProperties = new Dictionary[platformProperties.length];
866 		for (int i = 0; i < platformProperties.length; i++) {
867 			newPlatformProperties[i] = new Hashtable<>(platformProperties[i].size());
868 			synchronized (platformProperties[i]) {
869 				for (Enumeration<?> keys = platformProperties[i].keys(); keys.hasMoreElements();) {
870 					Object key = keys.nextElement();
871 					Object value = platformProperties[i].get(key);
872 					newPlatformProperties[i].put(key, value);
873 				}
874 			}
875 			// make sure the bundle native code osgi properties have decent defaults
876 			if (newPlatformProperties[i].get(Constants.FRAMEWORK_OS_NAME) == null && newPlatformProperties[i].get(OSGI_OS) != null)
877 				newPlatformProperties[i].put(Constants.FRAMEWORK_OS_NAME, newPlatformProperties[i].get(OSGI_OS));
878 			if (newPlatformProperties[i].get(Constants.FRAMEWORK_PROCESSOR) == null && newPlatformProperties[i].get(OSGI_ARCH) != null)
879 				newPlatformProperties[i].put(Constants.FRAMEWORK_PROCESSOR, newPlatformProperties[i].get(OSGI_ARCH));
880 			if (newPlatformProperties[i].get(Constants.FRAMEWORK_LANGUAGE) == null && newPlatformProperties[i].get(OSGI_NL) instanceof String) {
881 				String osgiNL = (String) newPlatformProperties[i].get(OSGI_NL);
882 				int idx = osgiNL.indexOf('_');
883 				if (idx >= 0)
884 					osgiNL = osgiNL.substring(0, idx);
885 				newPlatformProperties[i].put(Constants.FRAMEWORK_LANGUAGE, osgiNL);
886 			}
887 
888 		}
889 		boolean result = false;
890 		boolean performResetSystemExports = false;
891 		boolean performResetSystemCapabilities = false;
892 		if (this.platformProperties.length != newPlatformProperties.length) {
893 			result = true;
894 			performResetSystemExports = true;
895 			performResetSystemCapabilities = true;
896 		} else {
897 			// we need to see if any of the existing filter prop keys have changed
898 			String[] keys = getPlatformPropertyKeys();
899 			for (int i = 0; i < newPlatformProperties.length && !result; i++) {
900 				result |= changedProps(this.platformProperties[i], newPlatformProperties[i], keys);
901 				if (resetSystemExports) {
902 					performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES));
903 					performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA));
904 					performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.SYSTEM_BUNDLE_SYMBOLICNAME), newPlatformProperties[i].get(Constants.SYSTEM_BUNDLE_SYMBOLICNAME));
905 					performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES));
906 					performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA));
907 					performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT), newPlatformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT));
908 				}
909 			}
910 		}
911 		// always do a complete replacement of the properties in case new bundles are added that uses new filter props
912 		this.platformProperties = newPlatformProperties;
913 		if (performResetSystemExports)
914 			resetSystemExports();
915 		if (performResetSystemCapabilities)
916 			resetSystemCapabilities();
917 		developmentMode = this.platformProperties.length == 0 ? false : DEVELOPMENT_MODE.equals(this.platformProperties[0].get(OSGI_RESOLVER_MODE));
918 		return result;
919 	}
920 
resetAllSystemCapabilities()921 	private void resetAllSystemCapabilities() {
922 		resetSystemExports();
923 		resetSystemCapabilities();
924 	}
925 
resetSystemExports()926 	private void resetSystemExports() {
927 		BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
928 		for (BundleDescription systemBundle : systemBundles) {
929 			BundleDescriptionImpl systemBundleImpl = (BundleDescriptionImpl) systemBundle;
930 			ExportPackageDescription[] exports = systemBundleImpl.getExportPackages();
931 			List<ExportPackageDescription> newExports = new ArrayList<>(exports.length);
932 			for (ExportPackageDescription export : exports) {
933 				if (((Integer) export.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() < 0) {
934 					newExports.add(export);
935 				}
936 			}
937 			addSystemExports(newExports);
938 			systemBundleImpl.setExportPackages(newExports.toArray(new ExportPackageDescription[newExports.size()]));
939 		}
940 	}
941 
addSystemExports(List<ExportPackageDescription> exports)942 	private void addSystemExports(List<ExportPackageDescription> exports) {
943 		for (int i = 0; i < platformProperties.length; i++)
944 			try {
945 				addSystemExports(exports, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES)), i);
946 				addSystemExports(exports, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)), i);
947 			} catch (BundleException e) {
948 				// TODO consider throwing this...
949 			}
950 	}
951 
addSystemExports(List<ExportPackageDescription> exports, ManifestElement[] elements, int index)952 	private void addSystemExports(List<ExportPackageDescription> exports, ManifestElement[] elements, int index) {
953 		if (elements == null)
954 			return;
955 		ExportPackageDescription[] systemExports = StateBuilder.createExportPackages(elements, null, null, false);
956 		Integer profInx = Integer.valueOf(index);
957 		for (ExportPackageDescription systemExport : systemExports) {
958 			((ExportPackageDescriptionImpl) systemExport).setDirective(ExportPackageDescriptionImpl.EQUINOX_EE, profInx);
959 			exports.add(systemExport);
960 		}
961 	}
962 
resetSystemCapabilities()963 	private void resetSystemCapabilities() {
964 		BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
965 		for (BundleDescription systemBundle : systemBundles) {
966 			GenericDescription[] capabilities = systemBundle.getGenericCapabilities();
967 			List<GenericDescription> newCapabilities = new ArrayList<>(capabilities.length);
968 			for (GenericDescription capability : capabilities) {
969 				Object equinoxEEIndex = capability.getDeclaredAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE);
970 				if (equinoxEEIndex == null)
971 					newCapabilities.add(capability); // keep the built in ones.
972 			}
973 			// now add the externally defined ones
974 			addSystemCapabilities(newCapabilities);
975 			((BundleDescriptionImpl) systemBundle).setGenericCapabilities(newCapabilities.toArray(new GenericDescription[newCapabilities.size()]));
976 		}
977 	}
978 
addSystemCapabilities(List<GenericDescription> capabilities)979 	private void addSystemCapabilities(List<GenericDescription> capabilities) {
980 		for (int i = 0; i < platformProperties.length; i++)
981 			try {
982 				addSystemCapabilities(capabilities, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES)), i);
983 				addSystemCapabilities(capabilities, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA)), i);
984 				checkOSGiEE(capabilities, (String) platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT), i);
985 			} catch (BundleException e) {
986 				// TODO consider throwing this...
987 			}
988 	}
989 
checkOSGiEE(List<GenericDescription> capabilities, String profileEE, Integer profileIndex)990 	private void checkOSGiEE(List<GenericDescription> capabilities, String profileEE, Integer profileIndex) {
991 		if (profileEE == null || profileEE.length() == 0)
992 			return;
993 		for (GenericDescription capability : capabilities) {
994 			if (OSGI_EE_NAMESPACE.equals(capability.getType()) && profileIndex.equals(capability.getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE)))
995 				return; // profile already specifies osgi.ee capabilities
996 		}
997 		Map<String, List<String>> eeVersions = new HashMap<>();
998 		String[] ees = ManifestElement.getArrayFromList(profileEE);
999 		for (String ee : ees) {
1000 			String[] eeNameVersion = StateBuilder.getOSGiEENameVersion(ee);
1001 
1002 			List<String> versions = eeVersions.get(eeNameVersion[0]);
1003 			if (versions == null) {
1004 				versions = new ArrayList<>();
1005 				eeVersions.put(eeNameVersion[0], versions);
1006 			}
1007 			if (eeNameVersion[1] != null && !versions.contains(eeNameVersion[1]))
1008 				versions.add(eeNameVersion[1]);
1009 		}
1010 		for (Map.Entry<String, List<String>> eeVersion : eeVersions.entrySet()) {
1011 			GenericDescriptionImpl capability = new GenericDescriptionImpl();
1012 			capability.setType(OSGI_EE_NAMESPACE);
1013 			Dictionary<String, Object> attributes = new Hashtable<>();
1014 			attributes.put(capability.getType(), eeVersion.getKey());
1015 			if (eeVersion.getValue().size() > 0) {
1016 				List<Version> versions = new ArrayList<>(eeVersion.getValue().size());
1017 				for (String version : eeVersion.getValue()) {
1018 					versions.add(new Version(version));
1019 				}
1020 				attributes.put("version", versions); //$NON-NLS-1$
1021 			}
1022 			attributes.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex);
1023 			capability.setAttributes(attributes);
1024 			capabilities.add(capability);
1025 		}
1026 	}
1027 
addSystemCapabilities(List<GenericDescription> capabilities, ManifestElement[] elements, Integer profileIndex)1028 	private void addSystemCapabilities(List<GenericDescription> capabilities, ManifestElement[] elements, Integer profileIndex) {
1029 		try {
1030 			StateBuilder.createOSGiCapabilities(elements, capabilities, profileIndex);
1031 		} catch (BundleException e) {
1032 			throw new RuntimeException("Unexpected exception adding system capabilities.", e); //$NON-NLS-1$
1033 		}
1034 	}
1035 
getPlatformProperties()1036 	public Dictionary<Object, Object>[] getPlatformProperties() {
1037 		return platformProperties;
1038 	}
1039 
checkProp(Object origObj, Object newObj)1040 	private boolean checkProp(Object origObj, Object newObj) {
1041 		if ((origObj == null && newObj != null) || (origObj != null && newObj == null))
1042 			return true;
1043 		if (origObj == null)
1044 			return false;
1045 		if (origObj.getClass() != newObj.getClass())
1046 			return true;
1047 		if (origObj instanceof String[]) {
1048 			String[] origProps = (String[]) origObj;
1049 			String[] newProps = (String[]) newObj;
1050 			if (origProps.length != newProps.length)
1051 				return true;
1052 			for (int i = 0; i < origProps.length; i++) {
1053 				if (!origProps[i].equals(newProps[i]))
1054 					return true;
1055 			}
1056 			return false;
1057 		}
1058 		return !origObj.equals(newObj);
1059 	}
1060 
changedProps(Dictionary<Object, Object> origProps, Dictionary<Object, Object> newProps, String[] keys)1061 	private boolean changedProps(Dictionary<Object, Object> origProps, Dictionary<Object, Object> newProps, String[] keys) {
1062 		for (String key : keys) {
1063 			Object origProp = origProps.get(key);
1064 			Object newProp = newProps.get(key);
1065 			if (checkProp(origProp, newProp))
1066 				return true;
1067 		}
1068 		return false;
1069 	}
1070 
getSystemBundle()1071 	public String getSystemBundle() {
1072 		String symbolicName = null;
1073 		if (platformProperties != null && platformProperties.length > 0)
1074 			symbolicName = (String) platformProperties[0].get(STATE_SYSTEM_BUNDLE);
1075 		return symbolicName != null ? symbolicName : EquinoxContainer.NAME;
1076 	}
1077 
getRemovalPending()1078 	public BundleDescription[] getRemovalPending() {
1079 		synchronized (this.monitor) {
1080 			return removalPendings.toArray(new BundleDescription[removalPendings.size()]);
1081 		}
1082 	}
1083 
addRemovalPending(BundleDescription removed)1084 	private void addRemovalPending(BundleDescription removed) {
1085 		synchronized (this.monitor) {
1086 			if (!removalPendings.contains(removed))
1087 				removalPendings.addFirst(removed);
1088 		}
1089 	}
1090 
getDependencyClosure(Collection<BundleDescription> bundles)1091 	public Collection<BundleDescription> getDependencyClosure(Collection<BundleDescription> bundles) {
1092 		BundleDescription[] removals = getRemovalPending();
1093 		Set<BundleDescription> result = new HashSet<>();
1094 		for (BundleDescription bundle : bundles) {
1095 			addDependents(bundle, result, removals);
1096 		}
1097 		return result;
1098 	}
1099 
addDependents(BundleDescription bundle, Set<BundleDescription> result, BundleDescription[] removals)1100 	private static void addDependents(BundleDescription bundle, Set<BundleDescription> result, BundleDescription[] removals) {
1101 		if (result.contains(bundle))
1102 			return; // avoid cycles
1103 		result.add(bundle);
1104 		BundleDescription[] dependents = bundle.getDependents();
1105 		for (BundleDescription dependent : dependents)
1106 			addDependents(dependent, result, removals);
1107 		// check if this is a removal pending
1108 		for (BundleDescription removed : removals) {
1109 			if (removed.getBundleId() == bundle.getBundleId())
1110 				addDependents(removed, result, removals);
1111 		}
1112 	}
1113 
1114 	/**
1115 	 * Returns the latest versions BundleDescriptions which have old removal pending versions.
1116 	 * @return the BundleDescriptions that have removal pending versions.
1117 	 */
internalGetRemovalPending()1118 	private BundleDescription[] internalGetRemovalPending() {
1119 		synchronized (this.monitor) {
1120 			Iterator<BundleDescription> removed = removalPendings.iterator();
1121 			BundleDescription[] result = new BundleDescription[removalPendings.size()];
1122 			int i = 0;
1123 			while (removed.hasNext())
1124 				// we return the latest version of the description if it is still contained in the state (bug 287636)
1125 				result[i++] = getBundle(removed.next().getBundleId());
1126 			return result;
1127 		}
1128 	}
1129 
linkDynamicImport(BundleDescription importingBundle, String requestedPackage)1130 	public ExportPackageDescription linkDynamicImport(BundleDescription importingBundle, String requestedPackage) {
1131 		if (resolver == null)
1132 			throw new IllegalStateException("no resolver set"); //$NON-NLS-1$
1133 		BundleDescriptionImpl importer = (BundleDescriptionImpl) importingBundle;
1134 		if (importer.getDynamicStamp(requestedPackage) == getTimeStamp())
1135 			return null;
1136 		fullyLoad();
1137 		synchronized (this.monitor) {
1138 			ResolverHook currentHook = null;
1139 			try {
1140 				resolving = true;
1141 				ResolverHookFactory currentFactory = hookFactory;
1142 				if (currentFactory != null) {
1143 					Collection<BundleRevision> triggers = new ArrayList<>(1);
1144 					triggers.add(importingBundle);
1145 					triggers = Collections.unmodifiableCollection(triggers);
1146 					currentHook = begin(triggers);
1147 				}
1148 				// ask the resolver to resolve our dynamic import
1149 				ExportPackageDescriptionImpl result = (ExportPackageDescriptionImpl) resolver.resolveDynamicImport(importingBundle, requestedPackage);
1150 				if (result == null)
1151 					importer.setDynamicStamp(requestedPackage, new Long(getTimeStamp()));
1152 				else {
1153 					importer.setDynamicStamp(requestedPackage, null); // remove any cached timestamp
1154 					// need to add the result to the list of resolved imports
1155 					importer.addDynamicResolvedImport(result);
1156 				}
1157 				setDynamicCacheChanged(true);
1158 				return result;
1159 			} finally {
1160 				resolving = false;
1161 				if (currentHook != null)
1162 					currentHook.end();
1163 			}
1164 		}
1165 
1166 	}
1167 
addDynamicImportPackages(BundleDescription importingBundle, ImportPackageSpecification[] dynamicImports)1168 	public void addDynamicImportPackages(BundleDescription importingBundle, ImportPackageSpecification[] dynamicImports) {
1169 		synchronized (this.monitor) {
1170 			((BundleDescriptionImpl) importingBundle).addDynamicImportPackages(dynamicImports);
1171 			setDynamicCacheChanged(true);
1172 		}
1173 	}
1174 
setReader(StateReader reader)1175 	void setReader(StateReader reader) {
1176 		synchronized (this.monitor) {
1177 			this.reader = reader;
1178 		}
1179 	}
1180 
getReader()1181 	StateReader getReader() {
1182 		synchronized (this.monitor) {
1183 			return reader;
1184 		}
1185 	}
1186 
1187 	// not synchronized on this to prevent deadlock
fullyLoad()1188 	public final void fullyLoad() {
1189 		synchronized (this.monitor) {
1190 			if (reader == null)
1191 				return;
1192 			if (fullyLoaded == true)
1193 				return;
1194 			if (reader.isLazyLoaded())
1195 				reader.fullyLoad();
1196 			fullyLoaded = true;
1197 		}
1198 	}
1199 
1200 	// not synchronized on this to prevent deadlock
unloadLazyData(long checkStamp)1201 	public final boolean unloadLazyData(long checkStamp) {
1202 		// make sure no other thread is trying to unload or load
1203 		synchronized (this.monitor) {
1204 			if (checkStamp != getTimeStamp() || dynamicCacheChanged())
1205 				return false;
1206 			if (reader.getAccessedFlag()) {
1207 				reader.setAccessedFlag(false); // reset accessed flag
1208 				return true;
1209 			}
1210 			fullyLoaded = false;
1211 			BundleDescription[] bundles = getBundles();
1212 			for (BundleDescription bundle : bundles) {
1213 				((BundleDescriptionImpl) bundle).unload();
1214 			}
1215 			reader.flushLazyObjectCache();
1216 			resolver.flush();
1217 			return true;
1218 		}
1219 	}
1220 
getSystemPackages()1221 	public ExportPackageDescription[] getSystemPackages() {
1222 		synchronized (this.monitor) {
1223 			List<ExportPackageDescription> result = new ArrayList<>();
1224 			BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
1225 			if (systemBundles.length > 0) {
1226 				BundleDescriptionImpl systemBundle = (BundleDescriptionImpl) systemBundles[0];
1227 				ExportPackageDescription[] exports = systemBundle.getExportPackages();
1228 				for (ExportPackageDescription export : exports) {
1229 					if (((Integer) export.getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() >= 0) {
1230 						result.add(export);
1231 					}
1232 				}
1233 			}
1234 			return result.toArray(new ExportPackageDescription[result.size()]);
1235 		}
1236 	}
1237 
inStrictMode()1238 	boolean inStrictMode() {
1239 		synchronized (this.monitor) {
1240 			return STRICT_MODE.equals(getPlatformProperties()[0].get(OSGI_RESOLVER_MODE));
1241 		}
1242 	}
1243 
getResolverErrors(BundleDescription bundle)1244 	public ResolverError[] getResolverErrors(BundleDescription bundle) {
1245 		synchronized (this.monitor) {
1246 			if (bundle.isResolved())
1247 				return new ResolverError[0];
1248 			List<ResolverError> result = resolverErrors.get(bundle);
1249 			return result == null ? new ResolverError[0] : result.toArray(new ResolverError[result.size()]);
1250 		}
1251 	}
1252 
addResolverError(BundleDescription bundle, int type, String data, VersionConstraint unsatisfied)1253 	public void addResolverError(BundleDescription bundle, int type, String data, VersionConstraint unsatisfied) {
1254 		synchronized (this.monitor) {
1255 			if (!resolving)
1256 				throw new IllegalStateException(); // TODO need error message here!
1257 			List<ResolverError> errors = resolverErrors.get(bundle);
1258 			if (errors == null) {
1259 				errors = new ArrayList<>(1);
1260 				resolverErrors.put(bundle, errors);
1261 			}
1262 			errors.add(new ResolverErrorImpl((BundleDescriptionImpl) bundle, type, data, unsatisfied));
1263 		}
1264 	}
1265 
removeResolverErrors(BundleDescription bundle)1266 	public void removeResolverErrors(BundleDescription bundle) {
1267 		synchronized (this.monitor) {
1268 			if (!resolving)
1269 				throw new IllegalStateException(); // TODO need error message here!
1270 			resolverErrors.remove(bundle);
1271 		}
1272 	}
1273 
dynamicCacheChanged()1274 	public boolean dynamicCacheChanged() {
1275 		synchronized (this.monitor) {
1276 			return dynamicCacheChanged;
1277 		}
1278 	}
1279 
setDynamicCacheChanged(boolean dynamicCacheChanged)1280 	void setDynamicCacheChanged(boolean dynamicCacheChanged) {
1281 		synchronized (this.monitor) {
1282 			this.dynamicCacheChanged = dynamicCacheChanged;
1283 		}
1284 	}
1285 
getStateHelper()1286 	public StateHelper getStateHelper() {
1287 		return StateHelperImpl.getInstance();
1288 	}
1289 
addPlatformPropertyKeys(String[] keys)1290 	void addPlatformPropertyKeys(String[] keys) {
1291 		synchronized (platformPropertyKeys) {
1292 			for (String key : keys) {
1293 				if (!platformPropertyKeys.contains(key)) {
1294 					platformPropertyKeys.add(key);
1295 				}
1296 			}
1297 		}
1298 	}
1299 
getPlatformPropertyKeys()1300 	String[] getPlatformPropertyKeys() {
1301 		synchronized (platformPropertyKeys) {
1302 			return platformPropertyKeys.toArray(new String[platformPropertyKeys.size()]);
1303 		}
1304 	}
1305 
getHighestBundleId()1306 	public long getHighestBundleId() {
1307 		synchronized (this.monitor) {
1308 			return highestBundleId;
1309 		}
1310 	}
1311 
setNativePathsInvalid(NativeCodeDescription nativeCodeDescription, boolean hasInvalidNativePaths)1312 	public void setNativePathsInvalid(NativeCodeDescription nativeCodeDescription, boolean hasInvalidNativePaths) {
1313 		((NativeCodeDescriptionImpl) nativeCodeDescription).setInvalidNativePaths(hasInvalidNativePaths);
1314 	}
1315 
getDisabledBundles()1316 	public BundleDescription[] getDisabledBundles() {
1317 		synchronized (this.monitor) {
1318 			return disabledBundles.keySet().toArray(new BundleDescription[0]);
1319 		}
1320 	}
1321 
addDisabledInfo(DisabledInfo disabledInfo)1322 	public void addDisabledInfo(DisabledInfo disabledInfo) {
1323 		synchronized (this.monitor) {
1324 			if (getBundle(disabledInfo.getBundle().getBundleId()) != disabledInfo.getBundle())
1325 				throw new IllegalArgumentException(NLS.bind(StateMsg.BUNDLE_NOT_IN_STATE, disabledInfo.getBundle()));
1326 			List<DisabledInfo> currentInfos = disabledBundles.get(disabledInfo.getBundle());
1327 			if (currentInfos == null) {
1328 				currentInfos = new ArrayList<>(1);
1329 				currentInfos.add(disabledInfo);
1330 				disabledBundles.put(disabledInfo.getBundle(), currentInfos);
1331 			} else {
1332 				Iterator<DisabledInfo> it = currentInfos.iterator();
1333 				while (it.hasNext()) {
1334 					DisabledInfo currentInfo = it.next();
1335 					if (disabledInfo.getPolicyName().equals(currentInfo.getPolicyName())) {
1336 						currentInfos.remove(currentInfo);
1337 						break;
1338 					}
1339 				}
1340 				currentInfos.add(disabledInfo);
1341 			}
1342 			updateTimeStamp();
1343 		}
1344 	}
1345 
removeDisabledInfo(DisabledInfo disabledInfo)1346 	public void removeDisabledInfo(DisabledInfo disabledInfo) {
1347 		synchronized (this.monitor) {
1348 			List<DisabledInfo> currentInfos = disabledBundles.get(disabledInfo.getBundle());
1349 			if ((currentInfos != null) && currentInfos.contains(disabledInfo)) {
1350 				currentInfos.remove(disabledInfo);
1351 				if (currentInfos.isEmpty()) {
1352 					disabledBundles.remove(disabledInfo.getBundle());
1353 				}
1354 			}
1355 			updateTimeStamp();
1356 		}
1357 	}
1358 
getDisabledInfo(BundleDescription bundle, String policyName)1359 	public DisabledInfo getDisabledInfo(BundleDescription bundle, String policyName) {
1360 		synchronized (this.monitor) {
1361 			List<DisabledInfo> currentInfos = disabledBundles.get(bundle);
1362 			if (currentInfos == null)
1363 				return null;
1364 			Iterator<DisabledInfo> it = currentInfos.iterator();
1365 			while (it.hasNext()) {
1366 				DisabledInfo currentInfo = it.next();
1367 				if (currentInfo.getPolicyName().equals(policyName)) {
1368 					return currentInfo;
1369 				}
1370 			}
1371 			return null;
1372 		}
1373 	}
1374 
getDisabledInfos(BundleDescription bundle)1375 	public DisabledInfo[] getDisabledInfos(BundleDescription bundle) {
1376 		synchronized (this.monitor) {
1377 			List<DisabledInfo> currentInfos = disabledBundles.get(bundle);
1378 			return currentInfos == null ? EMPTY_DISABLEDINFOS : currentInfos.toArray(new DisabledInfo[currentInfos.size()]);
1379 		}
1380 	}
1381 
1382 	/*
1383 	 * Used by StateWriter to get all the DisabledInfo objects to persist
1384 	 */
getDisabledInfos()1385 	DisabledInfo[] getDisabledInfos() {
1386 		List<DisabledInfo> results = new ArrayList<>();
1387 		synchronized (this.monitor) {
1388 			for (Iterator<List<DisabledInfo>> allDisabledInfos = disabledBundles.values().iterator(); allDisabledInfos.hasNext();)
1389 				results.addAll(allDisabledInfos.next());
1390 		}
1391 		return results.toArray(new DisabledInfo[results.size()]);
1392 	}
1393 
addBundleNameCacheEntry(BundleDescription description)1394 	private void addBundleNameCacheEntry(BundleDescription description) {
1395 		Set<BundleDescription> descriptions = bundleNameCache.get(description.getSymbolicName());
1396 		if (descriptions == null) {
1397 			descriptions = new LinkedHashSet<>();
1398 			bundleNameCache.put(description.getSymbolicName(), descriptions);
1399 		}
1400 		descriptions.add(description);
1401 	}
1402 
removeBundleNameCacheEntry(BundleDescription description)1403 	private void removeBundleNameCacheEntry(BundleDescription description) {
1404 		Set<BundleDescription> descriptions = bundleNameCache.get(description.getSymbolicName());
1405 		if (descriptions != null) {
1406 			descriptions.remove(description);
1407 			if (descriptions.isEmpty()) {
1408 				bundleNameCache.remove(description.getSymbolicName());
1409 			}
1410 		}
1411 	}
1412 }
1413