All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.osgi.container.ModuleWiring Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2012, 2021 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 static org.eclipse.osgi.internal.container.InternalUtils.asCopy;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.NamespaceList;
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.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;

/**
 * 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 NamespaceList capabilities;
	private volatile NamespaceList requirements;
	private final Collection substitutedPkgNames;
	private final AtomicLazyInitializer loader = new AtomicLazyInitializer<>();
	private final LoaderInitializer loaderInitializer = new LoaderInitializer();
	private volatile NamespaceList providedWires;
	private volatile NamespaceList requiredWires;
	volatile boolean isValid = true;
	private final AtomicReference> dynamicMissRef = new AtomicReference<>();

	ModuleWiring(ModuleRevision revision, NamespaceList capabilities,
			NamespaceList requirements, NamespaceList providedWires,
			NamespaceList 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 and the returned list is unmodifiable.
	 * 
	 * @param namespace the namespace
	 * @return the capabilities
	 * @see #getCapabilities(String)
	 */
	public List getModuleCapabilities(String namespace) {
		if (!isValid) {
			return null;
		}
		return capabilities.getList(namespace);
	}

	/**
	 * Returns the same result as {@link #getRequirements(String)} except uses type
	 * ModuleRequirement and the returned list is unmodifiable.
	 * 
	 * @param namespace the namespace
	 * @return the requirements
	 * @see #getRequirements(String)
	 */
	public List getModuleRequirements(String namespace) {
		if (!isValid) {
			return null;
		}
		return requirements.getList(namespace);
	}

	List getPersistentRequirements() {
		if (!isValid) {
			return null;
		}
		List persistentRequriements = new ArrayList<>(requirements.getList(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;
	}

	@Override
	public List getCapabilities(String namespace) {
		return asCopy(getModuleCapabilities(namespace));

	}

	@Override
	public List getRequirements(String namespace) {
		return asCopy(getModuleRequirements(namespace));
	}

	/**
	 * Returns the same result as {@link #getProvidedWires(String)} except uses type
	 * ModuleWire and the returned list is unmodifiable.
	 * 
	 * @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 and the returned list is unmodifiable.
	 * 
	 * @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(NamespaceList allWires) {
		if (!isValid) {
			return null;
		}
		List persistentWires = new ArrayList<>(allWires.getList(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 asCopy(getWires(namespace, providedWires));
	}

	@Override
	public List getRequiredWires(String namespace) {
		return asCopy(getWires(namespace, requiredWires));
	}

	private List getWires(String namespace, NamespaceList wires) {
		if (!isValid) {
			return null;
		}
		return wires.getList(namespace);
	}

	@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 asCopy(getModuleCapabilities(namespace));
	}

	@Override
	public List getResourceRequirements(String namespace) {
		return asCopy(getModuleRequirements(namespace));
	}

	@Override
	public List getProvidedResourceWires(String namespace) {
		return asCopy(getWires(namespace, providedWires));
	}

	@Override
	public List getRequiredResourceWires(String namespace) {
		return asCopy(getWires(namespace, requiredWires));
	}

	@Override
	public ModuleRevision getResource() {
		return revision;
	}

	void setProvidedWires(NamespaceList providedWires) {
		this.providedWires = providedWires;
	}

	void setRequiredWires(NamespaceList requiredWires) {
		this.requiredWires = requiredWires;
	}

	void setCapabilities(NamespaceList capabilities) {
		this.capabilities = capabilities;
	}

	void setRequirements(NamespaceList requirements) {
		this.requirements = requirements;
	}

	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) {
		NamespaceList.Builder newImports = builder.getRequirementsBuilder();
		NamespaceList.Builder newRequirements = newImports.transformIntoCopy(info -> {
			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);
			return new ModuleRequirement(info.getNamespace(), directives, attributes, revision);
		}, NamespaceList.REQUIREMENT);

		ModuleDatabase moduleDatabase = revision.getRevisions().getContainer().moduleDatabase;
		// Use the writeLockOperation to atomically update the revision timestamps
		// This is necessary to make sure any in flight resolve operations are using the
		// latest wiring data and avoids them overwriting the requirements incorrectly.
		moduleDatabase.writeLockOperation(true, () -> {
			NamespaceList.Builder requirmentsBuilder = requirements.createBuilder();
			requirmentsBuilder.addAll(newRequirements);
			requirements = requirmentsBuilder.build();
			// clear out miss cache when adding new dynamic imports.
			dynamicMissRef.updateAndGet((s) -> {
				if (s != null) {
					s.clear();
				}
				return s;
			});
		});
	}

	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.getList(PackageNamespace.PACKAGE_NAMESPACE);
		for (ModuleWire wire : current) {
			Capability cap = wire.getCapability();
			if (substitutedPkgNames.contains(cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
				substitutionWires.add(wire);
			}
		}
		return substitutionWires;
	}

	NamespaceList getCapabilities() {
		return capabilities;
	}

	NamespaceList getProvidedWires() {
		return providedWires;
	}

	NamespaceList getRequirements() {
		return requirements;
	}

	NamespaceList getRequiredWires() {
		return requiredWires;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy