/*******************************************************************************
* Copyright (c) 2014 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
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Jan S. Rellermeyer, IBM Research - initial API and implementation
*******************************************************************************/
package org.eclipse.concierge.compat.packageadmin;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleReference;
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.PackageNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleRevisions;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.resource.Capability;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.packageadmin.RequiredBundle;
@SuppressWarnings("deprecation")
final class PackageAdminImpl implements PackageAdmin {
private final BundleContext context;
PackageAdminImpl(final BundleContext context) {
this.context = context;
}
/**
* (non-Javadoc)
*
* @see org.osgi.service.packageadmin.PackageAdmin#getExportedPackages(org.osgi.framework.Bundle)
*/
public ExportedPackage[] getExportedPackages(final Bundle bundle) {
if (bundle == null) {
return getExportedPackages((String) null);
}
final ArrayList<ExportedPackage> result = new ArrayList<ExportedPackage>();
getExportedPackages0(bundle, null, result);
return toArrayOrNull(result, ExportedPackage.class);
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getExportedPackages(java.lang.String)
*/
public ExportedPackage[] getExportedPackages(final String name) {
final Bundle[] bundles = context.getBundles();
final ArrayList<ExportedPackage> result = new ArrayList<ExportedPackage>();
for (final Bundle bundle : bundles) {
getExportedPackages0(bundle, name, result);
}
return toArrayOrNull(result, ExportedPackage.class);
}
private void getExportedPackages0(final Bundle bundle, final String name, final ArrayList<ExportedPackage> result) {
if (bundle == null) {
throw new IllegalArgumentException("bundle==null");
}
if (result == null) {
throw new IllegalArgumentException("result==null");
}
final List<BundleRevision> revs = bundle.adapt(BundleRevisions.class).getRevisions();
if (revs.isEmpty()) {
return;
}
for (final BundleRevision r : revs) {
final BundleWiring wiring = r.getWiring();
if (wiring != null && wiring.isInUse()) {
for (final Capability cap : wiring.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
if (name == null || name.equals(cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
result.add(new ExportedPackageImpl((BundleCapability) cap));
}
}
}
}
}
protected static final Comparator<ExportedPackage> EXPORT_ORDER = new Comparator<ExportedPackage>() {
// reverts the order so that we can
// retrieve the 0st item to get the best
// match
public int compare(final ExportedPackage c1, final ExportedPackage c2) {
final BundleCapability cap1 = ((ExportedPackageImpl) c1).cap;
final BundleCapability cap2 = ((ExportedPackageImpl) c2).cap;
final int cap1Resolved = cap1.getResource().getWiring() == null ? 0 : 1;
final int cap2Resolved = cap2.getResource().getWiring() == null ? 0 : 1;
int score = cap2Resolved - cap1Resolved;
if (score != 0) {
return score;
}
final BundleWiring wiring1 = cap1.getResource().getWiring();
if (wiring1 != null) {
final List<BundleWire> wires = wiring1.getProvidedWires(PackageNamespace.PACKAGE_NAMESPACE);
for (final BundleWire wire : wires) {
if (wire.getCapability().equals(cap1)) {
score = -1;
break;
}
}
}
final BundleWiring wiring2 = cap2.getResource().getWiring();
if (wiring2 != null) {
final List<BundleWire> wires = wiring2.getProvidedWires(PackageNamespace.PACKAGE_NAMESPACE);
for (final BundleWire wire : wires) {
score = 1;
if (wire.getCapability().equals(cap2)) {
score++;
break;
}
}
}
if (score != 0) {
return score;
}
Version cap1Version = (Version) cap1.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
Version cap2Version = (Version) cap2.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
if (cap1Version == null) {
cap1Version = Version.emptyVersion;
}
if (cap2Version == null) {
cap2Version = Version.emptyVersion;
}
score = cap2Version.compareTo(cap1Version);
if (score != 0) {
return score;
}
final long cap1BundleId = cap1.getRevision().getBundle().getBundleId();
final long cap2BundleId = cap2.getRevision().getBundle().getBundleId();
return (int) (cap1BundleId - cap2BundleId);
}
};
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getExportedPackage(java.lang.String)
*/
public ExportedPackage getExportedPackage(final String name) {
final Bundle[] bundles = context.getBundles();
final ArrayList<ExportedPackage> result = new ArrayList<ExportedPackage>();
for (final Bundle bundle : bundles) {
getExportedPackages0(bundle, name, result);
}
if (result.isEmpty()) {
return null;
}
Collections.sort(result, EXPORT_ORDER);
System.err.println("MY ORDER IS " + result);
for (final ExportedPackage p : result) {
System.err.println(p + " exporter " + p.getExportingBundle());
}
return result.get(0);
}
private FrameworkWiring getFrameworkWiring() {
return context.getBundle(0).adapt(FrameworkWiring.class);
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#refreshPackages(org.osgi.framework.Bundle[])
*/
public void refreshPackages(final Bundle[] bundles) {
final FrameworkWiring wiring = getFrameworkWiring();
wiring.refreshBundles(bundles == null ? null : Arrays.asList(bundles));
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#resolveBundles(org.osgi.framework.Bundle[])
*/
public boolean resolveBundles(final Bundle[] bundles) {
final FrameworkWiring wiring = getFrameworkWiring();
return wiring.resolveBundles(bundles == null ? null : Arrays.asList(bundles));
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getRequiredBundles(java.lang.String)
*/
public RequiredBundle[] getRequiredBundles(final String symbolicName) {
final Bundle[] bundles = context.getBundles();
final ArrayList<RequiredBundle> result = new ArrayList<RequiredBundle>();
for (final Bundle bundle : bundles) {
if (bundle.getState() == Bundle.INSTALLED || bundle.getState() == Bundle.UNINSTALLED) {
continue;
}
final BundleRevision rev = bundle.adapt(BundleRevision.class);
if (isFragment(rev)) {
continue;
}
if (symbolicName == null || symbolicName.equals(rev.getSymbolicName())) {
result.add(new RequiredBundleImpl(rev));
}
}
return toArrayOrNull(result, RequiredBundle.class);
}
private void addRequiringBundles(final BundleWiring wiring, final ArrayList<Bundle> result) {
final List<BundleWire> wires = wiring.getProvidedWires(BundleNamespace.BUNDLE_NAMESPACE);
for (final BundleWire wire : wires) {
result.add(wire.getRequirer().getBundle());
if (BundleNamespace.VISIBILITY_REEXPORT.equals(
wire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE))) {
addRequiringBundles(wire.getRequirer().getWiring(), result);
}
}
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getBundles(java.lang.String,
* java.lang.String)
*/
public Bundle[] getBundles(final String symbolicName, final String versionRange) {
if (symbolicName == null) {
throw new IllegalArgumentException("symbolicName is null");
}
final VersionRange range = versionRange == null ? null : new VersionRange(versionRange);
final Bundle[] bundles = context.getBundles();
final ArrayList<Bundle> result = new ArrayList<Bundle>();
for (final Bundle bundle : bundles) {
if (symbolicName.equals(bundle.getSymbolicName())) {
if (range == null || range.includes(bundle.getVersion())) {
result.add(bundle);
}
}
}
if (result.isEmpty()) {
return null;
}
Collections.sort(result, new Comparator<Bundle>() {
public int compare(final Bundle b1, final Bundle b2) {
return b2.getVersion().compareTo(b1.getVersion());
}
});
return result.toArray(new Bundle[result.size()]);
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getFragments(org.osgi.framework.Bundle)
*/
public Bundle[] getFragments(final Bundle bundle) {
final BundleWiring wiring = bundle.adapt(BundleWiring.class);
// this will happen if a bundle has no current revision, e.g.
// is INSTALLED only
if (wiring == null) {
// System.err.println("o.e.c.service.packageadmin: getFragments has
// no wiring for bundle "
// + bundle.getSymbolicName());
return null;
}
final List<BundleWire> wires = wiring.getProvidedWires(HostNamespace.HOST_NAMESPACE);
if (wires == null || wires.isEmpty()) {
return null;
}
final Bundle[] result = new Bundle[wires.size()];
final Iterator<BundleWire> iter = wires.iterator();
for (int i = 0; i < result.length; i++) {
final BundleWire wire = iter.next();
result[i] = wire.getRequirer().getBundle();
}
return result;
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getHosts(org.osgi.framework.Bundle)
*/
public Bundle[] getHosts(final Bundle bundle) {
final BundleWiring wiring = bundle.adapt(BundleWiring.class);
if (wiring == null) {
return null;
}
final List<BundleWire> wires = wiring.getRequiredWires(HostNamespace.HOST_NAMESPACE);
final ArrayList<Bundle> result = new ArrayList<Bundle>();
for (final BundleWire wire : wires) {
if (wire.getRequirer().getBundle() == bundle) {
result.add(wire.getProvider().getBundle());
}
}
return toArrayOrNull(result, Bundle.class);
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getBundle(java.lang.Class)
*/
public Bundle getBundle(@SuppressWarnings("rawtypes") final Class clazz) {
final ClassLoader cl = clazz.getClassLoader();
if (cl instanceof BundleReference) {
return ((BundleReference) cl).getBundle();
}
return null;
}
/**
* @see org.osgi.service.packageadmin.PackageAdmin#getBundleType(org.osgi.framework.Bundle)
*/
public int getBundleType(final Bundle bundle) {
final BundleRevision rev = bundle.adapt(BundleRevision.class);
return isFragment(rev) ? BUNDLE_TYPE_FRAGMENT : 0;
}
private boolean isFragment(final BundleRevision rev) {
return !rev.getRequirements(HostNamespace.HOST_NAMESPACE).isEmpty();
}
@SuppressWarnings("unchecked")
private <T> T[] toArrayOrNull(final List<T> l, final Class<T> t) {
if (l == null || l.isEmpty()) {
return null;
}
return l.toArray((T[]) Array.newInstance(t, l.size()));
}
private class ExportedPackageImpl implements ExportedPackage {
final BundleCapability cap;
ExportedPackageImpl(final BundleCapability cap) {
this.cap = cap;
}
/**
* @see org.osgi.service.packageadmin.ExportedPackage#getName()
*/
public String getName() {
return (String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
}
/**
* @see org.osgi.service.packageadmin.ExportedPackage#getExportingBundle()
*/
public Bundle getExportingBundle() {
return cap.getResource().getBundle();
}
/**
* @see org.osgi.service.packageadmin.ExportedPackage#getImportingBundles()
*/
public Bundle[] getImportingBundles() {
final ArrayList<Bundle> result = new ArrayList<Bundle>();
final BundleWiring wiring = cap.getResource().getWiring();
if (wiring != null) {
final List<BundleWire> wires = wiring.getProvidedWires(PackageNamespace.PACKAGE_NAMESPACE);
for (final BundleWire wire : wires) {
if (wire.getCapability().equals(cap)) {
final Bundle b = wire.getRequirer().getBundle();
if (b != cap.getResource().getBundle()) {
result.add(wire.getRequirer().getBundle());
}
}
}
addRequiringBundles(wiring, result);
}
return toArrayOrNull(result, Bundle.class);
}
/**
* @see org.osgi.service.packageadmin.ExportedPackage#getSpecificationVersion()
*/
public String getSpecificationVersion() {
final Version version = getVersion();
return version == null ? null : version.toString();
}
/**
* @see org.osgi.service.packageadmin.ExportedPackage#getVersion()
*/
public Version getVersion() {
return (Version) cap.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
}
/**
* @see org.osgi.service.packageadmin.ExportedPackage#isRemovalPending()
*/
public boolean isRemovalPending() {
final BundleRevision rev = cap.getResource();
return rev.getBundle().getState() == Bundle.UNINSTALLED
|| rev.getBundle().adapt(BundleRevision.class) != rev;
}
@Override
public String toString() {
return cap.toString();
}
}
private class RequiredBundleImpl implements RequiredBundle {
private final BundleRevision rev;
public RequiredBundleImpl(final BundleRevision rev) {
this.rev = rev;
}
/**
* @see org.osgi.service.packageadmin.RequiredBundle#getSymbolicName()
*/
public String getSymbolicName() {
return rev.getSymbolicName();
}
/**
* @see org.osgi.service.packageadmin.RequiredBundle#getBundle()
*/
public Bundle getBundle() {
return rev.getBundle();
}
/**
* @see org.osgi.service.packageadmin.RequiredBundle#getRequiringBundles()
*/
public Bundle[] getRequiringBundles() {
final ArrayList<Bundle> result = new ArrayList<Bundle>();
final BundleWiring wiring = rev.getWiring();
if (wiring != null) {
addRequiringBundles(wiring, result);
}
return toArrayOrNull(result, Bundle.class);
}
/**
* @see org.osgi.service.packageadmin.RequiredBundle#getVersion()
*/
public Version getVersion() {
return rev.getVersion();
}
/**
* @see org.osgi.service.packageadmin.RequiredBundle#isRemovalPending()
*/
public boolean isRemovalPending() {
return rev.getBundle().getState() == Bundle.UNINSTALLED
|| rev.getBundle().adapt(BundleRevision.class) != rev;
}
public String toString() {
return "RequiredBundle{" + rev.toString();
}
}
}