| /******************************************************************************* |
| * Copyright (c) 2003, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Danail Nachev - ProSyst - bug 218625 |
| * Rob Harrop - SpringSource Inc. (bug 247522) |
| * Karsten Thoms (itemis) - bug 535341 |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.resolver; |
| |
| import java.util.*; |
| import org.eclipse.osgi.framework.util.*; |
| import org.eclipse.osgi.internal.framework.EquinoxContainer; |
| import org.eclipse.osgi.internal.framework.FilterImpl; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| import org.osgi.framework.hooks.resolver.ResolverHook; |
| import org.osgi.framework.hooks.resolver.ResolverHookFactory; |
| import org.osgi.framework.wiring.BundleRevision; |
| |
| public abstract class StateImpl implements State { |
| |
| public static final String ECLIPSE_PLATFORMFILTER = "Eclipse-PlatformFilter"; //$NON-NLS-1$ |
| public static final String Eclipse_JREBUNDLE = "Eclipse-JREBundle"; //$NON-NLS-1$ |
| /** |
| * Manifest Export-Package directive indicating that the exported package should only |
| * be made available when the resolver is not in strict mode. |
| */ |
| public static final String INTERNAL_DIRECTIVE = "x-internal"; //$NON-NLS-1$ |
| |
| /** |
| * Manifest Export-Package directive indicating that the exported package should only |
| * be made available to friends of the exporting bundle. |
| */ |
| public static final String FRIENDS_DIRECTIVE = "x-friends"; //$NON-NLS-1$ |
| |
| /** |
| * Manifest header (named "Provide-Package") |
| * identifying the packages name |
| * provided to other bundles which require the bundle. |
| * |
| * <p> |
| * NOTE: this is only used for backwards compatibility, bundles manifest using |
| * syntax version 2 will not recognize this header. |
| * |
| * <p>The attribute value may be retrieved from the |
| * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method. |
| * @deprecated |
| */ |
| public final static String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$ |
| |
| /** |
| * Manifest header attribute (named "reprovide") |
| * for Require-Bundle |
| * identifying that any packages that are provided |
| * by the required bundle must be reprovided by the requiring bundle. |
| * The default value is <tt>false</tt>. |
| * <p> |
| * The attribute value is encoded in the Require-Bundle manifest |
| * header like: |
| * <pre> |
| * Require-Bundle: com.acme.module.test; reprovide="true" |
| * </pre> |
| * <p> |
| * NOTE: this is only used for backwards compatibility, bundles manifest using |
| * syntax version 2 will not recognize this attribute. |
| * @deprecated |
| */ |
| public final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$ |
| |
| /** |
| * Manifest header attribute (named "optional") |
| * for Require-Bundle |
| * identifying that a required bundle is optional and that |
| * the requiring bundle can be resolved if there is no |
| * suitable required bundle. |
| * The default value is <tt>false</tt>. |
| * |
| * <p>The attribute value is encoded in the Require-Bundle manifest |
| * header like: |
| * <pre> |
| * Require-Bundle: com.acme.module.test; optional="true" |
| * </pre> |
| * <p> |
| * NOTE: this is only used for backwards compatibility, bundles manifest using |
| * syntax version 2 will not recognize this attribute. |
| * @since 1.3 <b>EXPERIMENTAL</b> |
| * @deprecated |
| */ |
| public final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$ |
| |
| public static final String OSGI_RESOLVER_MODE = "osgi.resolverMode"; //$NON-NLS-1$ |
| public static final String STRICT_MODE = "strict"; //$NON-NLS-1$ |
| public static final String DEVELOPMENT_MODE = "development"; //$NON-NLS-1$ |
| |
| public static final String STATE_SYSTEM_BUNDLE = "osgi.system.bundle"; //$NON-NLS-1$ |
| |
| private static final String OSGI_OS = "osgi.os"; //$NON-NLS-1$ |
| private static final String OSGI_WS = "osgi.ws"; //$NON-NLS-1$ |
| private static final String OSGI_NL = "osgi.nl"; //$NON-NLS-1$ |
| private static final String OSGI_ARCH = "osgi.arch"; //$NON-NLS-1$ |
| 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$ |
| private static final DisabledInfo[] EMPTY_DISABLEDINFOS = new DisabledInfo[0]; |
| public static final String OSGI_EE_NAMESPACE = "osgi.ee"; //$NON-NLS-1$ |
| |
| transient private Resolver resolver; |
| transient private StateDeltaImpl changes; |
| transient private boolean resolving = false; |
| transient private LinkedList<BundleDescription> removalPendings = new LinkedList<>(); |
| |
| private boolean resolved = true; |
| private long timeStamp = System.currentTimeMillis(); |
| private final KeyedHashSet bundleDescriptions = new KeyedHashSet(false); |
| private final Map<BundleDescription, List<ResolverError>> resolverErrors = new HashMap<>(); |
| private StateObjectFactory factory; |
| private final KeyedHashSet resolvedBundles = new KeyedHashSet(); |
| private final Map<BundleDescription, List<DisabledInfo>> disabledBundles = new HashMap<>(); |
| private final Map<String, Set<BundleDescription>> bundleNameCache = new HashMap<>(); |
| private boolean fullyLoaded = false; |
| private boolean dynamicCacheChanged = false; |
| // only used for lazy loading of BundleDescriptions |
| private StateReader reader; |
| @SuppressWarnings("unchecked") |
| private Dictionary<Object, Object>[] platformProperties = new Dictionary[] {new Hashtable<String, String>(PROPS.length)}; // Dictionary here because of Filter API |
| private long highestBundleId = -1; |
| private final Set<String> platformPropertyKeys = new HashSet<>(PROPS.length); |
| private ResolverHookFactory hookFactory; |
| private ResolverHook hook; |
| private boolean developmentMode = false; |
| |
| final Object monitor = new Object(); |
| |
| // to prevent extra-package instantiation |
| protected StateImpl() { |
| // always add the default platform property keys. |
| addPlatformPropertyKeys(PROPS); |
| } |
| |
| public boolean addBundle(BundleDescription description) { |
| synchronized (this.monitor) { |
| if (!basicAddBundle(description)) |
| return false; |
| String platformFilter = description.getPlatformFilter(); |
| if (platformFilter != null) { |
| try { |
| // add any new platform filter propery keys this bundle is using |
| FilterImpl filter = FilterImpl.newInstance(platformFilter); |
| addPlatformPropertyKeys(filter.getAttributes()); |
| } catch (InvalidSyntaxException e) { |
| // ignore this is handled in another place |
| } |
| } |
| NativeCodeSpecification nativeCode = description.getNativeCodeSpecification(); |
| if (nativeCode != null) { |
| NativeCodeDescription[] suppliers = nativeCode.getPossibleSuppliers(); |
| for (int i = 0; i < suppliers.length; i++) { |
| FilterImpl filter = (FilterImpl) suppliers[i].getFilter(); |
| if (filter != null) |
| addPlatformPropertyKeys(filter.getAttributes()); |
| } |
| } |
| resolved = false; |
| getDelta().recordBundleAdded((BundleDescriptionImpl) description); |
| if (getSystemBundle().equals(description.getSymbolicName())) |
| resetAllSystemCapabilities(); |
| if (resolver != null) |
| resolver.bundleAdded(description); |
| updateTimeStamp(); |
| return true; |
| } |
| } |
| |
| public boolean updateBundle(BundleDescription newDescription) { |
| synchronized (this.monitor) { |
| BundleDescriptionImpl existing = (BundleDescriptionImpl) bundleDescriptions.get((BundleDescriptionImpl) newDescription); |
| if (existing == null) |
| return false; |
| if (!bundleDescriptions.remove(existing)) |
| return false; |
| removeBundleNameCacheEntry(existing); |
| resolvedBundles.remove(existing); |
| List<DisabledInfo> infos = disabledBundles.remove(existing); |
| if (infos != null) { |
| List<DisabledInfo> newInfos = new ArrayList<>(infos.size()); |
| for (Iterator<DisabledInfo> iInfos = infos.iterator(); iInfos.hasNext();) { |
| DisabledInfo info = iInfos.next(); |
| newInfos.add(new DisabledInfo(info.getPolicyName(), info.getMessage(), newDescription)); |
| } |
| disabledBundles.put(newDescription, newInfos); |
| } |
| existing.setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true); |
| if (!basicAddBundle(newDescription)) |
| return false; |
| resolved = false; |
| getDelta().recordBundleUpdated((BundleDescriptionImpl) newDescription); |
| if (getSystemBundle().equals(newDescription.getSymbolicName())) |
| resetAllSystemCapabilities(); |
| if (resolver != null) { |
| boolean pending = isInUse(existing); |
| resolver.bundleUpdated(newDescription, existing, pending); |
| if (pending) { |
| getDelta().recordBundleRemovalPending(existing); |
| addRemovalPending(existing); |
| } else { |
| // an existing bundle has been updated with no dependents it can safely be unresolved now |
| try { |
| resolving = true; |
| resolverErrors.remove(existing); |
| resolveBundle(existing, false, null, null, null, null, null, null, null, null); |
| } finally { |
| resolving = false; |
| } |
| } |
| } |
| updateTimeStamp(); |
| return true; |
| } |
| } |
| |
| public BundleDescription removeBundle(long bundleId) { |
| synchronized (this.monitor) { |
| BundleDescription toRemove = getBundle(bundleId); |
| if (toRemove == null || !removeBundle(toRemove)) |
| return null; |
| return toRemove; |
| } |
| } |
| |
| public boolean removeBundle(BundleDescription toRemove) { |
| synchronized (this.monitor) { |
| toRemove = (BundleDescription) bundleDescriptions.get((KeyedElement) toRemove); |
| if (toRemove == null || !bundleDescriptions.remove((KeyedElement) toRemove)) |
| return false; |
| resolvedBundles.remove((KeyedElement) toRemove); |
| disabledBundles.remove(toRemove); |
| removeBundleNameCacheEntry(toRemove); |
| resolved = false; |
| getDelta().recordBundleRemoved((BundleDescriptionImpl) toRemove); |
| ((BundleDescriptionImpl) toRemove).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true); |
| if (resolver != null) { |
| boolean pending = isInUse(toRemove); |
| resolver.bundleRemoved(toRemove, pending); |
| if (pending) { |
| getDelta().recordBundleRemovalPending((BundleDescriptionImpl) toRemove); |
| addRemovalPending(toRemove); |
| } else { |
| // a bundle has been removed with no dependents it can safely be unresolved now |
| try { |
| resolving = true; |
| resolverErrors.remove(toRemove); |
| resolveBundle(toRemove, false, null, null, null, null, null); |
| } finally { |
| resolving = false; |
| } |
| } |
| } |
| updateTimeStamp(); |
| return true; |
| } |
| } |
| |
| private boolean isInUse(BundleDescription bundle) { |
| return bundle.getDependents().length > 0; |
| } |
| |
| public StateDelta getChanges() { |
| synchronized (this.monitor) { |
| return getDelta(); |
| } |
| } |
| |
| private StateDeltaImpl getDelta() { |
| if (changes == null) |
| changes = new StateDeltaImpl(this); |
| return changes; |
| } |
| |
| public BundleDescription[] getBundles(String symbolicName) { |
| synchronized (this.monitor) { |
| if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) |
| symbolicName = getSystemBundle(); |
| final Set<BundleDescription> bundles = bundleNameCache.get(symbolicName); |
| if (bundles == null) { |
| return new BundleDescription[0]; |
| } |
| return bundles.toArray(new BundleDescription[bundles.size()]); |
| } |
| } |
| |
| public BundleDescription[] getBundles() { |
| synchronized (this.monitor) { |
| return (BundleDescription[]) bundleDescriptions.elements(new BundleDescription[bundleDescriptions.size()]); |
| } |
| } |
| |
| public BundleDescription getBundle(long id) { |
| synchronized (this.monitor) { |
| BundleDescription result = (BundleDescription) bundleDescriptions.getByKey(new Long(id)); |
| if (result != null) |
| return result; |
| // need to look in removal pending bundles; |
| for (Iterator<BundleDescription> iter = removalPendings.iterator(); iter.hasNext();) { |
| BundleDescription removedBundle = iter.next(); |
| if (removedBundle.getBundleId() == id) // just return the first matching id |
| return removedBundle; |
| } |
| return null; |
| } |
| } |
| |
| public BundleDescription getBundle(String name, Version version) { |
| synchronized (this.monitor) { |
| BundleDescription[] allBundles = getBundles(name); |
| if (allBundles.length == 1) |
| return version == null || allBundles[0].getVersion().equals(version) ? allBundles[0] : null; |
| if (allBundles.length == 0) |
| return null; |
| BundleDescription unresolvedFound = null; |
| BundleDescription resolvedFound = null; |
| for (int i = 0; i < allBundles.length; i++) { |
| BundleDescription current = allBundles[i]; |
| BundleDescription base; |
| |
| if (current.isResolved()) |
| base = resolvedFound; |
| else |
| base = unresolvedFound; |
| |
| if (version == null || current.getVersion().equals(version)) { |
| if (base != null && (base.getVersion().compareTo(current.getVersion()) <= 0 || base.getBundleId() > current.getBundleId())) { |
| if (base == resolvedFound) |
| resolvedFound = current; |
| else |
| unresolvedFound = current; |
| } else { |
| if (current.isResolved()) |
| resolvedFound = current; |
| else |
| unresolvedFound = current; |
| } |
| |
| } |
| } |
| if (resolvedFound != null) |
| return resolvedFound; |
| return unresolvedFound; |
| } |
| } |
| |
| public long getTimeStamp() { |
| synchronized (this.monitor) { |
| return timeStamp; |
| } |
| } |
| |
| public boolean isResolved() { |
| synchronized (this.monitor) { |
| return resolved || isEmpty(); |
| } |
| } |
| |
| public void resolveConstraint(VersionConstraint constraint, BaseDescription supplier) { |
| ((VersionConstraintImpl) constraint).setSupplier(supplier); |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) { |
| resolveBundle(bundle, status, hosts, selectedExports, null, resolvedRequires, resolvedImports); |
| } |
| |
| /** |
| * @deprecated |
| */ |
| public void resolveBundle(BundleDescription bundle, boolean status, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports) { |
| resolveBundle(bundle, status, hosts, selectedExports, substitutedExports, null, resolvedRequires, resolvedImports, null, null); |
| } |
| |
| 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) { |
| synchronized (this.monitor) { |
| if (!resolving) |
| throw new IllegalStateException(); // TODO need error message here! |
| BundleDescriptionImpl modifiable = (BundleDescriptionImpl) bundle; |
| // must record the change before setting the resolve state to |
| // accurately record if a change has happened. |
| getDelta().recordBundleResolved(modifiable, status); |
| // force the new resolution data to stay in memory; we will not read this from disk anymore |
| modifiable.setLazyLoaded(false); |
| modifiable.setStateBit(BundleDescriptionImpl.RESOLVED, status); |
| if (status) { |
| resolverErrors.remove(modifiable); |
| resolvedBundles.add(modifiable); |
| } else { |
| // remove the bundle from the resolved pool |
| resolvedBundles.remove(modifiable); |
| modifiable.removeDependencies(); |
| } |
| // to support development mode we will resolveConstraints even if the resolve status == false |
| // we only do this if the resolved constraints are not null |
| if (selectedExports == null || resolvedRequires == null || resolvedImports == null) |
| unresolveConstraints(modifiable); |
| else |
| resolveConstraints(modifiable, hosts, selectedExports, substitutedExports, selectedCapabilities, resolvedRequires, resolvedImports, resolvedCapabilities, resolvedWires); |
| } |
| } |
| |
| public void removeBundleComplete(BundleDescription bundle) { |
| synchronized (this.monitor) { |
| if (!resolving) |
| throw new IllegalStateException(); // TODO need error message here! |
| getDelta().recordBundleRemovalComplete((BundleDescriptionImpl) bundle); |
| removalPendings.remove(bundle); |
| } |
| } |
| |
| private void resolveConstraints(BundleDescriptionImpl bundle, BundleDescription[] hosts, ExportPackageDescription[] selectedExports, ExportPackageDescription[] substitutedExports, GenericDescription[] selectedCapabilities, BundleDescription[] resolvedRequires, ExportPackageDescription[] resolvedImports, GenericDescription[] resolvedCapabilities, Map<String, List<StateWire>> resolvedWires) { |
| HostSpecificationImpl hostSpec = (HostSpecificationImpl) bundle.getHost(); |
| if (hostSpec != null) { |
| if (hosts != null) { |
| hostSpec.setHosts(hosts); |
| for (int i = 0; i < hosts.length; i++) { |
| ((BundleDescriptionImpl) hosts[i]).addDependency(bundle, true); |
| checkHostForSubstitutedExports((BundleDescriptionImpl) hosts[i], bundle); |
| } |
| } |
| } |
| |
| bundle.setSelectedExports(selectedExports); |
| bundle.setResolvedRequires(resolvedRequires); |
| bundle.setResolvedImports(resolvedImports); |
| bundle.setSubstitutedExports(substitutedExports); |
| bundle.setSelectedCapabilities(selectedCapabilities); |
| bundle.setResolvedCapabilities(resolvedCapabilities); |
| bundle.setStateWires(resolvedWires); |
| |
| bundle.addDependencies(hosts, true); |
| bundle.addDependencies(resolvedRequires, true); |
| bundle.addDependencies(resolvedImports, true); |
| bundle.addDependencies(resolvedCapabilities, true); |
| } |
| |
| private void checkHostForSubstitutedExports(BundleDescriptionImpl host, BundleDescriptionImpl fragment) { |
| // TODO need to handle this case where a fragment has its own export substituted |
| // there are issues here because the order in which fragments are resolved is not always the same ... |
| } |
| |
| private void unresolveConstraints(BundleDescriptionImpl bundle) { |
| HostSpecificationImpl host = (HostSpecificationImpl) bundle.getHost(); |
| if (host != null) |
| host.setHosts(null); |
| |
| bundle.setSelectedExports(null); |
| bundle.setResolvedImports(null); |
| bundle.setResolvedRequires(null); |
| bundle.setSubstitutedExports(null); |
| bundle.setSelectedCapabilities(null); |
| bundle.setResolvedCapabilities(null); |
| bundle.setStateWires(null); |
| bundle.clearAddedDynamicImportPackages(); |
| |
| // remove the constraint suppliers |
| NativeCodeSpecificationImpl nativeCode = (NativeCodeSpecificationImpl) bundle.getNativeCodeSpecification(); |
| if (nativeCode != null) |
| nativeCode.setSupplier(null); |
| ImportPackageSpecification[] imports = bundle.getImportPackages(); |
| for (int i = 0; i < imports.length; i++) |
| ((ImportPackageSpecificationImpl) imports[i]).setSupplier(null); |
| BundleSpecification[] requires = bundle.getRequiredBundles(); |
| for (int i = 0; i < requires.length; i++) |
| ((BundleSpecificationImpl) requires[i]).setSupplier(null); |
| GenericSpecification[] genericRequires = bundle.getGenericRequires(); |
| if (genericRequires.length > 0) |
| for (int i = 0; i < genericRequires.length; i++) |
| ((GenericSpecificationImpl) genericRequires[i]).setSupplers(null); |
| |
| bundle.removeDependencies(); |
| } |
| |
| private StateDelta resolve(boolean incremental, BundleDescription[] reResolve, BundleDescription[] triggers) { |
| fullyLoad(); |
| synchronized (this.monitor) { |
| if (resolver == null) |
| throw new IllegalStateException("no resolver set"); //$NON-NLS-1$ |
| if (resolving == true) |
| throw new IllegalStateException("An attempt to start a nested resolve process has been detected."); //$NON-NLS-1$ |
| try { |
| resolving = true; |
| if (!incremental) { |
| resolved = false; |
| reResolve = getBundles(); |
| // need to get any removal pendings before flushing |
| if (removalPendings.size() > 0) { |
| BundleDescription[] removed = internalGetRemovalPending(); |
| reResolve = mergeBundles(reResolve, removed); |
| } |
| flush(reResolve); |
| } else { |
| if (resolved && reResolve == null) |
| return new StateDeltaImpl(this); |
| if (developmentMode) { |
| // in dev mode we need to aggressively flush removal pendings |
| if (removalPendings.size() > 0) { |
| BundleDescription[] removed = internalGetRemovalPending(); |
| reResolve = mergeBundles(reResolve, removed); |
| } |
| } |
| if (reResolve == null) |
| reResolve = internalGetRemovalPending(); |
| if (triggers == null) { |
| Set<BundleDescription> triggerSet = new HashSet<>(); |
| Collection<BundleDescription> closure = getDependencyClosure(Arrays.asList(reResolve)); |
| for (BundleDescription toRefresh : closure) { |
| Bundle bundle = toRefresh.getBundle(); |
| if (bundle != null && (bundle.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED | Bundle.RESOLVED)) == 0) |
| triggerSet.add(toRefresh); |
| } |
| triggers = triggerSet.toArray(new BundleDescription[triggerSet.size()]); |
| } |
| } |
| // use the Headers class to handle ignoring case while matching keys (bug 180817) |
| @SuppressWarnings("unchecked") |
| CaseInsensitiveDictionaryMap<Object, Object>[] tmpPlatformProperties = new CaseInsensitiveDictionaryMap[platformProperties.length]; |
| for (int i = 0; i < platformProperties.length; i++) { |
| tmpPlatformProperties[i] = new CaseInsensitiveDictionaryMap<>(platformProperties[i].size()); |
| for (Enumeration<Object> keys = platformProperties[i].keys(); keys.hasMoreElements();) { |
| Object key = keys.nextElement(); |
| tmpPlatformProperties[i].put(key, platformProperties[i].get(key)); |
| } |
| } |
| |
| ResolverHookFactory currentFactory = hookFactory; |
| if (currentFactory != null) { |
| @SuppressWarnings("unchecked") |
| Collection<BundleRevision> triggerRevisions = Collections.unmodifiableCollection(triggers == null ? Collections.EMPTY_LIST : Arrays.asList((BundleRevision[]) triggers)); |
| begin(triggerRevisions); |
| } |
| ResolverHookException error = null; |
| try { |
| resolver.resolve(reResolve, tmpPlatformProperties); |
| } catch (ResolverHookException e) { |
| error = e; |
| resolverErrors.clear(); |
| } |
| resolved = removalPendings.size() == 0; |
| |
| StateDeltaImpl savedChanges = changes == null ? new StateDeltaImpl(this) : changes; |
| savedChanges.setResolverHookException(error); |
| changes = new StateDeltaImpl(this); |
| |
| if (savedChanges.getChanges().length > 0) |
| updateTimeStamp(); |
| return savedChanges; |
| } finally { |
| resolving = false; |
| } |
| } |
| } |
| |
| private BundleDescription[] mergeBundles(BundleDescription[] reResolve, BundleDescription[] removed) { |
| if (reResolve == null) |
| return removed; // just return all the removed bundles |
| if (reResolve.length == 0) |
| return reResolve; // if reResolve length==0 then we want to prevent pending removal |
| // merge in all removal pending bundles that are not already in the list |
| List<BundleDescription> result = new ArrayList<>(reResolve.length + removed.length); |
| for (int i = 0; i < reResolve.length; i++) |
| result.add(reResolve[i]); |
| for (int i = 0; i < removed.length; i++) { |
| boolean found = false; |
| for (int j = 0; j < reResolve.length; j++) { |
| if (removed[i] == reResolve[j]) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) |
| result.add(removed[i]); |
| } |
| return result.toArray(new BundleDescription[result.size()]); |
| } |
| |
| private void flush(BundleDescription[] bundles) { |
| resolver.flush(); |
| resolved = false; |
| resolverErrors.clear(); |
| if (resolvedBundles.isEmpty()) |
| return; |
| for (int i = 0; i < bundles.length; i++) { |
| resolveBundle(bundles[i], false, null, null, null, null, null); |
| } |
| resolvedBundles.clear(); |
| } |
| |
| public StateDelta resolve() { |
| return resolve(true, null, null); |
| } |
| |
| public StateDelta resolve(boolean incremental) { |
| return resolve(incremental, null, null); |
| } |
| |
| public StateDelta resolve(BundleDescription[] reResolve) { |
| return resolve(true, reResolve, null); |
| } |
| |
| public StateDelta resolve(BundleDescription[] resolve, boolean discard) { |
| BundleDescription[] reResolve = discard ? resolve : new BundleDescription[0]; |
| BundleDescription[] triggers = discard ? null : resolve; |
| return resolve(true, reResolve, triggers); |
| } |
| |
| @SuppressWarnings("deprecation") |
| public void setOverrides(Object value) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public void setResolverHookFactory(ResolverHookFactory hookFactory) { |
| synchronized (this.monitor) { |
| if (this.hookFactory != null) |
| throw new IllegalStateException("Resolver hook factory is already set."); //$NON-NLS-1$ |
| this.hookFactory = hookFactory; |
| } |
| } |
| |
| private ResolverHook begin(Collection<BundleRevision> triggers) { |
| ResolverHookFactory current; |
| synchronized (this.monitor) { |
| current = this.hookFactory; |
| } |
| ResolverHook newHook = current.begin(triggers); |
| synchronized (this.monitor) { |
| this.hook = newHook; |
| } |
| return newHook; |
| } |
| |
| ResolverHookFactory getResolverHookFactory() { |
| synchronized (this.monitor) { |
| return this.hookFactory; |
| } |
| } |
| |
| public ResolverHook getResolverHook() { |
| synchronized (this.monitor) { |
| return this.hook; |
| } |
| } |
| |
| public BundleDescription[] getResolvedBundles() { |
| synchronized (this.monitor) { |
| return (BundleDescription[]) resolvedBundles.elements(new BundleDescription[resolvedBundles.size()]); |
| } |
| } |
| |
| public boolean isEmpty() { |
| synchronized (this.monitor) { |
| return bundleDescriptions.isEmpty(); |
| } |
| } |
| |
| void setResolved(boolean resolved) { |
| synchronized (this.monitor) { |
| this.resolved = resolved; |
| } |
| } |
| |
| boolean basicAddBundle(BundleDescription description) { |
| synchronized (this.monitor) { |
| StateImpl origState = (StateImpl) description.getContainingState(); |
| if (origState != null && origState != this) { |
| if (origState.removalPendings.contains(description)) |
| throw new IllegalStateException(NLS.bind(StateMsg.BUNDLE_PENDING_REMOVE_STATE, description.toString())); |
| if (origState.getBundle(description.getBundleId()) == description) |
| throw new IllegalStateException(NLS.bind(StateMsg.BUNDLE_IN_OTHER_STATE, description.toString())); |
| } |
| ((BundleDescriptionImpl) description).setContainingState(this); |
| ((BundleDescriptionImpl) description).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, false); |
| if (bundleDescriptions.add((BundleDescriptionImpl) description)) { |
| if (description.getBundleId() > getHighestBundleId()) |
| highestBundleId = description.getBundleId(); |
| addBundleNameCacheEntry(description); |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| void addResolvedBundle(BundleDescriptionImpl resolvedBundle) { |
| synchronized (this.monitor) { |
| resolvedBundles.add(resolvedBundle); |
| } |
| } |
| |
| public ExportPackageDescription[] getExportedPackages() { |
| fullyLoad(); |
| synchronized (this.monitor) { |
| List<ExportPackageDescription> allExportedPackages = new ArrayList<>(); |
| for (Iterator<KeyedElement> iter = resolvedBundles.iterator(); iter.hasNext();) { |
| BundleDescription bundle = (BundleDescription) iter.next(); |
| ExportPackageDescription[] bundlePackages = bundle.getSelectedExports(); |
| if (bundlePackages == null) |
| continue; |
| for (int i = 0; i < bundlePackages.length; i++) |
| allExportedPackages.add(bundlePackages[i]); |
| } |
| for (Iterator<BundleDescription> iter = removalPendings.iterator(); iter.hasNext();) { |
| BundleDescription bundle = iter.next(); |
| ExportPackageDescription[] bundlePackages = bundle.getSelectedExports(); |
| if (bundlePackages == null) |
| continue; |
| for (int i = 0; i < bundlePackages.length; i++) |
| allExportedPackages.add(bundlePackages[i]); |
| } |
| return allExportedPackages.toArray(new ExportPackageDescription[allExportedPackages.size()]); |
| } |
| } |
| |
| BundleDescription[] getFragments(final BundleDescription host) { |
| final List<BundleDescription> fragments = new ArrayList<>(); |
| synchronized (this.monitor) { |
| for (Iterator<KeyedElement> iter = bundleDescriptions.iterator(); iter.hasNext();) { |
| BundleDescription bundle = (BundleDescription) iter.next(); |
| HostSpecification hostSpec = bundle.getHost(); |
| |
| if (hostSpec != null) { |
| BundleDescription[] hosts = hostSpec.getHosts(); |
| if (hosts != null) |
| for (int i = 0; i < hosts.length; i++) |
| if (hosts[i] == host) { |
| fragments.add(bundle); |
| break; |
| } |
| } |
| } |
| } |
| return fragments.toArray(new BundleDescription[fragments.size()]); |
| } |
| |
| public void setTimeStamp(long newTimeStamp) { |
| synchronized (this.monitor) { |
| timeStamp = newTimeStamp; |
| } |
| } |
| |
| private void updateTimeStamp() { |
| synchronized (this.monitor) { |
| if (getTimeStamp() == Long.MAX_VALUE) |
| setTimeStamp(0); |
| setTimeStamp(getTimeStamp() + 1); |
| } |
| } |
| |
| public StateObjectFactory getFactory() { |
| return factory; |
| } |
| |
| void setFactory(StateObjectFactory factory) { |
| this.factory = factory; |
| } |
| |
| public BundleDescription getBundleByLocation(String location) { |
| synchronized (this.monitor) { |
| for (Iterator<KeyedElement> i = bundleDescriptions.iterator(); i.hasNext();) { |
| BundleDescription current = (BundleDescription) i.next(); |
| if (location.equals(current.getLocation())) |
| return current; |
| } |
| return null; |
| } |
| } |
| |
| public Resolver getResolver() { |
| synchronized (this.monitor) { |
| return resolver; |
| } |
| } |
| |
| public void setResolver(Resolver newResolver) { |
| if (resolver == newResolver) |
| return; |
| if (resolver != null) { |
| Resolver oldResolver = resolver; |
| resolver = null; |
| oldResolver.setState(null); |
| } |
| synchronized (this.monitor) { |
| resolver = newResolver; |
| } |
| if (resolver == null) |
| return; |
| resolver.setState(this); |
| } |
| |
| public boolean setPlatformProperties(Dictionary<?, ?> platformProperties) { |
| return setPlatformProperties(new Dictionary[] {platformProperties}); |
| } |
| |
| public boolean setPlatformProperties(Dictionary<?, ?>[] platformProperties) { |
| return setPlatformProperties(platformProperties, true); |
| } |
| |
| synchronized boolean setPlatformProperties(Dictionary<?, ?>[] platformProperties, boolean resetSystemExports) { |
| if (platformProperties.length == 0) |
| throw new IllegalArgumentException(); |
| // copy the properties for our use internally; |
| // only copy String and String[] values |
| @SuppressWarnings("unchecked") |
| Dictionary<Object, Object>[] newPlatformProperties = new Dictionary[platformProperties.length]; |
| for (int i = 0; i < platformProperties.length; i++) { |
| newPlatformProperties[i] = new Hashtable<>(platformProperties[i].size()); |
| synchronized (platformProperties[i]) { |
| for (Enumeration<?> keys = platformProperties[i].keys(); keys.hasMoreElements();) { |
| Object key = keys.nextElement(); |
| Object value = platformProperties[i].get(key); |
| newPlatformProperties[i].put(key, value); |
| } |
| } |
| // make sure the bundle native code osgi properties have decent defaults |
| if (newPlatformProperties[i].get(Constants.FRAMEWORK_OS_NAME) == null && newPlatformProperties[i].get(OSGI_OS) != null) |
| newPlatformProperties[i].put(Constants.FRAMEWORK_OS_NAME, newPlatformProperties[i].get(OSGI_OS)); |
| if (newPlatformProperties[i].get(Constants.FRAMEWORK_PROCESSOR) == null && newPlatformProperties[i].get(OSGI_ARCH) != null) |
| newPlatformProperties[i].put(Constants.FRAMEWORK_PROCESSOR, newPlatformProperties[i].get(OSGI_ARCH)); |
| if (newPlatformProperties[i].get(Constants.FRAMEWORK_LANGUAGE) == null && newPlatformProperties[i].get(OSGI_NL) instanceof String) { |
| String osgiNL = (String) newPlatformProperties[i].get(OSGI_NL); |
| int idx = osgiNL.indexOf('_'); |
| if (idx >= 0) |
| osgiNL = osgiNL.substring(0, idx); |
| newPlatformProperties[i].put(Constants.FRAMEWORK_LANGUAGE, osgiNL); |
| } |
| |
| } |
| boolean result = false; |
| boolean performResetSystemExports = false; |
| boolean performResetSystemCapabilities = false; |
| if (this.platformProperties.length != newPlatformProperties.length) { |
| result = true; |
| performResetSystemExports = true; |
| performResetSystemCapabilities = true; |
| } else { |
| // we need to see if any of the existing filter prop keys have changed |
| String[] keys = getPlatformPropertyKeys(); |
| for (int i = 0; i < newPlatformProperties.length && !result; i++) { |
| result |= changedProps(this.platformProperties[i], newPlatformProperties[i], keys); |
| if (resetSystemExports) { |
| performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES)); |
| performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)); |
| performResetSystemExports |= checkProp(this.platformProperties[i].get(Constants.SYSTEM_BUNDLE_SYMBOLICNAME), newPlatformProperties[i].get(Constants.SYSTEM_BUNDLE_SYMBOLICNAME)); |
| performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES)); |
| performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA), newPlatformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA)); |
| performResetSystemCapabilities |= checkProp(this.platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT), newPlatformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT)); |
| } |
| } |
| } |
| // always do a complete replacement of the properties in case new bundles are added that uses new filter props |
| this.platformProperties = newPlatformProperties; |
| if (performResetSystemExports) |
| resetSystemExports(); |
| if (performResetSystemCapabilities) |
| resetSystemCapabilities(); |
| developmentMode = this.platformProperties.length == 0 ? false : DEVELOPMENT_MODE.equals(this.platformProperties[0].get(OSGI_RESOLVER_MODE)); |
| return result; |
| } |
| |
| private void resetAllSystemCapabilities() { |
| resetSystemExports(); |
| resetSystemCapabilities(); |
| } |
| |
| private void resetSystemExports() { |
| BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME); |
| for (int idx = 0; idx < systemBundles.length; idx++) { |
| BundleDescriptionImpl systemBundle = (BundleDescriptionImpl) systemBundles[idx]; |
| ExportPackageDescription[] exports = systemBundle.getExportPackages(); |
| List<ExportPackageDescription> newExports = new ArrayList<>(exports.length); |
| for (int i = 0; i < exports.length; i++) |
| if (((Integer) exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() < 0) |
| newExports.add(exports[i]); |
| addSystemExports(newExports); |
| systemBundle.setExportPackages(newExports.toArray(new ExportPackageDescription[newExports.size()])); |
| } |
| } |
| |
| private void addSystemExports(List<ExportPackageDescription> exports) { |
| for (int i = 0; i < platformProperties.length; i++) |
| try { |
| addSystemExports(exports, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES)), i); |
| addSystemExports(exports, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA)), i); |
| } catch (BundleException e) { |
| // TODO consider throwing this... |
| } |
| } |
| |
| private void addSystemExports(List<ExportPackageDescription> exports, ManifestElement[] elements, int index) { |
| if (elements == null) |
| return; |
| ExportPackageDescription[] systemExports = StateBuilder.createExportPackages(elements, null, null, false); |
| Integer profInx = new Integer(index); |
| for (int j = 0; j < systemExports.length; j++) { |
| ((ExportPackageDescriptionImpl) systemExports[j]).setDirective(ExportPackageDescriptionImpl.EQUINOX_EE, profInx); |
| exports.add(systemExports[j]); |
| } |
| } |
| |
| private void resetSystemCapabilities() { |
| BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME); |
| for (BundleDescription systemBundle : systemBundles) { |
| GenericDescription[] capabilities = systemBundle.getGenericCapabilities(); |
| List<GenericDescription> newCapabilities = new ArrayList<>(capabilities.length); |
| for (GenericDescription capability : capabilities) { |
| Object equinoxEEIndex = capability.getDeclaredAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE); |
| if (equinoxEEIndex == null) |
| newCapabilities.add(capability); // keep the built in ones. |
| } |
| // now add the externally defined ones |
| addSystemCapabilities(newCapabilities); |
| ((BundleDescriptionImpl) systemBundle).setGenericCapabilities(newCapabilities.toArray(new GenericDescription[newCapabilities.size()])); |
| } |
| } |
| |
| private void addSystemCapabilities(List<GenericDescription> capabilities) { |
| for (int i = 0; i < platformProperties.length; i++) |
| try { |
| addSystemCapabilities(capabilities, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES)), i); |
| addSystemCapabilities(capabilities, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, (String) platformProperties[i].get(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA)), i); |
| checkOSGiEE(capabilities, (String) platformProperties[i].get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT), i); |
| } catch (BundleException e) { |
| // TODO consider throwing this... |
| } |
| } |
| |
| private void checkOSGiEE(List<GenericDescription> capabilities, String profileEE, Integer profileIndex) { |
| if (profileEE == null || profileEE.length() == 0) |
| return; |
| for (GenericDescription capability : capabilities) { |
| if (OSGI_EE_NAMESPACE.equals(capability.getType()) && profileIndex.equals(capability.getAttributes().get(ExportPackageDescriptionImpl.EQUINOX_EE))) |
| return; // profile already specifies osgi.ee capabilities |
| } |
| Map<String, List<String>> eeVersions = new HashMap<>(); |
| String[] ees = ManifestElement.getArrayFromList(profileEE); |
| for (String ee : ees) { |
| String[] eeNameVersion = StateBuilder.getOSGiEENameVersion(ee); |
| |
| List<String> versions = eeVersions.get(eeNameVersion[0]); |
| if (versions == null) { |
| versions = new ArrayList<>(); |
| eeVersions.put(eeNameVersion[0], versions); |
| } |
| if (eeNameVersion[1] != null && !versions.contains(eeNameVersion[1])) |
| versions.add(eeNameVersion[1]); |
| } |
| for (Map.Entry<String, List<String>> eeVersion : eeVersions.entrySet()) { |
| GenericDescriptionImpl capability = new GenericDescriptionImpl(); |
| capability.setType(OSGI_EE_NAMESPACE); |
| Dictionary<String, Object> attributes = new Hashtable<>(); |
| attributes.put(capability.getType(), eeVersion.getKey()); |
| if (eeVersion.getValue().size() > 0) { |
| List<Version> versions = new ArrayList<>(eeVersion.getValue().size()); |
| for (String version : eeVersion.getValue()) { |
| versions.add(new Version(version)); |
| } |
| attributes.put("version", versions); //$NON-NLS-1$ |
| } |
| attributes.put(ExportPackageDescriptionImpl.EQUINOX_EE, profileIndex); |
| capability.setAttributes(attributes); |
| capabilities.add(capability); |
| } |
| } |
| |
| private void addSystemCapabilities(List<GenericDescription> capabilities, ManifestElement[] elements, Integer profileIndex) { |
| try { |
| StateBuilder.createOSGiCapabilities(elements, capabilities, profileIndex); |
| } catch (BundleException e) { |
| throw new RuntimeException("Unexpected exception adding system capabilities.", e); //$NON-NLS-1$ |
| } |
| } |
| |
| public Dictionary<Object, Object>[] getPlatformProperties() { |
| return platformProperties; |
| } |
| |
| private boolean checkProp(Object origObj, Object newObj) { |
| if ((origObj == null && newObj != null) || (origObj != null && newObj == null)) |
| return true; |
| if (origObj == null) |
| return false; |
| if (origObj.getClass() != newObj.getClass()) |
| return true; |
| if (origObj instanceof String[]) { |
| String[] origProps = (String[]) origObj; |
| String[] newProps = (String[]) newObj; |
| if (origProps.length != newProps.length) |
| return true; |
| for (int i = 0; i < origProps.length; i++) { |
| if (!origProps[i].equals(newProps[i])) |
| return true; |
| } |
| return false; |
| } |
| return !origObj.equals(newObj); |
| } |
| |
| private boolean changedProps(Dictionary<Object, Object> origProps, Dictionary<Object, Object> newProps, String[] keys) { |
| for (int i = 0; i < keys.length; i++) { |
| Object origProp = origProps.get(keys[i]); |
| Object newProp = newProps.get(keys[i]); |
| if (checkProp(origProp, newProp)) |
| return true; |
| } |
| return false; |
| } |
| |
| public String getSystemBundle() { |
| String symbolicName = null; |
| if (platformProperties != null && platformProperties.length > 0) |
| symbolicName = (String) platformProperties[0].get(STATE_SYSTEM_BUNDLE); |
| return symbolicName != null ? symbolicName : EquinoxContainer.NAME; |
| } |
| |
| public BundleDescription[] getRemovalPending() { |
| synchronized (this.monitor) { |
| return removalPendings.toArray(new BundleDescription[removalPendings.size()]); |
| } |
| } |
| |
| private void addRemovalPending(BundleDescription removed) { |
| synchronized (this.monitor) { |
| if (!removalPendings.contains(removed)) |
| removalPendings.addFirst(removed); |
| } |
| } |
| |
| public Collection<BundleDescription> getDependencyClosure(Collection<BundleDescription> bundles) { |
| BundleDescription[] removals = getRemovalPending(); |
| Set<BundleDescription> result = new HashSet<>(); |
| for (BundleDescription bundle : bundles) { |
| addDependents(bundle, result, removals); |
| } |
| return result; |
| } |
| |
| private static void addDependents(BundleDescription bundle, Set<BundleDescription> result, BundleDescription[] removals) { |
| if (result.contains(bundle)) |
| return; // avoid cycles |
| result.add(bundle); |
| BundleDescription[] dependents = bundle.getDependents(); |
| for (BundleDescription dependent : dependents) |
| addDependents(dependent, result, removals); |
| // check if this is a removal pending |
| for (BundleDescription removed : removals) { |
| if (removed.getBundleId() == bundle.getBundleId()) |
| addDependents(removed, result, removals); |
| } |
| } |
| |
| /** |
| * Returns the latest versions BundleDescriptions which have old removal pending versions. |
| * @return the BundleDescriptions that have removal pending versions. |
| */ |
| private BundleDescription[] internalGetRemovalPending() { |
| synchronized (this.monitor) { |
| Iterator<BundleDescription> removed = removalPendings.iterator(); |
| BundleDescription[] result = new BundleDescription[removalPendings.size()]; |
| int i = 0; |
| while (removed.hasNext()) |
| // we return the latest version of the description if it is still contained in the state (bug 287636) |
| result[i++] = getBundle(removed.next().getBundleId()); |
| return result; |
| } |
| } |
| |
| public ExportPackageDescription linkDynamicImport(BundleDescription importingBundle, String requestedPackage) { |
| if (resolver == null) |
| throw new IllegalStateException("no resolver set"); //$NON-NLS-1$ |
| BundleDescriptionImpl importer = (BundleDescriptionImpl) importingBundle; |
| if (importer.getDynamicStamp(requestedPackage) == getTimeStamp()) |
| return null; |
| fullyLoad(); |
| synchronized (this.monitor) { |
| ResolverHook currentHook = null; |
| try { |
| resolving = true; |
| ResolverHookFactory currentFactory = hookFactory; |
| if (currentFactory != null) { |
| Collection<BundleRevision> triggers = new ArrayList<>(1); |
| triggers.add(importingBundle); |
| triggers = Collections.unmodifiableCollection(triggers); |
| currentHook = begin(triggers); |
| } |
| // ask the resolver to resolve our dynamic import |
| ExportPackageDescriptionImpl result = (ExportPackageDescriptionImpl) resolver.resolveDynamicImport(importingBundle, requestedPackage); |
| if (result == null) |
| importer.setDynamicStamp(requestedPackage, new Long(getTimeStamp())); |
| else { |
| importer.setDynamicStamp(requestedPackage, null); // remove any cached timestamp |
| // need to add the result to the list of resolved imports |
| importer.addDynamicResolvedImport(result); |
| } |
| setDynamicCacheChanged(true); |
| return result; |
| } finally { |
| resolving = false; |
| if (currentHook != null) |
| currentHook.end(); |
| } |
| } |
| |
| } |
| |
| public void addDynamicImportPackages(BundleDescription importingBundle, ImportPackageSpecification[] dynamicImports) { |
| synchronized (this.monitor) { |
| ((BundleDescriptionImpl) importingBundle).addDynamicImportPackages(dynamicImports); |
| setDynamicCacheChanged(true); |
| } |
| } |
| |
| void setReader(StateReader reader) { |
| synchronized (this.monitor) { |
| this.reader = reader; |
| } |
| } |
| |
| StateReader getReader() { |
| synchronized (this.monitor) { |
| return reader; |
| } |
| } |
| |
| // not synchronized on this to prevent deadlock |
| public final void fullyLoad() { |
| synchronized (this.monitor) { |
| if (reader == null) |
| return; |
| if (fullyLoaded == true) |
| return; |
| if (reader.isLazyLoaded()) |
| reader.fullyLoad(); |
| fullyLoaded = true; |
| } |
| } |
| |
| // not synchronized on this to prevent deadlock |
| public final boolean unloadLazyData(long checkStamp) { |
| // make sure no other thread is trying to unload or load |
| synchronized (this.monitor) { |
| if (checkStamp != getTimeStamp() || dynamicCacheChanged()) |
| return false; |
| if (reader.getAccessedFlag()) { |
| reader.setAccessedFlag(false); // reset accessed flag |
| return true; |
| } |
| fullyLoaded = false; |
| BundleDescription[] bundles = getBundles(); |
| for (int i = 0; i < bundles.length; i++) |
| ((BundleDescriptionImpl) bundles[i]).unload(); |
| reader.flushLazyObjectCache(); |
| resolver.flush(); |
| return true; |
| } |
| } |
| |
| public ExportPackageDescription[] getSystemPackages() { |
| synchronized (this.monitor) { |
| List<ExportPackageDescription> result = new ArrayList<>(); |
| BundleDescription[] systemBundles = getBundles(Constants.SYSTEM_BUNDLE_SYMBOLICNAME); |
| if (systemBundles.length > 0) { |
| BundleDescriptionImpl systemBundle = (BundleDescriptionImpl) systemBundles[0]; |
| ExportPackageDescription[] exports = systemBundle.getExportPackages(); |
| for (int i = 0; i < exports.length; i++) |
| if (((Integer) exports[i].getDirective(ExportPackageDescriptionImpl.EQUINOX_EE)).intValue() >= 0) |
| result.add(exports[i]); |
| } |
| return result.toArray(new ExportPackageDescription[result.size()]); |
| } |
| } |
| |
| boolean inStrictMode() { |
| synchronized (this.monitor) { |
| return STRICT_MODE.equals(getPlatformProperties()[0].get(OSGI_RESOLVER_MODE)); |
| } |
| } |
| |
| public ResolverError[] getResolverErrors(BundleDescription bundle) { |
| synchronized (this.monitor) { |
| if (bundle.isResolved()) |
| return new ResolverError[0]; |
| List<ResolverError> result = resolverErrors.get(bundle); |
| return result == null ? new ResolverError[0] : result.toArray(new ResolverError[result.size()]); |
| } |
| } |
| |
| public void addResolverError(BundleDescription bundle, int type, String data, VersionConstraint unsatisfied) { |
| synchronized (this.monitor) { |
| if (!resolving) |
| throw new IllegalStateException(); // TODO need error message here! |
| List<ResolverError> errors = resolverErrors.get(bundle); |
| if (errors == null) { |
| errors = new ArrayList<>(1); |
| resolverErrors.put(bundle, errors); |
| } |
| errors.add(new ResolverErrorImpl((BundleDescriptionImpl) bundle, type, data, unsatisfied)); |
| } |
| } |
| |
| public void removeResolverErrors(BundleDescription bundle) { |
| synchronized (this.monitor) { |
| if (!resolving) |
| throw new IllegalStateException(); // TODO need error message here! |
| resolverErrors.remove(bundle); |
| } |
| } |
| |
| public boolean dynamicCacheChanged() { |
| synchronized (this.monitor) { |
| return dynamicCacheChanged; |
| } |
| } |
| |
| void setDynamicCacheChanged(boolean dynamicCacheChanged) { |
| synchronized (this.monitor) { |
| this.dynamicCacheChanged = dynamicCacheChanged; |
| } |
| } |
| |
| public StateHelper getStateHelper() { |
| return StateHelperImpl.getInstance(); |
| } |
| |
| void addPlatformPropertyKeys(String[] keys) { |
| synchronized (platformPropertyKeys) { |
| for (int i = 0; i < keys.length; i++) |
| if (!platformPropertyKeys.contains(keys[i])) |
| platformPropertyKeys.add(keys[i]); |
| } |
| } |
| |
| String[] getPlatformPropertyKeys() { |
| synchronized (platformPropertyKeys) { |
| return platformPropertyKeys.toArray(new String[platformPropertyKeys.size()]); |
| } |
| } |
| |
| public long getHighestBundleId() { |
| synchronized (this.monitor) { |
| return highestBundleId; |
| } |
| } |
| |
| public void setNativePathsInvalid(NativeCodeDescription nativeCodeDescription, boolean hasInvalidNativePaths) { |
| ((NativeCodeDescriptionImpl) nativeCodeDescription).setInvalidNativePaths(hasInvalidNativePaths); |
| } |
| |
| public BundleDescription[] getDisabledBundles() { |
| synchronized (this.monitor) { |
| return disabledBundles.keySet().toArray(new BundleDescription[0]); |
| } |
| } |
| |
| public void addDisabledInfo(DisabledInfo disabledInfo) { |
| synchronized (this.monitor) { |
| if (getBundle(disabledInfo.getBundle().getBundleId()) != disabledInfo.getBundle()) |
| throw new IllegalArgumentException(NLS.bind(StateMsg.BUNDLE_NOT_IN_STATE, disabledInfo.getBundle())); |
| List<DisabledInfo> currentInfos = disabledBundles.get(disabledInfo.getBundle()); |
| if (currentInfos == null) { |
| currentInfos = new ArrayList<>(1); |
| currentInfos.add(disabledInfo); |
| disabledBundles.put(disabledInfo.getBundle(), currentInfos); |
| } else { |
| Iterator<DisabledInfo> it = currentInfos.iterator(); |
| while (it.hasNext()) { |
| DisabledInfo currentInfo = it.next(); |
| if (disabledInfo.getPolicyName().equals(currentInfo.getPolicyName())) { |
| currentInfos.remove(currentInfo); |
| break; |
| } |
| } |
| currentInfos.add(disabledInfo); |
| } |
| updateTimeStamp(); |
| } |
| } |
| |
| public void removeDisabledInfo(DisabledInfo disabledInfo) { |
| synchronized (this.monitor) { |
| List<DisabledInfo> currentInfos = disabledBundles.get(disabledInfo.getBundle()); |
| if ((currentInfos != null) && currentInfos.contains(disabledInfo)) { |
| currentInfos.remove(disabledInfo); |
| if (currentInfos.isEmpty()) { |
| disabledBundles.remove(disabledInfo.getBundle()); |
| } |
| } |
| updateTimeStamp(); |
| } |
| } |
| |
| public DisabledInfo getDisabledInfo(BundleDescription bundle, String policyName) { |
| synchronized (this.monitor) { |
| List<DisabledInfo> currentInfos = disabledBundles.get(bundle); |
| if (currentInfos == null) |
| return null; |
| Iterator<DisabledInfo> it = currentInfos.iterator(); |
| while (it.hasNext()) { |
| DisabledInfo currentInfo = it.next(); |
| if (currentInfo.getPolicyName().equals(policyName)) { |
| return currentInfo; |
| } |
| } |
| return null; |
| } |
| } |
| |
| public DisabledInfo[] getDisabledInfos(BundleDescription bundle) { |
| synchronized (this.monitor) { |
| List<DisabledInfo> currentInfos = disabledBundles.get(bundle); |
| return currentInfos == null ? EMPTY_DISABLEDINFOS : currentInfos.toArray(new DisabledInfo[currentInfos.size()]); |
| } |
| } |
| |
| /* |
| * Used by StateWriter to get all the DisabledInfo objects to persist |
| */ |
| DisabledInfo[] getDisabledInfos() { |
| List<DisabledInfo> results = new ArrayList<>(); |
| synchronized (this.monitor) { |
| for (Iterator<List<DisabledInfo>> allDisabledInfos = disabledBundles.values().iterator(); allDisabledInfos.hasNext();) |
| results.addAll(allDisabledInfos.next()); |
| } |
| return results.toArray(new DisabledInfo[results.size()]); |
| } |
| |
| private void addBundleNameCacheEntry(BundleDescription description) { |
| Set<BundleDescription> descriptions = bundleNameCache.get(description.getSymbolicName()); |
| if (descriptions == null) { |
| descriptions = new LinkedHashSet<>(); |
| bundleNameCache.put(description.getSymbolicName(), descriptions); |
| } |
| descriptions.add(description); |
| } |
| |
| private void removeBundleNameCacheEntry(BundleDescription description) { |
| Set<BundleDescription> descriptions = bundleNameCache.get(description.getSymbolicName()); |
| if (descriptions != null) { |
| descriptions.remove(description); |
| if (descriptions.isEmpty()) { |
| bundleNameCache.remove(description.getSymbolicName()); |
| } |
| } |
| } |
| } |