blob: c6d03ad38b99cf7e0cd7520867cdc80a328f9690 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 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.internal.framework.legacy;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleCapability;
import org.eclipse.osgi.container.ModuleContainer;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleWire;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.internal.container.Capabilities;
import org.eclipse.osgi.internal.container.InternalUtils;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.framework.VersionRange;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.packageadmin.RequiredBundle;
@Deprecated
public class PackageAdminImpl implements PackageAdmin {
private final ModuleContainer container;
/*
* We need to make sure that the GetBundleAction class loads early to prevent a ClassCircularityError when checking permissions.
* See bug 161561
*/
static {
Class<?> c;
c = GetBundleAction.class;
c.getName(); // to prevent compiler warnings
}
static class GetBundleAction implements PrivilegedAction<Bundle> {
private Class<?> clazz;
private PackageAdminImpl impl;
public GetBundleAction(PackageAdminImpl impl, Class<?> clazz) {
this.impl = impl;
this.clazz = clazz;
}
public Bundle run() {
return impl.getBundlePriv(clazz);
}
}
/**
* Constructor.
*
* @param container the container
*/
public PackageAdminImpl(ModuleContainer container) {
this.container = container;
}
public ExportedPackage[] getExportedPackages(Bundle bundle) {
if (bundle == null) {
return getExportedPackages((String) null);
}
Module module = StartLevelImpl.getModule(bundle);
Collection<ModuleRevision> revisions = module == null ? Collections.<ModuleRevision> emptyList() : module.getRevisions().getModuleRevisions();
Collection<ExportedPackage> allExports = new ArrayList<>();
for (ModuleRevision revision : revisions) {
ModuleWiring wiring = revision.getWiring();
if (wiring != null) {
List<ModuleCapability> providedPackages = wiring.getModuleCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
if (providedPackages != null) {
for (ModuleCapability providedPackage : providedPackages) {
allExports.add(new ExportedPackageImpl(providedPackage, wiring));
}
}
}
}
return allExports.isEmpty() ? null : allExports.toArray(new ExportedPackage[allExports.size()]);
}
public ExportedPackage getExportedPackage(String name) {
ExportedPackage[] allExports = getExportedPackages(name);
if (allExports == null)
return null;
ExportedPackage result = null;
for (int i = 0; i < allExports.length; i++) {
if (name.equals(allExports[i].getName())) {
if (result == null) {
result = allExports[i];
} else {
Version curVersion = result.getVersion();
Version newVersion = allExports[i].getVersion();
if (newVersion.compareTo(curVersion) >= 0)
result = allExports[i];
}
}
}
return result;
}
public ExportedPackage[] getExportedPackages(String name) {
String filter = "(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + (name == null ? "*" : name) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
Map<String, String> directives = Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
Map<String, Boolean> attributes = Collections.singletonMap(Capabilities.SYNTHETIC_REQUIREMENT, Boolean.TRUE);
Requirement packageReq = ModuleContainer.createRequirement(PackageNamespace.PACKAGE_NAMESPACE, directives, attributes);
Collection<BundleCapability> packageCaps = container.getFrameworkWiring().findProviders(packageReq);
InternalUtils.filterCapabilityPermissions(packageCaps);
List<ExportedPackage> result = new ArrayList<>();
for (BundleCapability capability : packageCaps) {
ModuleWiring wiring = (ModuleWiring) capability.getRevision().getWiring();
if (wiring != null) {
Collection<ModuleWiring> wirings = Collections.emptyList();
if ((capability.getRevision().getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
// This is a fragment, just get all the host wirings
List<ModuleWire> hostWires = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
if (hostWires != null && !hostWires.isEmpty()) {
wirings = new ArrayList<>(hostWires.size());
for (ModuleWire hostWire : hostWires) {
ModuleWiring hostWiring = hostWire.getProviderWiring();
if (hostWiring != null) {
wirings.add(hostWiring);
}
}
}
} else {
// just a single host wiring
wirings = Collections.singletonList(wiring);
}
for (ModuleWiring moduleWiring : wirings) {
if (!moduleWiring.getSubstitutedNames().contains(capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
result.add(new ExportedPackageImpl((ModuleCapability) capability, moduleWiring));
}
}
}
}
return (result.size() == 0 ? null : result.toArray(new ExportedPackage[result.size()]));
}
public void refreshPackages(Bundle[] input) {
container.getFrameworkWiring().refreshBundles(input == null ? null : Arrays.asList(input));
}
public boolean resolveBundles(Bundle[] input) {
return container.getFrameworkWiring().resolveBundles(input == null ? null : Arrays.asList(input));
}
public RequiredBundle[] getRequiredBundles(String symbolicName) {
String filter = "(" + BundleNamespace.BUNDLE_NAMESPACE + "=" + (symbolicName == null ? "*" : symbolicName) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
Map<String, String> directives = Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
Map<String, Boolean> attributes = Collections.singletonMap(Capabilities.SYNTHETIC_REQUIREMENT, Boolean.TRUE);
Requirement bundleReq = ModuleContainer.createRequirement(BundleNamespace.BUNDLE_NAMESPACE, directives, attributes);
Collection<BundleCapability> bundleCaps = container.getFrameworkWiring().findProviders(bundleReq);
InternalUtils.filterCapabilityPermissions(bundleCaps);
Collection<RequiredBundle> result = new ArrayList<>();
for (BundleCapability capability : bundleCaps) {
BundleWiring wiring = capability.getRevision().getWiring();
if (wiring != null) {
result.add(new RequiredBundleImpl(capability, wiring));
}
}
return result.isEmpty() ? null : result.toArray(new RequiredBundle[result.size()]);
}
public Bundle[] getBundles(String symbolicName, String versionRange) {
if (symbolicName == null) {
throw new IllegalArgumentException();
}
if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) {
// need to alias system.bundle to the implementation BSN
symbolicName = EquinoxContainer.NAME;
}
VersionRange range = versionRange == null ? null : new VersionRange(versionRange);
String filter = (range != null ? "(&" : "") + "(" + IdentityNamespace.IDENTITY_NAMESPACE + "=" + symbolicName + ")" + (range != null ? range.toFilterString(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE) + ")" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
Requirement identityReq = ModuleContainer.createRequirement(IdentityNamespace.IDENTITY_NAMESPACE, Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter), Collections.<String, Object> emptyMap());
Collection<BundleCapability> identityCaps = container.getFrameworkWiring().findProviders(identityReq);
if (identityCaps.isEmpty()) {
return null;
}
List<Bundle> sorted = new ArrayList<>(identityCaps.size());
for (BundleCapability capability : identityCaps) {
Bundle b = capability.getRevision().getBundle();
// a sanity check incase this is an old revision
if (symbolicName.equals(b.getSymbolicName()) && !sorted.contains(b)) {
sorted.add(b);
}
}
Collections.sort(sorted, new Comparator<Bundle>() {
@Override
public int compare(Bundle b1, Bundle b2) {
return b2.getVersion().compareTo(b1.getVersion());
}
});
if (sorted.isEmpty()) {
return null;
}
return sorted.toArray(new Bundle[sorted.size()]);
}
public Bundle[] getFragments(Bundle bundle) {
ModuleWiring wiring = getWiring(bundle);
if (wiring == null) {
return null;
}
List<ModuleWire> hostWires = wiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE);
if (hostWires == null) {
// we don't hold locks while checking the graph, just return if no longer valid
return null;
}
Collection<Bundle> fragments = new ArrayList<>(hostWires.size());
for (ModuleWire wire : hostWires) {
Bundle fragment = wire.getRequirer().getBundle();
if (fragment != null) {
fragments.add(fragment);
}
}
return fragments.isEmpty() ? null : fragments.toArray(new Bundle[fragments.size()]);
}
public Bundle[] getHosts(Bundle bundle) {
ModuleWiring wiring = getWiring(bundle);
if (wiring == null) {
return null;
}
List<ModuleWire> hostWires = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
if (hostWires == null) {
// we don't hold locks while checking the graph, just return if no longer valid
return null;
}
Collection<Bundle> hosts = new ArrayList<>(hostWires.size());
for (ModuleWire wire : hostWires) {
Bundle host = wire.getProvider().getBundle();
if (host != null) {
hosts.add(host);
}
}
return hosts.isEmpty() ? null : hosts.toArray(new Bundle[hosts.size()]);
}
private ModuleWiring getWiring(Bundle bundle) {
Module module = StartLevelImpl.getModule(bundle);
if (module == null) {
return null;
}
List<ModuleRevision> revisions = module.getRevisions().getModuleRevisions();
if (revisions.isEmpty()) {
return null;
}
return revisions.get(0).getWiring();
}
Bundle getBundlePriv(Class<?> clazz) {
ClassLoader cl = clazz.getClassLoader();
if (cl instanceof BundleReference) {
return ((BundleReference) cl).getBundle();
}
if (cl == getClass().getClassLoader()) {
return container.getModule(0).getBundle();
}
return null;
}
public Bundle getBundle(final Class<?> clazz) {
if (System.getSecurityManager() == null)
return getBundlePriv(clazz);
return AccessController.doPrivileged(new GetBundleAction(this, clazz));
}
public int getBundleType(Bundle bundle) {
Module module = StartLevelImpl.getModule(bundle);
if (module == null) {
return 0;
}
List<BundleRevision> revisions = module.getRevisions().getRevisions();
if (revisions.isEmpty()) {
return 0;
}
return (revisions.get(0).getTypes() & BundleRevision.TYPE_FRAGMENT) != 0 ? PackageAdmin.BUNDLE_TYPE_FRAGMENT : 0;
}
public Collection<Bundle> getRemovalPendingBundles() {
return container.getFrameworkWiring().getRemovalPendingBundles();
}
public Collection<Bundle> getDependencyClosure(Collection<Bundle> bundles) {
return container.getFrameworkWiring().getDependencyClosure(bundles);
}
static class ExportedPackageImpl implements ExportedPackage {
private final ModuleCapability packageCapability;
private final ModuleWiring providerWiring;
public ExportedPackageImpl(ModuleCapability packageCapability, ModuleWiring providerWiring) {
this.packageCapability = packageCapability;
this.providerWiring = providerWiring;
}
public String getName() {
return (String) packageCapability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
}
public Bundle getExportingBundle() {
if (!providerWiring.isInUse())
return null;
return providerWiring.getBundle();
}
public Bundle[] getImportingBundles() {
if (!providerWiring.isInUse()) {
return null;
}
Set<Bundle> importing = new HashSet<>();
String packageName = getName();
addRequirers(importing, providerWiring, packageName);
List<ModuleWire> providedPackages = providerWiring.getProvidedModuleWires(PackageNamespace.PACKAGE_NAMESPACE);
if (providedPackages == null) {
// we don't hold locks while checking the graph, just return if no longer valid
return null;
}
for (ModuleWire packageWire : providedPackages) {
if (packageCapability.equals(packageWire.getCapability())) {
importing.add(packageWire.getRequirer().getBundle());
if (packageWire.getRequirerWiring().isSubstitutedPackage(packageName)) {
addRequirers(importing, packageWire.getRequirerWiring(), packageName);
}
}
}
return importing.toArray(new Bundle[importing.size()]);
}
private static void addRequirers(Set<Bundle> importing, ModuleWiring wiring, String packageName) {
List<ModuleWire> requirerWires = wiring.getProvidedModuleWires(BundleNamespace.BUNDLE_NAMESPACE);
if (requirerWires == null) {
// we don't hold locks while checking the graph, just return if no longer isInUse
return;
}
for (ModuleWire requireBundleWire : requirerWires) {
Bundle requirer = requireBundleWire.getRequirer().getBundle();
if (importing.contains(requirer)) {
continue;
}
importing.add(requirer);
// if reexported then need to add any requirers of the reexporter
String reExport = requireBundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
ModuleWiring requirerWiring = requireBundleWire.getRequirerWiring();
if (BundleNamespace.VISIBILITY_REEXPORT.equals(reExport)) {
addRequirers(importing, requirerWiring, packageName);
}
// also need to add any importers of the same package as the wiring exports; case of aggregations
if (!requirerWiring.equals(wiring)) {
List<ModuleWire> providedPackages = requirerWiring.getProvidedModuleWires(PackageNamespace.PACKAGE_NAMESPACE);
if (providedPackages != null) {
for (ModuleWire packageWire : providedPackages) {
if (packageName.equals(packageWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
importing.add(packageWire.getRequirer().getBundle());
if (packageWire.getRequirerWiring().isSubstitutedPackage(packageName)) {
addRequirers(importing, packageWire.getRequirerWiring(), packageName);
}
}
}
}
}
}
}
/**
* @deprecated
*/
public String getSpecificationVersion() {
return getVersion().toString();
}
public Version getVersion() {
Version version = (Version) packageCapability.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
return version == null ? Version.emptyVersion : version;
}
public boolean isRemovalPending() {
return !providerWiring.isCurrent();
}
public String toString() {
return packageCapability.toString();
}
}
private static class RequiredBundleImpl implements RequiredBundle {
private final BundleCapability bundleCapability;
private final BundleWiring providerWiring;
public RequiredBundleImpl(BundleCapability bundleCapability, BundleWiring providerWiring) {
this.bundleCapability = bundleCapability;
this.providerWiring = providerWiring;
}
@Override
public String getSymbolicName() {
return (String) bundleCapability.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE);
}
@Override
public Bundle getBundle() {
if (!providerWiring.isInUse())
return null;
return providerWiring.getBundle();
}
@Override
public Bundle[] getRequiringBundles() {
if (!providerWiring.isInUse()) {
return null;
}
Set<Bundle> requiring = new HashSet<>();
addRequirers(requiring, providerWiring);
return requiring.toArray(new Bundle[requiring.size()]);
}
private static void addRequirers(Set<Bundle> requiring, BundleWiring providerWiring) {
List<BundleWire> requirerWires = providerWiring.getProvidedWires(BundleNamespace.BUNDLE_NAMESPACE);
if (requirerWires == null) {
// we don't hold locks while checking the graph, just return if no longer isInUse
return;
}
for (BundleWire requireBundleWire : requirerWires) {
Bundle requirer = requireBundleWire.getRequirer().getBundle();
if (requiring.contains(requirer)) {
continue;
}
requiring.add(requirer);
String reExport = requireBundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
if (BundleNamespace.VISIBILITY_REEXPORT.equals(reExport)) {
addRequirers(requiring, requireBundleWire.getRequirerWiring());
}
}
}
@Override
public Version getVersion() {
Version version = (Version) bundleCapability.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
return version == null ? Version.emptyVersion : version;
}
public boolean isRemovalPending() {
return !providerWiring.isCurrent();
}
public String toString() {
return bundleCapability.toString();
}
}
}