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 "reprovide") 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 "optional") 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