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 );
+                }
+
             }
         }