Copyright (c) 2012, 2017 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
/******************************************************************************* * Copyright (c) 2012, 2017 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 *******************************************************************************/
package org.eclipse.osgi.container; import java.net.URL; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.osgi.container.ModuleRevisionBuilder.GenericInfo; import org.eclipse.osgi.internal.container.AtomicLazyInitializer; import org.eclipse.osgi.internal.container.InternalUtils; import org.osgi.framework.AdminPermission; import org.osgi.framework.Bundle; import org.osgi.framework.namespace.HostNamespace; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.wiring.*; import org.osgi.resource.*;
An implementation of BundleWiring.
Since:3.10
/** * An implementation of {@link BundleWiring}. * @since 3.10 */
public final class ModuleWiring implements BundleWiring { class LoaderInitializer implements Callable<ModuleLoader> { @Override public ModuleLoader call() throws Exception { if (!isValid) { return null; } return getRevision().getRevisions().getContainer().adaptor.createModuleLoader(ModuleWiring.this); } } private static final RuntimePermission GET_CLASSLOADER_PERM = new RuntimePermission("getClassLoader"); //$NON-NLS-1$ private static final String DYNAMICALLY_ADDED_IMPORT_DIRECTIVE = "x.dynamically.added"; //$NON-NLS-1$ private final ModuleRevision revision; private volatile List<ModuleCapability> capabilities; private volatile List<ModuleRequirement> requirements; private final Collection<String> substitutedPkgNames; private final AtomicLazyInitializer<ModuleLoader> loader = new AtomicLazyInitializer<>(); private final LoaderInitializer loaderInitializer = new LoaderInitializer(); private volatile List<ModuleWire> providedWires; private volatile List<ModuleWire> requiredWires; volatile boolean isValid = true; private final AtomicReference<Set<String>> dynamicMissRef = new AtomicReference<>(); ModuleWiring(ModuleRevision revision, List<ModuleCapability> capabilities, List<ModuleRequirement> requirements, List<ModuleWire> providedWires, List<ModuleWire> requiredWires, Collection<String> substitutedPkgNames) { super(); this.revision = revision; this.capabilities = capabilities; this.requirements = requirements; this.providedWires = providedWires; this.requiredWires = requiredWires; this.substitutedPkgNames = substitutedPkgNames.isEmpty() ? Collections.<String> emptyList() : substitutedPkgNames; } @Override public Bundle getBundle() { return revision.getBundle(); } @Override public boolean isCurrent() { return isValid && revision.isCurrent(); } @Override public boolean isInUse() { return isCurrent() || !providedWires.isEmpty() || isFragmentInUse(); } private boolean isFragmentInUse() { // A fragment is considered in use if it has any required host wires if ((BundleRevision.TYPE_FRAGMENT & revision.getTypes()) != 0) { List<ModuleWire> hostWires = getRequiredModuleWires(HostNamespace.HOST_NAMESPACE); // hostWires may be null if the fragment wiring is no longer valid return hostWires == null ? false : !hostWires.isEmpty(); } return false; }
Returns the same result as getCapabilities(String) except uses type ModuleCapability.
Params:
  • namespace – the namespace
See Also:
Returns:the capabilities
/** * Returns the same result as {@link #getCapabilities(String)} except * uses type ModuleCapability. * @param namespace the namespace * @return the capabilities * @see #getCapabilities(String) */
public List<ModuleCapability> getModuleCapabilities(String namespace) { return getModuleCapabilities(namespace, capabilities); } private List<ModuleCapability> getModuleCapabilities(String namespace, List<ModuleCapability> allCapabilities) { if (!isValid) return null; if (namespace == null) return new ArrayList<>(allCapabilities); List<ModuleCapability> result = new ArrayList<>(); for (ModuleCapability capability : allCapabilities) { if (namespace.equals(capability.getNamespace())) { result.add(capability); } } return result; }
Returns the same result as getRequirements(String) except uses type ModuleRequirement.
Params:
  • namespace – the namespace
See Also:
Returns:the requirements
/** * Returns the same result as {@link #getRequirements(String)} except * uses type ModuleRequirement. * @param namespace the namespace * @return the requirements * @see #getRequirements(String) */
public List<ModuleRequirement> getModuleRequirements(String namespace) { return getModuleRequirements(namespace, requirements); } List<ModuleRequirement> getPersistentRequirements() { List<ModuleRequirement> persistentRequriements = getModuleRequirements(null); if (persistentRequriements == null) { return null; } for (Iterator<ModuleRequirement> iRequirements = persistentRequriements.iterator(); iRequirements.hasNext();) { ModuleRequirement requirement = iRequirements.next(); if (PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) { if ("true".equals(requirement.getDirectives().get(DYNAMICALLY_ADDED_IMPORT_DIRECTIVE))) { //$NON-NLS-1$ iRequirements.remove(); } } } return persistentRequriements; } private List<ModuleRequirement> getModuleRequirements(String namespace, List<ModuleRequirement> allRequirements) { if (!isValid) return null; if (namespace == null) return new ArrayList<>(allRequirements); List<ModuleRequirement> result = new ArrayList<>(); for (ModuleRequirement requirement : allRequirements) { if (namespace.equals(requirement.getNamespace())) { result.add(requirement); } } return result; } @Override public List<BundleCapability> getCapabilities(String namespace) { return InternalUtils.asListBundleCapability(getModuleCapabilities(namespace)); } @Override public List<BundleRequirement> getRequirements(String namespace) { return InternalUtils.asListBundleRequirement(getModuleRequirements(namespace)); }
Returns the same result as getProvidedWires(String) except uses type ModuleWire.
Params:
  • namespace – the namespace
See Also:
Returns:the wires
/** * Returns the same result as {@link #getProvidedWires(String)} except * uses type ModuleWire. * @param namespace the namespace * @return the wires * @see #getProvidedWires(String) */
public List<ModuleWire> getProvidedModuleWires(String namespace) { return getWires(namespace, providedWires); } List<ModuleWire> getPersistentProvidedWires() { return getPersistentWires(providedWires); }
Returns the same result as getRequiredWires(String) except uses type ModuleWire.
Params:
  • namespace – the namespace
See Also:
Returns:the wires
/** * Returns the same result as {@link #getRequiredWires(String)} except * uses type ModuleWire. * @param namespace the namespace * @return the wires * @see #getRequiredWires(String) */
public List<ModuleWire> getRequiredModuleWires(String namespace) { return getWires(namespace, requiredWires); } List<ModuleWire> getPersistentRequiredWires() { return getPersistentWires(requiredWires); } private List<ModuleWire> getPersistentWires(List<ModuleWire> allWires) { List<ModuleWire> persistentWires = getWires(null, allWires); if (persistentWires == null) { return null; } for (Iterator<ModuleWire> iWires = persistentWires.iterator(); iWires.hasNext();) { ModuleWire wire = iWires.next(); if (PackageNamespace.PACKAGE_NAMESPACE.equals(wire.getRequirement().getNamespace())) { if ("true".equals(wire.getRequirement().getDirectives().get(DYNAMICALLY_ADDED_IMPORT_DIRECTIVE))) { //$NON-NLS-1$ iWires.remove(); } } } return persistentWires; } @Override public List<BundleWire> getProvidedWires(String namespace) { return InternalUtils.asListBundleWire(getWires(namespace, providedWires)); } @Override public List<BundleWire> getRequiredWires(String namespace) { return InternalUtils.asListBundleWire(getWires(namespace, requiredWires)); } private List<ModuleWire> getWires(String namespace, List<ModuleWire> allWires) { if (!isValid) return null; if (namespace == null) return new ArrayList<>(allWires); List<ModuleWire> result = new ArrayList<>(); for (ModuleWire moduleWire : allWires) { if (namespace.equals(moduleWire.getCapability().getNamespace())) { result.add(moduleWire); } } return result; } @Override public ModuleRevision getRevision() { return revision; } @Override public ClassLoader getClassLoader() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(GET_CLASSLOADER_PERM); } if (!isValid) { return null; } ModuleLoader current = getModuleLoader(); if (current == null) { // must not be valid return null; } return current.getClassLoader(); }
Returns the module loader for this wiring. If the module loader does not exist yet then one will be created
Returns:the module loader for this wiring.
/** * Returns the module loader for this wiring. If the module * loader does not exist yet then one will be created * @return the module loader for this wiring. */
public ModuleLoader getModuleLoader() { return loader.getInitialized(loaderInitializer); } void loadFragments(Collection<ModuleRevision> fragments) { ModuleLoader current = loader.get(); if (current != null) { current.loadFragments(fragments); } } @Override public List<URL> findEntries(String path, String filePattern, int options) { if (!hasResourcePermission()) return Collections.emptyList(); if (!isValid) { return null; } ModuleLoader current = getModuleLoader(); if (current == null) { // must not be valid return null; } return current.findEntries(path, filePattern, options); } @Override public Collection<String> listResources(String path, String filePattern, int options) { if (!hasResourcePermission()) return Collections.emptyList(); if (!isValid) { return null; } ModuleLoader current = getModuleLoader(); if (current == null) { // must not be valid return null; } return current.listResources(path, filePattern, options); } @Override public List<Capability> getResourceCapabilities(String namespace) { return InternalUtils.asListCapability(getCapabilities(namespace)); } @Override public List<Requirement> getResourceRequirements(String namespace) { return InternalUtils.asListRequirement(getRequirements(namespace)); } @Override public List<Wire> getProvidedResourceWires(String namespace) { return InternalUtils.asListWire(getWires(namespace, providedWires)); } @Override public List<Wire> getRequiredResourceWires(String namespace) { return InternalUtils.asListWire(getWires(namespace, requiredWires)); } @Override public ModuleRevision getResource() { return revision; } void setProvidedWires(List<ModuleWire> providedWires) { this.providedWires = providedWires; } void setRequiredWires(List<ModuleWire> requiredWires) { this.requiredWires = requiredWires; } void setCapabilities(List<ModuleCapability> capabilities) { this.capabilities = capabilities; } void unload() { // When unloading a wiring we need to release the loader. // This is so that the loaders are not pinned when stopping the framework. // Then the framework can be relaunched, at which point new loaders will // get created. invalidate0(true); } void invalidate() { invalidate0(false); } private void invalidate0(boolean releaseLoader) { // set the isValid to false first isValid = false; ModuleLoader current = releaseLoader ? loader.getAndClear() : loader.get(); revision.getRevisions().getContainer().getAdaptor().invalidateWiring(this, current); } void validate() { this.isValid = true; } boolean isSubtituted(ModuleCapability capability) { if (!PackageNamespace.PACKAGE_NAMESPACE.equals(capability.getNamespace())) { return false; } return substitutedPkgNames.contains(capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)); }
Returns true if the specified package name has been substituted in this wiring
Params:
  • packageName – the package name to check
Returns:true if the specified package name has been substituted in this wiring
/** * Returns true if the specified package name has been substituted in this wiring * @param packageName the package name to check * @return true if the specified package name has been substituted in this wiring */
public boolean isSubstitutedPackage(String packageName) { return substitutedPkgNames.contains(packageName); }
Returns an unmodifiable collection of package names for package capabilities that have been substituted.
Returns:the substituted package names
/** * Returns an unmodifiable collection of package names for * package capabilities that have been substituted. * @return the substituted package names */
public Collection<String> getSubstitutedNames() { return Collections.unmodifiableCollection(substitutedPkgNames); } private boolean hasResourcePermission() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { sm.checkPermission(new AdminPermission(getBundle(), AdminPermission.RESOURCE)); } catch (SecurityException e) { return false; } } return true; }
Adds the requirements from the specified builder to this wiring. The new requirements must be in the PackageNamespace. These requirements are transient and will not exist when loading up persistent wirings.
Params:
  • builder – the builder that defines the new dynamic imports.
/** * Adds the {@link ModuleRevisionBuilder#getRequirements() requirements} from * the specified builder to this wiring. The new requirements must be in the * {@link PackageNamespace}. These requirements are transient * and will not exist when loading up persistent wirings. * @param builder the builder that defines the new dynamic imports. */
public void addDynamicImports(ModuleRevisionBuilder builder) { List<GenericInfo> newImports = builder.getRequirements(); List<ModuleRequirement> newRequirements = new ArrayList<>(); for (GenericInfo info : newImports) { if (!PackageNamespace.PACKAGE_NAMESPACE.equals(info.getNamespace())) { throw new IllegalArgumentException("Invalid namespace for package imports: " + info.getNamespace()); //$NON-NLS-1$ } Map<String, Object> attributes = new HashMap<>(info.getAttributes()); Map<String, String> directives = new HashMap<>(info.getDirectives()); directives.put(DYNAMICALLY_ADDED_IMPORT_DIRECTIVE, "true"); //$NON-NLS-1$ directives.put(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE, PackageNamespace.RESOLUTION_DYNAMIC); newRequirements.add(new ModuleRequirement(info.getNamespace(), directives, attributes, revision)); } ModuleDatabase moduleDatabase = revision.getRevisions().getContainer().moduleDatabase; moduleDatabase.writeLock(); try { List<ModuleRequirement> updatedRequirements = new ArrayList<>(requirements); updatedRequirements.addAll(newRequirements); requirements = updatedRequirements; } finally { moduleDatabase.writeUnlock(); } } void addDynamicPackageMiss(String packageName) { Set<String> misses = dynamicMissRef.get(); if (misses == null) { dynamicMissRef.compareAndSet(null, Collections.synchronizedSet(new HashSet<String>())); misses = dynamicMissRef.get(); } misses.add(packageName); } boolean isDynamicPackageMiss(String packageName) { Set<String> misses = dynamicMissRef.get(); return misses != null && misses.contains(packageName); } void removeDynamicPackageMisses(Collection<String> packageNames) { Set<String> misses = dynamicMissRef.get(); if (misses != null) { misses.removeAll(packageNames); } } @Override public String toString() { return revision.toString(); } List<Wire> getSubstitutionWires() { if (substitutedPkgNames.isEmpty()) { return Collections.emptyList(); } // Could cache this, but seems unnecessary since it will only be used by the resolver List<Wire> substitutionWires = new ArrayList<>(substitutedPkgNames.size()); List<ModuleWire> current = requiredWires; for (ModuleWire wire : current) { Capability cap = wire.getCapability(); if (PackageNamespace.PACKAGE_NAMESPACE.equals(cap.getNamespace())) { if (substitutedPkgNames.contains(cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) { substitutionWires.add(wire); } } } return substitutionWires; } }