org.eclipse.osgi.container.ModuleWiring Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/*******************************************************************************
* 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 {@link BundleWiring}.
* @since 3.10
*/
public final class ModuleWiring implements BundleWiring {
class LoaderInitializer implements Callable {
@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 capabilities;
private volatile List requirements;
private final Collection substitutedPkgNames;
private final AtomicLazyInitializer loader = new AtomicLazyInitializer<>();
private final LoaderInitializer loaderInitializer = new LoaderInitializer();
private volatile List providedWires;
private volatile List requiredWires;
volatile boolean isValid = true;
private final AtomicReference> dynamicMissRef = new AtomicReference<>();
ModuleWiring(ModuleRevision revision, List capabilities, List requirements, List providedWires, List requiredWires, Collection substitutedPkgNames) {
super();
this.revision = revision;
this.capabilities = capabilities;
this.requirements = requirements;
this.providedWires = providedWires;
this.requiredWires = requiredWires;
this.substitutedPkgNames = substitutedPkgNames.isEmpty() ? Collections. 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 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 {@link #getCapabilities(String)} except
* uses type ModuleCapability.
* @param namespace the namespace
* @return the capabilities
* @see #getCapabilities(String)
*/
public List getModuleCapabilities(String namespace) {
return getModuleCapabilities(namespace, capabilities);
}
private List getModuleCapabilities(String namespace, List allCapabilities) {
if (!isValid)
return null;
if (namespace == null)
return new ArrayList<>(allCapabilities);
List result = new ArrayList<>();
for (ModuleCapability capability : allCapabilities) {
if (namespace.equals(capability.getNamespace())) {
result.add(capability);
}
}
return result;
}
/**
* Returns the same result as {@link #getRequirements(String)} except
* uses type ModuleRequirement.
* @param namespace the namespace
* @return the requirements
* @see #getRequirements(String)
*/
public List getModuleRequirements(String namespace) {
return getModuleRequirements(namespace, requirements);
}
List getPersistentRequirements() {
List persistentRequriements = getModuleRequirements(null);
if (persistentRequriements == null) {
return null;
}
for (Iterator 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 getModuleRequirements(String namespace, List allRequirements) {
if (!isValid)
return null;
if (namespace == null)
return new ArrayList<>(allRequirements);
List result = new ArrayList<>();
for (ModuleRequirement requirement : allRequirements) {
if (namespace.equals(requirement.getNamespace())) {
result.add(requirement);
}
}
return result;
}
@Override
public List getCapabilities(String namespace) {
return InternalUtils.asListBundleCapability(getModuleCapabilities(namespace));
}
@Override
public List getRequirements(String namespace) {
return InternalUtils.asListBundleRequirement(getModuleRequirements(namespace));
}
/**
* Returns the same result as {@link #getProvidedWires(String)} except
* uses type ModuleWire.
* @param namespace the namespace
* @return the wires
* @see #getProvidedWires(String)
*/
public List getProvidedModuleWires(String namespace) {
return getWires(namespace, providedWires);
}
List getPersistentProvidedWires() {
return getPersistentWires(providedWires);
}
/**
* Returns the same result as {@link #getRequiredWires(String)} except
* uses type ModuleWire.
* @param namespace the namespace
* @return the wires
* @see #getRequiredWires(String)
*/
public List getRequiredModuleWires(String namespace) {
return getWires(namespace, requiredWires);
}
List getPersistentRequiredWires() {
return getPersistentWires(requiredWires);
}
private List getPersistentWires(List allWires) {
List persistentWires = getWires(null, allWires);
if (persistentWires == null) {
return null;
}
for (Iterator 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 getProvidedWires(String namespace) {
return InternalUtils.asListBundleWire(getWires(namespace, providedWires));
}
@Override
public List getRequiredWires(String namespace) {
return InternalUtils.asListBundleWire(getWires(namespace, requiredWires));
}
private List getWires(String namespace, List allWires) {
if (!isValid)
return null;
if (namespace == null)
return new ArrayList<>(allWires);
List 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
* @return the module loader for this wiring.
*/
public ModuleLoader getModuleLoader() {
return loader.getInitialized(loaderInitializer);
}
void loadFragments(Collection fragments) {
ModuleLoader current = loader.get();
if (current != null) {
current.loadFragments(fragments);
}
}
@Override
public List 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 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 getResourceCapabilities(String namespace) {
return InternalUtils.asListCapability(getCapabilities(namespace));
}
@Override
public List getResourceRequirements(String namespace) {
return InternalUtils.asListRequirement(getRequirements(namespace));
}
@Override
public List getProvidedResourceWires(String namespace) {
return InternalUtils.asListWire(getWires(namespace, providedWires));
}
@Override
public List getRequiredResourceWires(String namespace) {
return InternalUtils.asListWire(getWires(namespace, requiredWires));
}
@Override
public ModuleRevision getResource() {
return revision;
}
void setProvidedWires(List providedWires) {
this.providedWires = providedWires;
}
void setRequiredWires(List requiredWires) {
this.requiredWires = requiredWires;
}
void setCapabilities(List 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
* @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.
* @return the substituted package names
*/
public Collection 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 {@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 newImports = builder.getRequirements();
List 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 attributes = new HashMap<>(info.getAttributes());
Map 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 updatedRequirements = new ArrayList<>(requirements);
updatedRequirements.addAll(newRequirements);
requirements = updatedRequirements;
} finally {
moduleDatabase.writeUnlock();
}
}
void addDynamicPackageMiss(String packageName) {
Set misses = dynamicMissRef.get();
if (misses == null) {
dynamicMissRef.compareAndSet(null, Collections.synchronizedSet(new HashSet()));
misses = dynamicMissRef.get();
}
misses.add(packageName);
}
boolean isDynamicPackageMiss(String packageName) {
Set misses = dynamicMissRef.get();
return misses != null && misses.contains(packageName);
}
void removeDynamicPackageMisses(Collection packageNames) {
Set misses = dynamicMissRef.get();
if (misses != null) {
misses.removeAll(packageNames);
}
}
@Override
public String toString() {
return revision.toString();
}
List getSubstitutionWires() {
if (substitutedPkgNames.isEmpty()) {
return Collections.emptyList();
}
// Could cache this, but seems unnecessary since it will only be used by the resolver
List substitutionWires = new ArrayList<>(substitutedPkgNames.size());
List 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;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy