blob: b54a40ba22ea6d8e64264528a6611add07eacc32 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 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
* 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.internal.loader.sources;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.framework.util.KeyedElement;
import org.eclipse.osgi.internal.framework.EquinoxBundle;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.loader.SystemBundleLoader;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.service.packageadmin.PackageAdmin;
public abstract class PackageSource implements KeyedElement {
protected final String id;
public PackageSource(String id) {
// others depend on the id being interned; see SingleSourcePackage.equals
this.id = id.intern();
}
public String getId() {
return id;
}
public abstract SingleSourcePackage[] getSuppliers();
public boolean compare(KeyedElement other) {
return id.equals(((PackageSource) other).getId());
}
public int getKeyHashCode() {
return id.hashCode();
}
public Object getKey() {
return id;
}
public boolean isNullSource() {
return false;
}
public abstract Class<?> loadClass(String name) throws ClassNotFoundException;
public abstract URL getResource(String name);
public abstract Enumeration<URL> getResources(String name) throws IOException;
//TODO See how this relates with FilteredSourcePackage. Overwriting or doing a double dispatch might be good.
// This is intentionally lenient; we don't force all suppliers to match (only one)
// it is better to get class cast exceptions in split package cases than miss an event
public boolean hasCommonSource(PackageSource other) {
if (other == null)
return false;
if (this == other)
return true;
SingleSourcePackage[] suppliers1 = getSuppliers();
SingleSourcePackage[] suppliers2 = other.getSuppliers();
if (suppliers1 == null || suppliers2 == null)
return false;
// This will return true if the specified source has at least one
// of the suppliers of this source.
for (int i = 0; i < suppliers1.length; i++)
for (int j = 0; j < suppliers2.length; j++)
if (suppliers2[j].equals(suppliers1[i]))
return true;
return false;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(id).append(" -> "); //$NON-NLS-1$
SingleSourcePackage[] suppliers = getSuppliers();
if (suppliers == null) {
return builder.append(String.valueOf(null)).toString();
}
builder.append('[');
for (int i = 0; i < suppliers.length; i++) {
if (i > 0) {
builder.append(',');
}
builder.append(suppliers[i].getLoader());
}
builder.append(']');
return builder.toString();
}
public abstract Collection<String> listResources(String path, String filePattern);
/**
* Used by ServiceReferenceImpl for isAssignableTo
* @param registrant Bundle registering service
* @param client Bundle desiring to use service
* @param className class name to use
* @param serviceClass class of original service object
* @param container the equinox container
* @return true if assignable given package wiring
*/
public static boolean isServiceAssignableTo(Bundle registrant, Bundle client, String className, Class<?> serviceClass, EquinoxContainer container) {
// 1) if the registrant == client always return true
if (registrant == client) {
return true;
}
// 2) get the package name from the specified className
String pkgName = BundleLoader.getPackageName(className);
if (pkgName.startsWith("java.")) //$NON-NLS-1$
return true;
BundleLoader producerBL = getBundleLoader(registrant);
if (producerBL == null)
return false;
BundleLoader consumerBL = getBundleLoader(client);
if (consumerBL == null)
return false;
// 3) for the specified bundle, find the wiring for the package. If no wiring is found return true
PackageSource consumerSource = consumerBL.getPackageSource(pkgName);
if (consumerSource == null)
return true;
// work around the issue when the package is in the EE and we delegate to boot for that package
if (container.isBootDelegationPackage(pkgName)) {
Bundle systemBundle = container.getStorage().getModuleContainer().getModule(0).getBundle();
SystemBundleLoader systemLoader = (SystemBundleLoader) getBundleLoader(systemBundle);
if (systemLoader.isExportedPackage(pkgName)) {
return true; // in this case we have a common source from the EE
}
}
// 4) For the registrant bundle, find the wiring for the package.
PackageSource producerSource = producerBL.getPackageSource(pkgName);
if (producerSource == null) {
if (serviceClass != null && ServiceFactory.class.isAssignableFrom(serviceClass)) {
@SuppressWarnings("deprecation")
Bundle bundle = container.getPackageAdmin().getBundle(serviceClass);
if (bundle != null && bundle != registrant)
// in this case we have a wacky ServiceFactory that is doing something we cannot
// verify if it is correct. Instead of failing we allow the assignment and hope for the best
// bug 326918
return true;
}
// 5) If no wiring is found for the registrant bundle then find the wiring for the classloader of the service object. If no wiring is found return false.
producerSource = getPackageSource(serviceClass, pkgName, container.getPackageAdmin());
if (producerSource == null)
return false;
}
// 6) If the two wirings found are equal then return true; otherwise return false.
return producerSource.hasCommonSource(consumerSource);
}
private static PackageSource getPackageSource(Class<?> serviceClass, String pkgName, @SuppressWarnings("deprecation") PackageAdmin packageAdmin) {
if (serviceClass == null)
return null;
@SuppressWarnings("deprecation")
Bundle serviceBundle = packageAdmin.getBundle(serviceClass);
if (serviceBundle == null)
return null;
BundleLoader producerBL = getBundleLoader(serviceBundle);
if (producerBL == null)
return null;
PackageSource producerSource = producerBL.getPackageSource(pkgName);
if (producerSource != null)
return producerSource;
// try the interfaces
Class<?>[] interfaces = serviceClass.getInterfaces();
// note that getInterfaces never returns null
for (int i = 0; i < interfaces.length; i++) {
producerSource = getPackageSource(interfaces[i], pkgName, packageAdmin);
if (producerSource != null)
return producerSource;
}
// try super class
return getPackageSource(serviceClass.getSuperclass(), pkgName, packageAdmin);
}
private static BundleLoader getBundleLoader(Bundle bundle) {
ModuleRevision producer = ((EquinoxBundle) bundle).getModule().getCurrentRevision();
ModuleWiring producerWiring = producer.getWiring();
return producerWiring == null ? null : (BundleLoader) producerWiring.getModuleLoader();
}
}