org.eclipse.osgi.container.ModuleWiring Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2012, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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