FELIX-2520 Use PackageAdmin service to try to resolve the interface class to find a matching method and fall back to Object class if interface class is not exported by any bundle.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@982608 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/pom.xml b/scr/pom.xml
index 7f6d772..7238cba 100644
--- a/scr/pom.xml
+++ b/scr/pom.xml
@@ -186,6 +186,13 @@
org.osgi.service.log;version="[1.3,2)";resolution:=optional,
<!--
+ PackageAdmin is used to find reference types if
+ the any component's bundle does not import it.
+ See BindMethod.getParameterClass(Class) for details
+ -->
+ org.osgi.service.packageadmin;version="[1.2,2)";resolution:=optional,
+
+ <!--
Configuration Admin is optional and if it is
present, version 1.2 (from R4.0) is enough
-->
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/Activator.java b/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
index ebadd5b..7fad6e5 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
@@ -46,6 +46,9 @@
// name of the LogService class (this is a string to not create a reference to the class)
static final String LOGSERVICE_CLASS = "org.osgi.service.log.LogService";
+ // name of the PackageAdmin class (this is a string to not create a reference to the class)
+ static final String PACKAGEADMIN_CLASS = "org.osgi.service.packageadmin.PackageAdmin";
+
// Our configuration from bundle context properties and Config Admin
private ScrConfiguration m_configuration;
@@ -53,11 +56,14 @@
private static int m_logLevel = LogService.LOG_ERROR;
// this bundle's context
- private BundleContext m_context;
+ private static BundleContext m_context;
// the log service to log messages to
private static ServiceTracker m_logService;
+ // the package admin service (see BindMethod.getParameterClass)
+ private static ServiceTracker m_packageAdmin;
+
// map of BundleComponentActivator instances per Bundle indexed by Bundle id
private Map m_componentBundles;
@@ -148,6 +154,23 @@
m_componentActor.terminate();
m_componentActor = null;
}
+
+ // close the LogService tracker now
+ if ( m_logService != null )
+ {
+ m_logService.close();
+ m_logService = null;
+ }
+
+ // close the PackageAdmin tracker now
+ if ( m_packageAdmin != null )
+ {
+ m_packageAdmin.close();
+ m_packageAdmin = null;
+ }
+
+ // remove the reference to the component context
+ m_context = null;
}
@@ -433,4 +456,22 @@
}
}
}
+
+
+ public static Object getPackageAdmin()
+ {
+ if ( m_packageAdmin == null )
+ {
+ synchronized ( Activator.class )
+ {
+ if ( m_packageAdmin == null )
+ {
+ m_packageAdmin = new ServiceTracker( m_context, PACKAGEADMIN_CLASS, null );
+ m_packageAdmin.open();
+ }
+ }
+ }
+
+ return m_packageAdmin.getService();
+ }
}
\ No newline at end of file
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
index 8a6a749..a6d1c8f 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/BindMethod.java
@@ -22,9 +22,12 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import org.apache.felix.scr.impl.Activator;
import org.apache.felix.scr.impl.manager.AbstractComponentManager;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
/**
@@ -33,6 +36,8 @@
public class BindMethod extends BaseMethod
{
+ private static final Class OBJECT_CLASS = Object.class;
+
private final String m_referenceName;
private final String m_referenceClassName;
@@ -75,6 +80,12 @@
// flag indicating a suitable but inaccessible method has been found
boolean suitableMethodNotAccessible = false;
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "doFindMethod: Looking for method " + targetClass.getName() + "." + getMethodName(), null );
+ }
+
// Case 1 - Service reference parameter
Method method;
try
@@ -82,6 +93,10 @@
method = getServiceReferenceMethod( targetClass, acceptPrivate, acceptPackage );
if ( method != null )
{
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null );
+ }
return method;
}
}
@@ -95,6 +110,14 @@
if ( parameterClass != null )
{
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log(
+ LogService.LOG_DEBUG,
+ "doFindMethod: No method taking ServiceReference found, checking method taking "
+ + parameterClass.getName(), null );
+ }
+
// Case 2 - Service object parameter
try
{
@@ -159,6 +182,13 @@
}
}
+ else if ( getComponentManager().isLogEnabled( LogService.LOG_WARNING ) )
+ {
+ getComponentManager().log(
+ LogService.LOG_WARNING,
+ "doFindMethod: Cannot check for methods taking parameter class " + m_referenceClassName + ": "
+ + targetClass.getName() + " does not see it", null );
+ }
// if at least one suitable method could be found but none of
// the suitable methods are accessible, we have to terminate
@@ -190,6 +220,14 @@
*/
private Class getParameterClass( final Class targetClass )
{
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log(
+ LogService.LOG_DEBUG,
+ "getParameterClass: Looking for interface class " + m_referenceClassName + "through loader of "
+ + targetClass.getName(), null );
+ }
+
try
{
// need the class loader of the target class, which may be the
@@ -200,8 +238,13 @@
loader = ClassLoader.getSystemClassLoader();
}
- return loader.loadClass( m_referenceClassName );
-
+ final Class referenceClass = loader.loadClass( m_referenceClassName );
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "getParameterClass: Found class " + referenceClass.getName(), null );
+ }
+ return referenceClass;
}
catch ( ClassNotFoundException cnfe )
{
@@ -209,7 +252,67 @@
// super class so we try this class next
}
- return null;
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "getParameterClass: Not found through component class, using PackageAdmin service", null );
+ }
+
+ // try to load the class with the help of the PackageAdmin service
+ PackageAdmin pa = ( PackageAdmin ) Activator.getPackageAdmin();
+ if ( pa != null )
+ {
+ final String referenceClassPackage = m_referenceClassName.substring( 0, m_referenceClassName
+ .lastIndexOf( '.' ) );
+ ExportedPackage[] pkg = pa.getExportedPackages( referenceClassPackage );
+ if ( pkg != null )
+ {
+ for ( int i = 0; i < pkg.length; i++ )
+ {
+ try
+ {
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log(
+ LogService.LOG_DEBUG,
+ "getParameterClass: Checking Bundle " + pkg[i].getExportingBundle().getSymbolicName()
+ + "/" + pkg[i].getExportingBundle().getBundleId(), null );
+ }
+
+ Class referenceClass = pkg[i].getExportingBundle().loadClass( m_referenceClassName );
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "getParameterClass: Found class " + referenceClass.getName(), null );
+ }
+ return referenceClass;
+ }
+ catch ( ClassNotFoundException cnfe )
+ {
+ // exported package does not provide the interface !!!!
+ }
+ }
+ }
+ else if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "getParameterClass: No bundles exporting package " + referenceClassPackage + " found ", null );
+ }
+ }
+ else if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "getParameterClass: PackageAdmin service not available, cannot find class", null );
+ }
+
+ // class cannot be found, neither through the component nor from an
+ // export, so we fall back to assuming Object
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG,
+ "getParameterClass: No class found, falling back to class Object", null );
+ }
+ return OBJECT_CLASS;
}
@@ -286,10 +389,22 @@
Method candidateBindMethods[] = targetClass.getDeclaredMethods();
boolean suitableNotAccessible = false;
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log(
+ LogService.LOG_DEBUG,
+ "getServiceObjectAssignableMethod: Checking " + candidateBindMethods.length
+ + " declared method in class " + targetClass.getName(), null );
+ }
+
// Iterate over them
for ( int i = 0; i < candidateBindMethods.length; i++ )
{
Method method = candidateBindMethods[i];
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG, "getServiceObjectAssignableMethod: Checking " + method, null );
+ }
// Get the parameters for the current method
Class[] parameters = method.getParameterTypes();
@@ -300,6 +415,11 @@
if ( parameters.length == 1 && method.getName().equals( getMethodName() ) )
{
+ if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log( LogService.LOG_DEBUG, "getServiceObjectAssignableMethod: Considering " + method, null );
+ }
+
// Get the parameter type
final Class theParameter = parameters[0];
@@ -316,6 +436,14 @@
// suitable method is not accessible, flag for exception
suitableNotAccessible = true;
}
+ else if ( getComponentManager().isLogEnabled( LogService.LOG_DEBUG ) )
+ {
+ getComponentManager().log(
+ LogService.LOG_DEBUG,
+ "getServiceObjectAssignableMethod: Parameter failure: Required " + theParameter + "; actual "
+ + parameterClass.getName(), null );
+ }
+
}
}