| /******************************************************************************* |
| * Copyright (c) 2003, 2020 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 and 255520) |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.resolver; |
| |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.osgi.framework.util.KeyedElement; |
| import org.eclipse.osgi.service.resolver.BaseDescription; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.osgi.service.resolver.BundleSpecification; |
| import org.eclipse.osgi.service.resolver.ExportPackageDescription; |
| import org.eclipse.osgi.service.resolver.GenericDescription; |
| import org.eclipse.osgi.service.resolver.GenericSpecification; |
| import org.eclipse.osgi.service.resolver.HostSpecification; |
| import org.eclipse.osgi.service.resolver.ImportPackageSpecification; |
| import org.eclipse.osgi.service.resolver.NativeCodeDescription; |
| import org.eclipse.osgi.service.resolver.NativeCodeSpecification; |
| import org.eclipse.osgi.service.resolver.State; |
| import org.eclipse.osgi.service.resolver.StateWire; |
| import org.eclipse.osgi.service.resolver.VersionConstraint; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleReference; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.wiring.BundleCapability; |
| import org.osgi.framework.wiring.BundleRequirement; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.framework.wiring.BundleWire; |
| import org.osgi.framework.wiring.BundleWiring; |
| import org.osgi.resource.Capability; |
| import org.osgi.resource.Requirement; |
| import org.osgi.resource.Wire; |
| |
| public final class BundleDescriptionImpl extends BaseDescriptionImpl implements BundleDescription, KeyedElement { |
| static final String[] EMPTY_STRING = new String[0]; |
| static final ImportPackageSpecification[] EMPTY_IMPORTS = new ImportPackageSpecification[0]; |
| static final BundleSpecification[] EMPTY_BUNDLESPECS = new BundleSpecification[0]; |
| static final ExportPackageDescription[] EMPTY_EXPORTS = new ExportPackageDescription[0]; |
| static final BundleDescription[] EMPTY_BUNDLEDESCS = new BundleDescription[0]; |
| static final GenericSpecification[] EMPTY_GENERICSPECS = new GenericSpecification[0]; |
| static final GenericDescription[] EMPTY_GENERICDESCS = new GenericDescription[0]; |
| static final RuntimePermission GET_CLASSLOADER_PERM = new RuntimePermission("getClassLoader"); //$NON-NLS-1$ |
| |
| static final int RESOLVED = 0x01; |
| static final int SINGLETON = 0x02; |
| static final int REMOVAL_PENDING = 0x04; |
| static final int FULLY_LOADED = 0x08; |
| static final int LAZY_LOADED = 0x10; |
| static final int HAS_DYNAMICIMPORT = 0x20; |
| static final int ATTACH_FRAGMENTS = 0x40; |
| static final int DYNAMIC_FRAGMENTS = 0x80; |
| |
| // set to fully loaded and allow dynamic fragments by default |
| private volatile int stateBits = FULLY_LOADED | ATTACH_FRAGMENTS | DYNAMIC_FRAGMENTS; |
| |
| private volatile long bundleId = -1; |
| volatile HostSpecification host; //null if the bundle is not a fragment. volatile to allow unsynchronized checks for null |
| private volatile StateImpl containingState; |
| |
| private volatile int lazyDataOffset = -1; |
| private volatile int lazyDataSize = -1; |
| |
| private List<BundleDescription> dependencies; |
| private List<BundleDescription> dependents; |
| private String[] mandatory; |
| private Map<String, Object> attributes; |
| private Map<String, String> arbitraryDirectives; |
| |
| private volatile LazyData lazyData; |
| private volatile int equinox_ee = -1; |
| |
| private DescriptionWiring bundleWiring; |
| |
| public BundleDescriptionImpl() { |
| // |
| } |
| |
| public long getBundleId() { |
| return bundleId; |
| } |
| |
| public String getSymbolicName() { |
| return getName(); |
| } |
| |
| public BundleDescription getSupplier() { |
| return this; |
| } |
| |
| public String getLocation() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| return currentData.location; |
| } |
| } |
| |
| public String getPlatformFilter() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| return currentData.platformFilter; |
| } |
| } |
| |
| public String[] getExecutionEnvironments() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.executionEnvironments == null) |
| return EMPTY_STRING; |
| return currentData.executionEnvironments; |
| } |
| } |
| |
| public ImportPackageSpecification[] getImportPackages() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.importPackages == null) |
| return EMPTY_IMPORTS; |
| return currentData.importPackages; |
| } |
| } |
| |
| public ImportPackageSpecification[] getAddedDynamicImportPackages() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.addedDynamicImports == null) |
| return EMPTY_IMPORTS; |
| return currentData.addedDynamicImports.toArray(new ImportPackageSpecification[currentData.addedDynamicImports.size()]); |
| } |
| } |
| |
| public BundleSpecification[] getRequiredBundles() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.requiredBundles == null) |
| return EMPTY_BUNDLESPECS; |
| return currentData.requiredBundles; |
| } |
| } |
| |
| public GenericSpecification[] getGenericRequires() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.genericRequires == null) |
| return EMPTY_GENERICSPECS; |
| return currentData.genericRequires; |
| } |
| } |
| |
| public GenericDescription[] getGenericCapabilities() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.genericCapabilities == null) |
| return EMPTY_GENERICDESCS; |
| return currentData.genericCapabilities; |
| } |
| } |
| |
| public NativeCodeSpecification getNativeCodeSpecification() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| return currentData.nativeCode; |
| } |
| } |
| |
| public ExportPackageDescription[] getExportPackages() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| return currentData.exportPackages == null ? EMPTY_EXPORTS : currentData.exportPackages; |
| } |
| } |
| |
| public boolean isResolved() { |
| return (stateBits & RESOLVED) != 0; |
| } |
| |
| public State getContainingState() { |
| return containingState; |
| } |
| |
| public BundleDescription[] getFragments() { |
| if (host != null) |
| return EMPTY_BUNDLEDESCS; |
| StateImpl currentState = (StateImpl) getContainingState(); |
| if (currentState == null) |
| throw new IllegalStateException("BundleDescription does not belong to a state."); //$NON-NLS-1$ |
| return currentState.getFragments(this); |
| } |
| |
| public HostSpecification getHost() { |
| return host; |
| } |
| |
| public boolean isSingleton() { |
| return (stateBits & SINGLETON) != 0; |
| } |
| |
| public boolean isRemovalPending() { |
| return (stateBits & REMOVAL_PENDING) != 0; |
| } |
| |
| public boolean hasDynamicImports() { |
| return (stateBits & HAS_DYNAMICIMPORT) != 0; |
| } |
| |
| public boolean attachFragments() { |
| return (stateBits & ATTACH_FRAGMENTS) != 0; |
| } |
| |
| public boolean dynamicFragments() { |
| return (stateBits & DYNAMIC_FRAGMENTS) != 0; |
| } |
| |
| public ExportPackageDescription[] getSelectedExports() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.selectedExports == null) |
| return EMPTY_EXPORTS; |
| return currentData.selectedExports; |
| } |
| } |
| |
| public GenericDescription[] getSelectedGenericCapabilities() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.selectedCapabilities == null) |
| return EMPTY_GENERICDESCS; |
| return currentData.selectedCapabilities; |
| } |
| } |
| |
| public ExportPackageDescription[] getSubstitutedExports() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.substitutedExports == null) |
| return EMPTY_EXPORTS; |
| return currentData.substitutedExports; |
| } |
| } |
| |
| public BundleDescription[] getResolvedRequires() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.resolvedRequires == null) |
| return EMPTY_BUNDLEDESCS; |
| return currentData.resolvedRequires; |
| } |
| } |
| |
| public ExportPackageDescription[] getResolvedImports() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.resolvedImports == null) |
| return EMPTY_EXPORTS; |
| return currentData.resolvedImports; |
| } |
| } |
| |
| public GenericDescription[] getResolvedGenericRequires() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.resolvedCapabilities == null) |
| return EMPTY_GENERICDESCS; |
| return currentData.resolvedCapabilities; |
| } |
| } |
| |
| public Map<String, List<StateWire>> getWires() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.stateWires == null) { |
| currentData.stateWires = new HashMap<>(0); |
| } |
| return currentData.stateWires; |
| } |
| } |
| |
| Map<String, List<StateWire>> getWiresInternal() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| return currentData.stateWires; |
| } |
| } |
| |
| protected void setBundleId(long bundleId) { |
| this.bundleId = bundleId; |
| } |
| |
| protected void setSymbolicName(String symbolicName) { |
| setName(symbolicName); |
| } |
| |
| protected void setLocation(String location) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.location = location; |
| } |
| } |
| |
| protected void setPlatformFilter(String platformFilter) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.platformFilter = platformFilter; |
| } |
| } |
| |
| protected void setExecutionEnvironments(String[] executionEnvironments) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.executionEnvironments = executionEnvironments == null || executionEnvironments.length > 0 ? executionEnvironments : EMPTY_STRING; |
| } |
| } |
| |
| protected void setExportPackages(ExportPackageDescription[] exportPackages) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.exportPackages = exportPackages == null || exportPackages.length > 0 ? exportPackages : EMPTY_EXPORTS; |
| if (exportPackages != null) { |
| for (ExportPackageDescription exportPackage : exportPackages) { |
| ((ExportPackageDescriptionImpl) exportPackage).setExporter(this); |
| } |
| } |
| } |
| } |
| |
| protected void setImportPackages(ImportPackageSpecification[] importPackages) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.importPackages = importPackages == null || importPackages.length > 0 ? importPackages : EMPTY_IMPORTS; |
| if (importPackages != null) { |
| for (ImportPackageSpecification importPackage : importPackages) { |
| ((ImportPackageSpecificationImpl) importPackage).setBundle(this); |
| if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(importPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) { |
| stateBits |= HAS_DYNAMICIMPORT; |
| } |
| } |
| } |
| } |
| } |
| |
| protected void setRequiredBundles(BundleSpecification[] requiredBundles) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.requiredBundles = requiredBundles == null || requiredBundles.length > 0 ? requiredBundles : EMPTY_BUNDLESPECS; |
| if (requiredBundles != null) |
| for (BundleSpecification requiredBundle : requiredBundles) { |
| ((VersionConstraintImpl) requiredBundle).setBundle(this); |
| } |
| } |
| } |
| |
| protected void setGenericCapabilities(GenericDescription[] genericCapabilities) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.genericCapabilities = genericCapabilities == null || genericCapabilities.length > 0 ? genericCapabilities : EMPTY_GENERICDESCS; |
| if (genericCapabilities != null) |
| for (GenericDescription genericCapability : genericCapabilities) { |
| ((GenericDescriptionImpl) genericCapability).setSupplier(this); |
| } |
| } |
| } |
| |
| protected void setGenericRequires(GenericSpecification[] genericRequires) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.genericRequires = genericRequires == null || genericRequires.length > 0 ? genericRequires : EMPTY_GENERICSPECS; |
| if (genericRequires != null) |
| for (GenericSpecification genericRequire : genericRequires) { |
| ((VersionConstraintImpl) genericRequire).setBundle(this); |
| } |
| } |
| } |
| |
| protected void setNativeCodeSpecification(NativeCodeSpecification nativeCode) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.nativeCode = nativeCode; |
| if (nativeCode != null) { |
| ((NativeCodeSpecificationImpl) nativeCode).setBundle(this); |
| NativeCodeDescription[] suppliers = nativeCode.getPossibleSuppliers(); |
| if (suppliers != null) |
| for (NativeCodeDescription supplier : suppliers) { |
| ((NativeCodeDescriptionImpl) supplier).setSupplier(this); |
| } |
| } |
| } |
| } |
| |
| protected int getStateBits() { |
| return stateBits; |
| } |
| |
| protected void setStateBit(int stateBit, boolean on) { |
| synchronized (this.monitor) { |
| if (on) { |
| stateBits |= stateBit; |
| } else { |
| stateBits &= ~stateBit; |
| if (stateBit == RESOLVED) { |
| if (bundleWiring != null) |
| bundleWiring.invalidate(); |
| bundleWiring = null; |
| } |
| } |
| } |
| } |
| |
| protected void setContainingState(State value) { |
| synchronized (this.monitor) { |
| containingState = (StateImpl) value; |
| if (containingState != null && containingState.getReader() != null) { |
| if (containingState.getReader().isLazyLoaded()) |
| stateBits |= LAZY_LOADED; |
| else |
| stateBits &= ~LAZY_LOADED; |
| } else { |
| stateBits &= ~LAZY_LOADED; |
| } |
| } |
| } |
| |
| protected void setHost(HostSpecification host) { |
| synchronized (this.monitor) { |
| this.host = host; |
| if (host != null) { |
| ((VersionConstraintImpl) host).setBundle(this); |
| } |
| } |
| } |
| |
| protected void setLazyLoaded(boolean lazyLoad) { |
| loadLazyData(); |
| synchronized (this.monitor) { |
| if (lazyLoad) |
| stateBits |= LAZY_LOADED; |
| else |
| stateBits &= ~LAZY_LOADED; |
| } |
| } |
| |
| protected void setSelectedExports(ExportPackageDescription[] selectedExports) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.selectedExports = selectedExports == null || selectedExports.length > 0 ? selectedExports : EMPTY_EXPORTS; |
| if (selectedExports != null) { |
| for (ExportPackageDescription selectedExport : selectedExports) { |
| ((ExportPackageDescriptionImpl) selectedExport).setExporter(this); |
| } |
| } |
| } |
| } |
| |
| protected void setSelectedCapabilities(GenericDescription[] selectedCapabilities) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.selectedCapabilities = selectedCapabilities == null || selectedCapabilities.length > 0 ? selectedCapabilities : EMPTY_GENERICDESCS; |
| if (selectedCapabilities != null) { |
| for (GenericDescription capability : selectedCapabilities) { |
| ((GenericDescriptionImpl) capability).setSupplier(this); |
| } |
| } |
| } |
| } |
| |
| protected void setSubstitutedExports(ExportPackageDescription[] substitutedExports) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.substitutedExports = substitutedExports != null && substitutedExports.length > 0 ? substitutedExports : EMPTY_EXPORTS; |
| } |
| } |
| |
| protected void setResolvedImports(ExportPackageDescription[] resolvedImports) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.resolvedImports = resolvedImports == null || resolvedImports.length > 0 ? resolvedImports : EMPTY_EXPORTS; |
| } |
| } |
| |
| protected void setResolvedRequires(BundleDescription[] resolvedRequires) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.resolvedRequires = resolvedRequires == null || resolvedRequires.length > 0 ? resolvedRequires : EMPTY_BUNDLEDESCS; |
| } |
| } |
| |
| protected void setResolvedCapabilities(GenericDescription[] resolvedCapabilities) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.resolvedCapabilities = resolvedCapabilities == null || resolvedCapabilities.length > 0 ? resolvedCapabilities : EMPTY_GENERICDESCS; |
| } |
| } |
| |
| protected void setStateWires(Map<String, List<StateWire>> stateWires) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.stateWires = stateWires; |
| } |
| } |
| |
| void clearAddedDynamicImportPackages() { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.addedDynamicImports = null; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| if (getSymbolicName() == null) |
| return "[" + getBundleId() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| return getSymbolicName() + "_" + getVersion(); //$NON-NLS-1$ |
| } |
| |
| public Object getKey() { |
| return Long.valueOf(bundleId); |
| } |
| |
| public boolean compare(KeyedElement other) { |
| if (!(other instanceof BundleDescriptionImpl)) |
| return false; |
| BundleDescriptionImpl otherBundleDescription = (BundleDescriptionImpl) other; |
| return bundleId == otherBundleDescription.bundleId; |
| } |
| |
| public int getKeyHashCode() { |
| return (int) (bundleId ^ (bundleId >>> 32)); |
| } |
| |
| /* TODO Determine if we need more than just Object ID type of hashcode. |
| public int hashCode() { |
| if (getSymbolicName() == null) |
| return (int) (bundleId % Integer.MAX_VALUE); |
| return (int) ((bundleId * (getSymbolicName().hashCode())) % Integer.MAX_VALUE); |
| } |
| */ |
| |
| protected void removeDependencies() { |
| synchronized (this.monitor) { |
| if (dependencies == null) |
| return; |
| Iterator<BundleDescription> iter = dependencies.iterator(); |
| while (iter.hasNext()) { |
| ((BundleDescriptionImpl) iter.next()).removeDependent(this); |
| } |
| dependencies = null; |
| } |
| } |
| |
| protected void addDependencies(BaseDescription[] newDependencies, boolean checkDups) { |
| synchronized (this.monitor) { |
| if (newDependencies == null) |
| return; |
| if (!checkDups && dependencies == null) |
| dependencies = new ArrayList<>(newDependencies.length); |
| for (BaseDescription newDependency : newDependencies) { |
| addDependency((BaseDescriptionImpl) newDependency, checkDups); |
| } |
| } |
| } |
| |
| protected void addDependency(BaseDescriptionImpl dependency, boolean checkDups) { |
| synchronized (this.monitor) { |
| BundleDescriptionImpl bundle = (BundleDescriptionImpl) dependency.getSupplier(); |
| if (bundle == this) |
| return; |
| if (dependencies == null) |
| dependencies = new ArrayList<>(10); |
| if (!checkDups || !dependencies.contains(bundle)) { |
| bundle.addDependent(this); |
| dependencies.add(bundle); |
| } |
| } |
| } |
| |
| /* |
| * Gets all the bundle dependencies as a result of import-package or require-bundle. |
| * Self and fragment bundles are removed. |
| */ |
| List<BundleDescription> getBundleDependencies() { |
| synchronized (this.monitor) { |
| if (dependencies == null) |
| return new ArrayList<>(0); |
| ArrayList<BundleDescription> required = new ArrayList<>(dependencies.size()); |
| for (Iterator<BundleDescription> iter = dependencies.iterator(); iter.hasNext();) { |
| BundleDescription dep = iter.next(); |
| if (dep != this && dep.getHost() == null) |
| required.add(dep); |
| } |
| return required; |
| } |
| } |
| |
| protected void addDependent(BundleDescription dependent) { |
| synchronized (this.monitor) { |
| if (dependents == null) |
| dependents = new ArrayList<>(10); |
| // no need to check for duplicates here; this is only called in addDepenency which already checks for dups. |
| dependents.add(dependent); |
| } |
| } |
| |
| protected void removeDependent(BundleDescription dependent) { |
| synchronized (this.monitor) { |
| if (dependents == null) |
| return; |
| dependents.remove(dependent); |
| } |
| } |
| |
| public BundleDescription[] getDependents() { |
| synchronized (this.monitor) { |
| if (dependents == null) |
| return EMPTY_BUNDLEDESCS; |
| return dependents.toArray(new BundleDescription[dependents.size()]); |
| } |
| } |
| |
| boolean hasDependents() { |
| synchronized (this.monitor) { |
| return dependents == null ? false : dependents.size() > 0; |
| } |
| } |
| |
| void setFullyLoaded(boolean fullyLoaded) { |
| synchronized (this.monitor) { |
| if (fullyLoaded) { |
| stateBits |= FULLY_LOADED; |
| } else { |
| stateBits &= ~FULLY_LOADED; |
| } |
| } |
| } |
| |
| boolean isFullyLoaded() { |
| return (stateBits & FULLY_LOADED) != 0; |
| } |
| |
| void setLazyDataOffset(int lazyDataOffset) { |
| this.lazyDataOffset = lazyDataOffset; |
| } |
| |
| int getLazyDataOffset() { |
| return this.lazyDataOffset; |
| } |
| |
| void setLazyDataSize(int lazyDataSize) { |
| this.lazyDataSize = lazyDataSize; |
| } |
| |
| int getLazyDataSize() { |
| return this.lazyDataSize; |
| } |
| |
| // DO NOT call while holding this.monitor |
| private LazyData loadLazyData() { |
| // TODO add back if ee min 1.2 adds holdsLock method |
| //if (Thread.holdsLock(this.monitor)) { |
| // throw new IllegalStateException("Should not call fullyLoad() holding monitor."); //$NON-NLS-1$ |
| //} |
| if ((stateBits & LAZY_LOADED) == 0) |
| return this.lazyData; |
| |
| StateImpl currentState = (StateImpl) getContainingState(); |
| StateReader reader = currentState == null ? null : currentState.getReader(); |
| if (reader == null) |
| throw new IllegalStateException("No valid reader for the bundle description"); //$NON-NLS-1$ |
| |
| synchronized (currentState.monitor) { |
| if (isFullyLoaded()) { |
| reader.setAccessedFlag(true); // set reader accessed flag |
| return this.lazyData; |
| } |
| try { |
| reader.fullyLoad(this); |
| return this.lazyData; |
| } catch (IOException e) { |
| throw new RuntimeException(e.getMessage(), e); // TODO not sure what to do here!! |
| } |
| } |
| } |
| |
| void addDynamicResolvedImport(ExportPackageDescriptionImpl result) { |
| synchronized (this.monitor) { |
| // mark the dependency |
| addDependency(result, true); |
| // add the export to the list of the resolvedImports |
| checkLazyData(); |
| if (lazyData.resolvedImports == null) { |
| lazyData.resolvedImports = new ExportPackageDescription[] {result}; |
| return; |
| } |
| ExportPackageDescription[] newImports = new ExportPackageDescription[lazyData.resolvedImports.length + 1]; |
| System.arraycopy(lazyData.resolvedImports, 0, newImports, 0, lazyData.resolvedImports.length); |
| newImports[newImports.length - 1] = result; |
| lazyData.resolvedImports = newImports; |
| } |
| } |
| |
| void addDynamicImportPackages(ImportPackageSpecification[] dynamicImport) { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| if (currentData.addedDynamicImports == null) |
| currentData.addedDynamicImports = new ArrayList<>(); |
| for (ImportPackageSpecification addImport : dynamicImport) { |
| if (!ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(addImport.getDirective(Constants.RESOLUTION_DIRECTIVE))) |
| throw new IllegalArgumentException("Import must be a dynamic import."); //$NON-NLS-1$ |
| } |
| adding: for (ImportPackageSpecification addImport : dynamicImport) { |
| for (ImportPackageSpecification currentImport : currentData.addedDynamicImports) { |
| if (equalImports(addImport, currentImport)) |
| continue adding; |
| } |
| ((ImportPackageSpecificationImpl) addImport).setBundle(this); |
| currentData.addedDynamicImports.add(addImport); |
| } |
| } |
| } |
| |
| private boolean equalImports(ImportPackageSpecification addImport, ImportPackageSpecification currentImport) { |
| if (!isEqual(addImport.getName(), currentImport.getName())) |
| return false; |
| if (!isEqual(addImport.getVersionRange(), currentImport.getVersionRange())) |
| return false; |
| if (!isEqual(addImport.getBundleSymbolicName(), currentImport.getBundleSymbolicName())) |
| return false; |
| if (!isEqual(addImport.getBundleVersionRange(), currentImport.getBundleVersionRange())) |
| return false; |
| return isEqual(addImport.getAttributes(), currentImport.getAttributes()); |
| } |
| |
| private boolean isEqual(Object o1, Object o2) { |
| return (o1 == null) ? o2 == null : o1.equals(o2); |
| } |
| |
| void unload() { |
| StateImpl currentState = (StateImpl) getContainingState(); |
| StateReader reader = currentState == null ? null : currentState.getReader(); |
| if (reader == null) |
| throw new IllegalStateException("BundleDescription does not belong to a reader."); //$NON-NLS-1$ |
| synchronized (currentState.monitor) { |
| if ((stateBits & LAZY_LOADED) == 0) |
| return; |
| if (!isFullyLoaded()) |
| return; |
| synchronized (this.monitor) { |
| setFullyLoaded(false); |
| lazyData = null; |
| } |
| } |
| } |
| |
| void setDynamicStamps(Map<String, Long> dynamicStamps) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| lazyData.dynamicStamps = dynamicStamps; |
| } |
| } |
| |
| void setDynamicStamp(String requestedPackage, Long timestamp) { |
| synchronized (this.monitor) { |
| checkLazyData(); |
| if (lazyData.dynamicStamps == null) { |
| if (timestamp == null) |
| return; |
| lazyData.dynamicStamps = new HashMap<>(); |
| } |
| if (timestamp == null) |
| lazyData.dynamicStamps.remove(requestedPackage); |
| else |
| lazyData.dynamicStamps.put(requestedPackage, timestamp); |
| } |
| } |
| |
| long getDynamicStamp(String requestedPackage) { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| Long stamp = currentData.dynamicStamps == null ? null : (Long) currentData.dynamicStamps.get(requestedPackage); |
| return stamp == null ? 0 : stamp.longValue(); |
| } |
| } |
| |
| Map<String, Long> getDynamicStamps() { |
| LazyData currentData = loadLazyData(); |
| synchronized (this.monitor) { |
| return currentData.dynamicStamps; |
| } |
| } |
| |
| public void setEquinoxEE(int equinox_ee) { |
| this.equinox_ee = equinox_ee; |
| } |
| |
| public int getEquinoxEE() { |
| return equinox_ee; |
| } |
| |
| private void checkLazyData() { |
| if (lazyData == null) |
| lazyData = new LazyData(); |
| } |
| |
| final class LazyData { |
| String location; |
| String platformFilter; |
| |
| BundleSpecification[] requiredBundles; |
| ExportPackageDescription[] exportPackages; |
| ImportPackageSpecification[] importPackages; |
| GenericDescription[] genericCapabilities; |
| GenericSpecification[] genericRequires; |
| NativeCodeSpecification nativeCode; |
| |
| ExportPackageDescription[] selectedExports; |
| GenericDescription[] selectedCapabilities; |
| BundleDescription[] resolvedRequires; |
| ExportPackageDescription[] resolvedImports; |
| GenericDescription[] resolvedCapabilities; |
| ExportPackageDescription[] substitutedExports; |
| String[] executionEnvironments; |
| |
| Map<String, Long> dynamicStamps; |
| Map<String, List<StateWire>> stateWires; |
| // Note that this is not persisted in the state cache |
| List<ImportPackageSpecification> addedDynamicImports; |
| } |
| |
| public Map<String, Object> getAttributes() { |
| synchronized (this.monitor) { |
| return attributes; |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| void setAttributes(Map<String, ?> attributes) { |
| synchronized (this.monitor) { |
| this.attributes = (Map<String, Object>) attributes; |
| } |
| } |
| |
| Object getDirective(String key) { |
| synchronized (this.monitor) { |
| if (Constants.MANDATORY_DIRECTIVE.equals(key)) |
| return mandatory; |
| if (Constants.SINGLETON_DIRECTIVE.equals(key)) |
| return isSingleton() ? Boolean.TRUE : Boolean.FALSE; |
| if (Constants.FRAGMENT_ATTACHMENT_DIRECTIVE.equals(key)) { |
| if (!attachFragments()) |
| return Constants.FRAGMENT_ATTACHMENT_NEVER; |
| if (dynamicFragments()) |
| return Constants.FRAGMENT_ATTACHMENT_ALWAYS; |
| return Constants.FRAGMENT_ATTACHMENT_RESOLVETIME; |
| } |
| } |
| return null; |
| } |
| |
| void setDirective(String key, Object value) { |
| // only pay attention to mandatory directive for now; others are set with setState method |
| if (Constants.MANDATORY_DIRECTIVE.equals(key)) |
| mandatory = (String[]) value; |
| } |
| |
| @SuppressWarnings("unchecked") |
| void setArbitraryDirectives(Map<String, ?> directives) { |
| synchronized (this.monitor) { |
| this.arbitraryDirectives = (Map<String, String>) directives; |
| } |
| } |
| |
| Map<String, String> getArbitraryDirectives() { |
| synchronized (this.monitor) { |
| return arbitraryDirectives; |
| } |
| } |
| |
| public Map<String, String> getDeclaredDirectives() { |
| Map<String, String> result = new HashMap<>(2); |
| Map<String, String> arbitrary = getArbitraryDirectives(); |
| if (arbitrary != null) |
| result.putAll(arbitrary); |
| if (!attachFragments()) { |
| result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_NEVER); |
| } else { |
| if (dynamicFragments()) |
| result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_ALWAYS); |
| else |
| result.put(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE, Constants.FRAGMENT_ATTACHMENT_RESOLVETIME); |
| } |
| if (isSingleton()) |
| result.put(Constants.SINGLETON_DIRECTIVE, Boolean.TRUE.toString()); |
| String[] mandatoryDirective = (String[]) getDirective(Constants.MANDATORY_DIRECTIVE); |
| if (mandatoryDirective != null) |
| result.put(Constants.MANDATORY_DIRECTIVE, ExportPackageDescriptionImpl.toString(mandatoryDirective)); |
| return Collections.unmodifiableMap(result); |
| } |
| |
| public Map<String, Object> getDeclaredAttributes() { |
| Map<String, Object> result = new HashMap<>(1); |
| synchronized (this.monitor) { |
| if (attributes != null) |
| result.putAll(attributes); |
| } |
| result.put(BundleRevision.BUNDLE_NAMESPACE, getName()); |
| result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, getVersion()); |
| return Collections.unmodifiableMap(result); |
| } |
| |
| public List<BundleRequirement> getDeclaredRequirements(String namespace) { |
| List<BundleRequirement> result = new ArrayList<>(); |
| if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) { |
| BundleSpecification[] requires = getRequiredBundles(); |
| for (BundleSpecification require : requires) { |
| result.add(require.getRequirement()); |
| } |
| } |
| if (host != null && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) { |
| result.add(host.getRequirement()); |
| } |
| if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) { |
| ImportPackageSpecification[] imports = getImportPackages(); |
| for (ImportPackageSpecification importPkg : imports) |
| result.add(importPkg.getRequirement()); |
| } |
| GenericSpecification[] genericSpecifications = getGenericRequires(); |
| for (GenericSpecification requirement : genericSpecifications) { |
| if (namespace == null || namespace.equals(requirement.getType())) |
| result.add(requirement.getRequirement()); |
| } |
| return Collections.unmodifiableList(result); |
| } |
| |
| public List<BundleCapability> getDeclaredCapabilities(String namespace) { |
| List<BundleCapability> result = new ArrayList<>(); |
| if (host == null) { |
| if (getSymbolicName() != null) { |
| if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) { |
| result.add(BundleDescriptionImpl.this.getCapability()); |
| } |
| if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) { |
| result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE)); |
| } |
| } |
| |
| } else { |
| // may need to have a osgi.wiring.fragment capability |
| } |
| if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) { |
| ExportPackageDescription[] exports = getExportPackages(); |
| for (ExportPackageDescription exportPkg : exports) |
| result.add(exportPkg.getCapability()); |
| } |
| GenericDescription[] genericCapabilities = getGenericCapabilities(); |
| for (GenericDescription capabilitiy : genericCapabilities) { |
| if (namespace == null || namespace.equals(capabilitiy.getType())) |
| result.add(capabilitiy.getCapability()); |
| } |
| return Collections.unmodifiableList(result); |
| } |
| |
| public int getTypes() { |
| return getHost() != null ? BundleRevision.TYPE_FRAGMENT : 0; |
| } |
| |
| public Bundle getBundle() { |
| Object ref = getUserObject(); |
| if (ref instanceof BundleReference) |
| return ((BundleReference) ref).getBundle(); |
| return null; |
| } |
| |
| @Override |
| String getInternalNameSpace() { |
| return BundleRevision.BUNDLE_NAMESPACE; |
| } |
| |
| public BundleWiring getWiring() { |
| synchronized (this.monitor) { |
| if (bundleWiring != null || !isResolved()) |
| return bundleWiring; |
| return bundleWiring = new DescriptionWiring(); |
| } |
| } |
| |
| static class BundleWireImpl implements BundleWire { |
| private final BundleCapability capability; |
| private final BundleWiring provider; |
| private final BundleRequirement requirement; |
| private final BundleWiring requirer; |
| |
| public BundleWireImpl(StateWire wire) { |
| VersionConstraint declaredRequirement = wire.getDeclaredRequirement(); |
| if (declaredRequirement instanceof HostSpecification) |
| this.capability = ((BaseDescriptionImpl) wire.getDeclaredCapability()).getCapability(BundleRevision.HOST_NAMESPACE); |
| else |
| this.capability = wire.getDeclaredCapability().getCapability(); |
| this.provider = wire.getCapabilityHost().getWiring(); |
| this.requirement = declaredRequirement.getRequirement(); |
| this.requirer = wire.getRequirementHost().getWiring(); |
| } |
| |
| public BundleCapability getCapability() { |
| return capability; |
| } |
| |
| public BundleRequirement getRequirement() { |
| return requirement; |
| } |
| |
| public BundleWiring getProviderWiring() { |
| return provider; |
| } |
| |
| public BundleWiring getRequirerWiring() { |
| return requirer; |
| } |
| |
| @Override |
| public int hashCode() { |
| int hashcode = 31 + capability.hashCode(); |
| hashcode = hashcode * 31 + requirement.hashCode(); |
| hashcode = hashcode * 31 + provider.hashCode(); |
| hashcode = hashcode * 31 + requirer.hashCode(); |
| return hashcode; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof BundleWireImpl)) |
| return false; |
| BundleWireImpl other = (BundleWireImpl) obj; |
| return capability.equals(other.getCapability()) && requirement.equals(other.getRequirement()) && provider.equals(other.getProviderWiring()) && requirer.equals(other.getRequirerWiring()); |
| } |
| |
| @Override |
| public String toString() { |
| return getRequirement() + " -> " + getCapability(); //$NON-NLS-1$ |
| } |
| |
| public BundleRevision getProvider() { |
| return provider.getRevision(); |
| } |
| |
| public BundleRevision getRequirer() { |
| return requirer.getRevision(); |
| } |
| } |
| |
| /** |
| * Coerce the generic type of a list from List<BundleWire> |
| * to List<Wire> |
| * @param l List to be coerced. |
| * @return l coerced to List<Wire> |
| */ |
| @SuppressWarnings("unchecked") |
| static List<Wire> asListWire(List<? extends Wire> l) { |
| return (List<Wire>) l; |
| } |
| |
| /** |
| * Coerce the generic type of a list from List<BundleCapability> |
| * to List<Capability> |
| * @param l List to be coerced. |
| * @return l coerced to List<Capability> |
| */ |
| @SuppressWarnings("unchecked") |
| static List<Capability> asListCapability(List<? extends Capability> l) { |
| return (List<Capability>) l; |
| } |
| |
| /** |
| * Coerce the generic type of a list from List<BundleRequirement> |
| * to List<Requirement> |
| * @param l List to be coerced. |
| * @return l coerced to List<Requirement> |
| */ |
| @SuppressWarnings("unchecked") |
| static List<Requirement> asListRequirement(List<? extends Requirement> l) { |
| return (List<Requirement>) l; |
| } |
| |
| // Note that description wiring are identity equality based |
| class DescriptionWiring implements BundleWiring { |
| private volatile boolean valid = true; |
| |
| public Bundle getBundle() { |
| return BundleDescriptionImpl.this.getBundle(); |
| } |
| |
| public boolean isInUse() { |
| return valid && (isCurrent() || BundleDescriptionImpl.this.hasDependents()); |
| } |
| |
| void invalidate() { |
| valid = false; |
| } |
| |
| public boolean isCurrent() { |
| return valid && !BundleDescriptionImpl.this.isRemovalPending(); |
| } |
| |
| public List<BundleCapability> getCapabilities(String namespace) { |
| if (!isInUse()) |
| return null; |
| List<BundleCapability> result = new ArrayList<>(); |
| GenericDescription[] genericCapabilities = getSelectedGenericCapabilities(); |
| for (GenericDescription capabilitiy : genericCapabilities) { |
| if (namespace == null || namespace.equals(capabilitiy.getType())) |
| result.add(capabilitiy.getCapability()); |
| } |
| if (host != null) |
| return result; |
| if (getSymbolicName() != null) { |
| if (namespace == null || BundleRevision.BUNDLE_NAMESPACE.equals(namespace)) { |
| result.add(BundleDescriptionImpl.this.getCapability()); |
| } |
| if (attachFragments() && (namespace == null || BundleRevision.HOST_NAMESPACE.equals(namespace))) { |
| result.add(BundleDescriptionImpl.this.getCapability(BundleRevision.HOST_NAMESPACE)); |
| } |
| } |
| if (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) { |
| ExportPackageDescription[] exports = getSelectedExports(); |
| for (ExportPackageDescription exportPkg : exports) |
| result.add(exportPkg.getCapability()); |
| } |
| return result; |
| } |
| |
| public List<Capability> getResourceCapabilities(String namespace) { |
| return asListCapability(getCapabilities(namespace)); |
| } |
| |
| public List<BundleRequirement> getRequirements(String namespace) { |
| List<BundleWire> requiredWires = getRequiredWires(namespace); |
| if (requiredWires == null) |
| // happens if not in use |
| return null; |
| List<BundleRequirement> requirements = new ArrayList<>(requiredWires.size()); |
| for (BundleWire wire : requiredWires) { |
| if (!requirements.contains(wire.getRequirement())) |
| requirements.add(wire.getRequirement()); |
| } |
| // get dynamic imports |
| if (getHost() == null && (namespace == null || BundleRevision.PACKAGE_NAMESPACE.equals(namespace))) { |
| // TODO need to handle fragments that add dynamic imports |
| if (hasDynamicImports()) { |
| ImportPackageSpecification[] imports = getImportPackages(); |
| for (ImportPackageSpecification impPackage : imports) { |
| if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(impPackage.getDirective(Constants.RESOLUTION_DIRECTIVE))) { |
| BundleRequirement req = impPackage.getRequirement(); |
| if (!requirements.contains(req)) |
| requirements.add(req); |
| } |
| } |
| } |
| ImportPackageSpecification[] addedDynamic = getAddedDynamicImportPackages(); |
| for (ImportPackageSpecification dynamicImport : addedDynamic) { |
| BundleRequirement req = dynamicImport.getRequirement(); |
| if (!requirements.contains(req)) |
| requirements.add(req); |
| } |
| } |
| return requirements; |
| } |
| |
| public List<Requirement> getResourceRequirements(String namespace) { |
| return asListRequirement(getRequirements(namespace)); |
| } |
| |
| public List<BundleWire> getProvidedWires(String namespace) { |
| if (!isInUse()) |
| return null; |
| BundleDescription[] dependentBundles = getDependents(); |
| List<BundleWire> unorderedResult = new ArrayList<>(); |
| for (BundleDescription dependent : dependentBundles) { |
| List<BundleWire> dependentWires = dependent.getWiring().getRequiredWires(namespace); |
| if (dependentWires != null) |
| for (BundleWire bundleWire : dependentWires) { |
| if (bundleWire.getProviderWiring() == this) |
| unorderedResult.add(bundleWire); |
| } |
| } |
| List<BundleWire> orderedResult = new ArrayList<>(unorderedResult.size()); |
| List<BundleCapability> capabilities = getCapabilities(namespace); |
| for (BundleCapability capability : capabilities) { |
| for (Iterator<BundleWire> wires = unorderedResult.iterator(); wires.hasNext();) { |
| BundleWire wire = wires.next(); |
| if (wire.getCapability().equals(capability)) { |
| wires.remove(); |
| orderedResult.add(wire); |
| } |
| } |
| } |
| return orderedResult; |
| } |
| |
| public List<Wire> getProvidedResourceWires(String namespace) { |
| return asListWire(getProvidedWires(namespace)); |
| } |
| |
| public List<BundleWire> getRequiredWires(String namespace) { |
| if (!isInUse()) |
| return null; |
| List<BundleWire> result = Collections.<BundleWire> emptyList(); |
| Map<String, List<StateWire>> wireMap = getWires(); |
| if (namespace == null) { |
| result = new ArrayList<>(); |
| for (List<StateWire> wires : wireMap.values()) { |
| for (StateWire wire : wires) { |
| result.add(new BundleWireImpl(wire)); |
| } |
| } |
| return result; |
| } |
| List<StateWire> wires = wireMap.get(namespace); |
| if (wires == null) |
| return result; |
| result = new ArrayList<>(wires.size()); |
| for (StateWire wire : wires) { |
| result.add(new BundleWireImpl(wire)); |
| } |
| return result; |
| } |
| |
| public List<Wire> getRequiredResourceWires(String namespace) { |
| return asListWire(getRequiredWires(namespace)); |
| } |
| |
| public BundleRevision getRevision() { |
| return BundleDescriptionImpl.this; |
| } |
| |
| public BundleRevision getResource() { |
| return getRevision(); |
| } |
| |
| public ClassLoader getClassLoader() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public List<URL> findEntries(String path, String filePattern, int options) { |
| return null; |
| } |
| |
| public Collection<String> listResources(String path, String filePattern, int options) { |
| return null; |
| } |
| |
| @Override |
| public String toString() { |
| return BundleDescriptionImpl.this.toString(); |
| } |
| } |
| |
| public List<Capability> getCapabilities(String namespace) { |
| return asListCapability(getDeclaredCapabilities(namespace)); |
| } |
| |
| public List<Requirement> getRequirements(String namespace) { |
| return asListRequirement(getDeclaredRequirements(namespace)); |
| } |
| } |