FELIX-927 Complete bind/unbind method signature extension for
   methods with service object assignable plus Map and support
   for private/default methods. Plust test cases in BindMethodTest
FELIX-1437 Only support new signatures and private/package support
   for DS 1.1 declared components
FELIX-1440 Add SuitableMethodNotAccessibleException to support
   method lookup termination if a class provides suitable methods
   which are not accessible. Plus test case in BindMethodTest

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@800244 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/BindMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/BindMethod.java
index 9ecbb27..524ffc3 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/BindMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/BindMethod.java
@@ -21,9 +21,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.felix.scr.impl.helper.ReadOnlyDictionary;
 import org.apache.felix.scr.impl.helper.ReflectionHelper;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
@@ -36,6 +34,7 @@
 class BindMethod
 {
 
+    private final boolean m_isDS11;
     private final String m_methodName;
     private final Class m_componentClass;
     private final String m_referenceName;
@@ -46,9 +45,10 @@
     private State m_state;
 
 
-    BindMethod( final String methodName, final Class componentClass, final String referenceName,
+    BindMethod( final boolean isDS11, final String methodName, final Class componentClass, final String referenceName,
         final String referenceClassName, final Logger logger )
     {
+        m_isDS11 = isDS11;
         m_methodName = methodName;
         m_componentClass = componentClass;
         m_referenceName = referenceName;
@@ -65,125 +65,383 @@
     }
 
 
-    private Method findMethod( final Class targetClass ) throws InvocationTargetException
+    /**
+     * Finds the method named in the {@link #m_methodName} field in the given
+     * <code>targetClass</code>. If the target class has no acceptable method
+     * the class hierarchy is traversed until a method is found or the root
+     * of the class hierarchy is reached without finding a method.
+     *
+     * @param targetClass The class in which to look for the method
+     * @param acceptPrivate <code>true</code> if private methods should be
+     *      considered.
+     * @param acceptPackage <code>true</code> if package private methods should
+     *      be considered.
+     * @return The requested method or <code>null</code> if no acceptable method
+     *      can be found in the target class or any super class.
+     * @throws InvocationTargetException If an unexpected Throwable is caught
+     *      trying to find the requested method.
+     * @throws SuitableMethodNotAccessibleException If a suitable method was
+     *      found which is not accessible
+     */
+    private Method findMethod( final Class targetClass, final boolean acceptPrivate, final boolean acceptPackage )
+        throws InvocationTargetException//, SuitableMethodNotAccessibleException
     {
-        Class parameterClass = null;
-
         // 112.3.1 The method is searched for using the following priority
         // 1. The method's parameter type is org.osgi.framework.ServiceReference
         // 2. The method's parameter type is the type specified by the
         // reference's interface attribute
         // 3. The method's parameter type is assignable from the type specified
         // by the reference's interface attribute
-        try
+
+        // Case 1 - Service reference parameter
+        Method method = getServiceReferenceMethod( targetClass, acceptPrivate, acceptPackage );
+        if ( method != null )
         {
-            // Case 1 - ServiceReference parameter
-            return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
-                { ReflectionHelper.SERVICE_REFERENCE_CLASS }, false, // do not accept private methods
-                false // do not accept package methods
-                );
+            return method;
         }
-        catch ( NoSuchMethodException ex )
+
+        // for further methods we need the class of the service object
+        final Class parameterClass = getParameterClass( targetClass );
+        if ( parameterClass != null )
         {
 
+            // Case2 - Service object parameter
+            method = getServiceObjectMethod( targetClass, parameterClass, acceptPrivate, acceptPackage );
+            if ( method != null )
+            {
+                return method;
+            }
+
+            // Case 3 - Service interface assignement compatible methods
+            SuitableMethodNotAccessibleException methodAccessibleEx = null;
             try
             {
-                // Case2 - Service object parameter
-
-                // need the class loader of the target class, which may be the
-                // system classloader, which case getClassLoader may retur null
-                ClassLoader loader = targetClass.getClassLoader();
-                if ( loader == null )
+                method = getServiceObjectAssignableMethod( targetClass, parameterClass, acceptPrivate, acceptPackage );
+                if ( method != null )
                 {
-                    loader = ClassLoader.getSystemClassLoader();
+                    return method;
                 }
-
-                parameterClass = loader.loadClass( m_referenceClassName );
-                return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
-                    { parameterClass }, false, false );
             }
-            catch ( NoSuchMethodException ex2 )
+            catch ( SuitableMethodNotAccessibleException ex )
+            {
+                methodAccessibleEx = ex;
+            }
+
+            // signatures taking a map are only supported starting with DS 1.1
+            if ( m_isDS11 )
             {
 
-                // Case 3 - Service interface assignement compatible methods
-
-                // Get all potential bind methods
-                Method candidateBindMethods[] = targetClass.getDeclaredMethods();
-
-                // Iterate over them
-                for ( int i = 0; i < candidateBindMethods.length; i++ )
+                // Case 4: same as case 2, but + Map param (DS 1.1 only)
+                method = getServiceObjectWithMapMethod( targetClass, parameterClass, acceptPrivate, acceptPackage );
+                if ( method != null )
                 {
-                    Method method = candidateBindMethods[i];
-
-                    // Get the parameters for the current method
-                    Class[] parameters = method.getParameterTypes();
-
-                    // Select only the methods that receive a single
-                    // parameter
-                    // and a matching name
-                    if ( parameters.length == 1 && method.getName().equals( m_methodName ) )
-                    {
-
-                        // Get the parameter type
-                        Class theParameter = parameters[0];
-
-                        // Check if the parameter type is ServiceReference
-                        // or is assignable from the type specified by the
-                        // reference's interface attribute
-                        if ( theParameter.isAssignableFrom( parameterClass ) )
-                        {
-
-                            // Final check: it must be public or protected
-                            if ( Modifier.isPublic( method.getModifiers() )
-                                || Modifier.isProtected( method.getModifiers() ) )
-                            {
-                                if ( !method.isAccessible() )
-                                {
-                                    method.setAccessible( true );
-                                }
-                                return method;
-                            }
-                        }
-                    }
+                    return method;
                 }
 
-                // Case 4: same as case 2, but + Map param
+                // Case 5: same as case 3, but + Map param (DS 1.1 only)
                 try
                 {
-                    // need the class loader of the target class, which may be the
-                    // system classloader, which case getClassLoader may retur null
-                    ClassLoader loader = targetClass.getClassLoader();
-                    if ( loader == null )
+                    method = getServiceObjectAssignableWithMapMethod( targetClass, parameterClass, acceptPrivate,
+                        acceptPackage );
+                    if ( method != null )
                     {
-                        loader = ClassLoader.getSystemClassLoader();
+                        return method;
                     }
-
-                    parameterClass = loader.loadClass( m_referenceClassName );
-                    return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
-                        { parameterClass, Map.class }, false, false );
                 }
-                catch ( NoSuchMethodException ex3 )
+                catch ( SuitableMethodNotAccessibleException ex )
                 {
+                    methodAccessibleEx = ex;
+                }
 
-                }
-                catch ( ClassNotFoundException ex3 )
-                {
-                    // if we can't load the class, perhaps the method is declared in a super class
-                    // so we try this class next
-                }
             }
-            catch ( ClassNotFoundException ex2 )
+
+            // if at least one suitable method could be found but none of
+            // the suitable methods are accessible, we have to terminate
+            if ( methodAccessibleEx != null )
             {
-                // if we can't load the class, perhaps the method is declared in a super class
-                // so we try this class next
+                m_logger.log( LogService.LOG_ERROR,
+                    "DependencyManager : Suitable but non-accessible method found in class " + targetClass.getName() );
+                return null;
             }
 
-            // TODO: Case 5: same as case 3, but + Map param
         }
 
         // if we get here, we have no method, so check the super class
-        Class superClass = targetClass.getSuperclass();
-        return ( superClass != null ) ? findMethod( superClass ) : null;
+        final Class superClass = targetClass.getSuperclass();
+        if (superClass == null) {
+            return null;
+        }
+
+        // super class method check ignores private methods and accepts
+        // package methods only if in the same package and package
+        // methods are (still) allowed
+        final boolean withPackage = acceptPackage && targetClass.getClassLoader() == superClass.getClassLoader()
+            && ReflectionHelper.getPackageName( targetClass ).equals( ReflectionHelper.getPackageName( superClass ) );
+        return findMethod( superClass, false, withPackage);
+    }
+
+
+    /**
+     * Returns the class object representing the class of the service reference
+     * named by the {@link #m_referenceClassName} field. The class loader of
+     * the <code>targetClass</code> is used to load the service class.
+     * <p>
+     * It may well be possible, that the classloader of the target class cannot
+     * see the service object class, for example if the service reference is
+     * inherited from a component class of another bundle.
+     *
+     * @return The class object for the referred to service or <code>null</code>
+     *      if the class loader of the <code>targetClass</code> cannot see that
+     *      class.
+     */
+    private Class getParameterClass( final Class targetClass )
+    {
+        try
+        {
+            // need the class loader of the target class, which may be the
+            // system classloader, which case getClassLoader may retur null
+            ClassLoader loader = targetClass.getClassLoader();
+            if ( loader == null )
+            {
+                loader = ClassLoader.getSystemClassLoader();
+            }
+
+            return loader.loadClass( m_referenceClassName );
+
+        }
+        catch ( ClassNotFoundException cnfe )
+        {
+            // if we can't load the class, perhaps the method is declared in a
+            // super class so we try this class next
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns a method taking a single <code>ServiceReference</code> object
+     * as a parameter or <code>null</code> if no such method exists.
+     *
+     * @param targetClass The class in which to look for the method. Only this
+     *      class is searched for the method.
+     * @param acceptPrivate <code>true</code> if private methods should be
+     *      considered.
+     * @param acceptPackage <code>true</code> if package private methods should
+     *      be considered.
+     * @return The requested method or <code>null</code> if no acceptable method
+     *      can be found in the target class.
+     * @throws InvocationTargetException If an unexpected Throwable is caught
+     *      trying to find the requested method.
+     */
+    private Method getServiceReferenceMethod( final Class targetClass, boolean acceptPrivate, boolean acceptPackage )
+        throws InvocationTargetException
+    {
+        try
+        {
+            return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
+                { ReflectionHelper.SERVICE_REFERENCE_CLASS }, acceptPrivate, acceptPackage );
+        }
+        catch ( NoSuchMethodException e )
+        {
+            // the named method could not be found
+        }
+
+        // no method taking service reference
+        return null;
+    }
+
+
+    /**
+     * Returns a method taking a single parameter of the exact type declared
+     * for the service reference or <code>null</code> if no such method exists.
+     *
+     * @param targetClass The class in which to look for the method. Only this
+     *      class is searched for the method.
+     * @param acceptPrivate <code>true</code> if private methods should be
+     *      considered.
+     * @param acceptPackage <code>true</code> if package private methods should
+     *      be considered.
+     * @return The requested method or <code>null</code> if no acceptable method
+     *      can be found in the target class.
+     * @throws InvocationTargetException If an unexpected Throwable is caught
+     *      trying to find the requested method.
+     */
+    private Method getServiceObjectMethod( final Class targetClass, final Class parameterClass, boolean acceptPrivate,
+        boolean acceptPackage ) throws InvocationTargetException
+    {
+        try
+        {
+            return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
+                { parameterClass }, acceptPrivate, acceptPackage );
+        }
+        catch ( NoSuchMethodException nsme )
+        {
+            // no method taking service object
+        }
+
+        // no method taking service object
+        return null;
+    }
+
+
+    /**
+     * Returns a method taking a single object whose type is assignment
+     * compatible with the declared service type or <code>null</code> if no
+     * such method exists.
+     *
+     * @param targetClass The class in which to look for the method. Only this
+     *      class is searched for the method.
+     * @param acceptPrivate <code>true</code> if private methods should be
+     *      considered.
+     * @param acceptPackage <code>true</code> if package private methods should
+     *      be considered.
+     * @return The requested method or <code>null</code> if no acceptable method
+     *      can be found in the target class.
+     * @throws SuitableMethodNotAccessibleException If a suitable method was
+     *      found which is not accessible
+     */
+    private Method getServiceObjectAssignableMethod( final Class targetClass, final Class parameterClass,
+        boolean acceptPrivate, boolean acceptPackage ) throws SuitableMethodNotAccessibleException
+    {
+        // Get all potential bind methods
+        Method candidateBindMethods[] = targetClass.getDeclaredMethods();
+        boolean suitableNotAccessible = false;
+
+        // Iterate over them
+        for ( int i = 0; i < candidateBindMethods.length; i++ )
+        {
+            Method method = candidateBindMethods[i];
+
+            // Get the parameters for the current method
+            Class[] parameters = method.getParameterTypes();
+
+            // Select only the methods that receive a single
+            // parameter
+            // and a matching name
+            if ( parameters.length == 1 && method.getName().equals( m_methodName ) )
+            {
+
+                // Get the parameter type
+                final Class theParameter = parameters[0];
+
+                // Check if the parameter type is ServiceReference
+                // or is assignable from the type specified by the
+                // reference's interface attribute
+                if ( theParameter.isAssignableFrom( parameterClass ) )
+                {
+                    if ( ReflectionHelper.accept( method, acceptPrivate, acceptPackage ) )
+                    {
+                        return method;
+                    }
+
+                    // suitable method is not accessible, flag for exception
+                    suitableNotAccessible = true;
+                }
+            }
+        }
+
+        // if one or more suitable methods which are not accessible is/are
+        // found an exception is thrown
+        if ( suitableNotAccessible )
+        {
+            throw new SuitableMethodNotAccessibleException();
+        }
+
+        // no method with assignment compatible argument found
+        return null;
+    }
+
+
+    /**
+     * Returns a method taking two parameters, the first being of the exact
+     * type declared for the service reference and the second being a
+     * <code>Map</code> or <code>null</code> if no such method exists.
+     *
+     * @param targetClass The class in which to look for the method. Only this
+     *      class is searched for the method.
+     * @param acceptPrivate <code>true</code> if private methods should be
+     *      considered.
+     * @param acceptPackage <code>true</code> if package private methods should
+     *      be considered.
+     * @return The requested method or <code>null</code> if no acceptable method
+     *      can be found in the target class.
+     * @throws InvocationTargetException If an unexpected Throwable is caught
+     *      trying to find the requested method.
+     */
+    private Method getServiceObjectWithMapMethod( final Class targetClass, final Class parameterClass,
+        boolean acceptPrivate, boolean acceptPackage ) throws InvocationTargetException
+    {
+        try
+        {
+            return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
+                { parameterClass, ReflectionHelper.MAP_CLASS }, acceptPrivate, acceptPackage );
+        }
+        catch ( NoSuchMethodException nsme )
+        {
+            // no method taking service object
+        }
+
+        // no method taking service object
+        return null;
+    }
+
+
+    /**
+     * Returns a method taking two parameters, the first being an object
+     * whose type is assignment compatible with the declared service type and
+     * the second being a <code>Map</code> or <code>null</code> if no such
+     * method exists.
+     *
+     * @param targetClass The class in which to look for the method. Only this
+     *      class is searched for the method.
+     * @param acceptPrivate <code>true</code> if private methods should be
+     *      considered.
+     * @param acceptPackage <code>true</code> if package private methods should
+     *      be considered.
+     * @return The requested method or <code>null</code> if no acceptable method
+     *      can be found in the target class.
+     * @throws SuitableMethodNotAccessibleException If a suitable method was
+     *      found which is not accessible
+     */
+    private Method getServiceObjectAssignableWithMapMethod( final Class targetClass, final Class parameterClass,
+        boolean acceptPrivate, boolean acceptPackage ) throws SuitableMethodNotAccessibleException
+    {
+        // Get all potential bind methods
+        Method candidateBindMethods[] = targetClass.getDeclaredMethods();
+        boolean suitableNotAccessible = false;
+
+        // Iterate over them
+        for ( int i = 0; i < candidateBindMethods.length; i++ )
+        {
+            final Method method = candidateBindMethods[i];
+            final Class[] parameters = method.getParameterTypes();
+            if ( parameters.length == 2 && method.getName().equals( m_methodName ) )
+            {
+
+                // parameters must be refclass,map
+                if ( parameters[0].isAssignableFrom( parameterClass ) && parameters[1] == ReflectionHelper.MAP_CLASS )
+                {
+                    if ( ReflectionHelper.accept( method, acceptPrivate, acceptPackage ) )
+                    {
+                        return method;
+                    }
+
+                    // suitable method is not accessible, flag for exception
+                    suitableNotAccessible = true;
+                }
+            }
+        }
+
+        // if one or more suitable methods which are not accessible is/are
+        // found an exception is thrown
+        if ( suitableNotAccessible )
+        {
+            throw new SuitableMethodNotAccessibleException();
+        }
+
+        // no method with assignment compatible argument found
+        return null;
     }
 
 
@@ -191,7 +449,6 @@
     {
         final Class[] paramTypes = m_method.getParameterTypes();
         final Object[] params = new Object[paramTypes.length];
-        Map properties = null;
         for ( int i = 0; i < params.length; i++ )
         {
             if ( paramTypes[i] == ReflectionHelper.SERVICE_REFERENCE_CLASS )
@@ -200,21 +457,7 @@
             }
             else if ( paramTypes[i] == ReflectionHelper.MAP_CLASS )
             {
-                if ( properties == null )
-                {
-                    final ServiceReference serviceReference = service.getReference();
-                    properties = new HashMap();
-                    final String[] keys = serviceReference.getPropertyKeys();
-                    if ( keys != null )
-                    {
-                        for ( int j = 0; j < keys.length; j++ )
-                        {
-                            final String key = keys[j];
-                            properties.put( key, serviceReference.getProperty( key ) );
-                        }
-                    }
-                }
-                params[i] = properties;
+                params[i] = new ReadOnlyDictionary( service.getReference() );
             }
             else
             {
@@ -288,7 +531,9 @@
             m_logger.log( LogService.LOG_DEBUG, "getting " + getMethodNamePrefix() + "bind: " + m_methodName );
             try
             {
-                m_method = findMethod( m_componentClass );
+                // if the owning component is declared with the DS 1.1 namespace
+                // (or newer), private and package private methods are accepted
+                m_method = findMethod( m_componentClass, m_isDS11, m_isDS11 );
                 if ( m_method == null )
                 {
                     m_state = new NotFound();
@@ -358,4 +603,10 @@
 
     }
 
+    //---------- Logger ------------------------------------
+
+    static class SuitableMethodNotAccessibleException extends Exception
+    {
+    }
+
 }
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/BindMethodTest.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/BindMethodTest.java
index 9fd1acd..6d4ae02 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/manager/BindMethodTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/BindMethodTest.java
@@ -19,10 +19,13 @@
 package org.apache.felix.scr.impl.manager;
 
 
-import java.util.Map;
-
 import junit.framework.TestCase;
 
+import org.apache.felix.scr.impl.manager.components.FakeService;
+import org.apache.felix.scr.impl.manager.components.T1;
+import org.apache.felix.scr.impl.manager.components.T1a;
+import org.apache.felix.scr.impl.manager.components.T3;
+import org.apache.felix.scr.impl.manager.components2.T2;
 import org.easymock.EasyMock;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
@@ -64,281 +67,374 @@
 
     public void test_Unexistent()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "unexistent", T1.class ).invoke( t1, m_service );
-        assertNull( t1.callPerformed );
+        testMethod( "unexistent", new T1(), false, null );
+        testMethod( "unexistent", new T1(), true, null );
+        testMethod( "unexistent", new T2(), false, null );
+        testMethod( "unexistent", new T2(), true, null );
+        testMethod( "unexistent", new T3(), false, null );
+        testMethod( "unexistent", new T3(), true, null );
     }
 
 
     public void test_privateT1()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "privateT1", T1.class ).invoke( t1, m_service );
-        assertNull( t1.callPerformed );
+        testMethod( "privateT1", new T1(), false, null );
+        testMethod( "privateT1", new T1(), true, null );
+        testMethod( "privateT1", new T2(), false, null );
+        testMethod( "privateT1", new T2(), true, null );
+        testMethod( "privateT1", new T3(), false, null );
+        testMethod( "privateT1", new T3(), true, null );
+    }
+
+
+    public void test_privateT1SR()
+    {
+        testMethod( "privateT1SR", new T1(), false, null );
+        testMethod( "privateT1SR", new T1(), true, "privateT1SR" );
+        testMethod( "privateT1SR", new T2(), false, null );
+        testMethod( "privateT1SR", new T2(), true, null );
+    }
+
+
+    public void test_privateT1SI()
+    {
+        testMethod( "privateT1SI", new T1(), false, null );
+        testMethod( "privateT1SI", new T1(), true, "privateT1SI" );
+        testMethod( "privateT1SI", new T2(), false, null );
+        testMethod( "privateT1SI", new T2(), true, null );
+    }
+
+
+    public void test_privateT1SIMap()
+    {
+        testMethod( "privateT1SIMap", new T1(), false, null );
+        testMethod( "privateT1SIMap", new T1(), true, "privateT1SIMap" );
+        testMethod( "privateT1SIMap", new T2(), false, null );
+        testMethod( "privateT1SIMap", new T2(), true, null );
+    }
+
+
+    public void test_privateT1SSI()
+    {
+        testMethod( "privateT1SSI", new T1(), false, null );
+        testMethod( "privateT1SSI", new T1(), true, "privateT1SSI" );
+        testMethod( "privateT1SSI", new T2(), false, null );
+        testMethod( "privateT1SSI", new T2(), true, null );
+    }
+
+
+    public void test_privateT1SSIMap()
+    {
+        testMethod( "privateT1SSIMap", new T1(), false, null );
+        testMethod( "privateT1SSIMap", new T1(), true, "privateT1SSIMap" );
+        testMethod( "privateT1SSIMap", new T2(), false, null );
+        testMethod( "privateT1SSIMap", new T2(), true, null );
+    }
+
+
+    public void test_privateT2()
+    {
+        testMethod( "privateT2", new T1(), false, null );
+        testMethod( "privateT2", new T1(), true, null );
+        testMethod( "privateT2", new T2(), false, null );
+        testMethod( "privateT2", new T2(), true, null );
+    }
+
+
+    public void test_privateT2SR()
+    {
+        testMethod( "privateT2SR", new T1(), false, null );
+        testMethod( "privateT2SR", new T1(), true, null );
+        testMethod( "privateT2SR", new T2(), false, null );
+        testMethod( "privateT2SR", new T2(), true, "privateT2SR" );
+    }
+
+
+    public void test_privateT2SI()
+    {
+        testMethod( "privateT2SI", new T1(), false, null );
+        testMethod( "privateT2SI", new T1(), true, null );
+        testMethod( "privateT2SI", new T2(), false, null );
+        testMethod( "privateT2SI", new T2(), true, "privateT2SI" );
+    }
+
+
+    public void test_privateT2SIMap()
+    {
+        testMethod( "privateT2SIMap", new T1(), false, null );
+        testMethod( "privateT2SIMap", new T1(), true, null );
+        testMethod( "privateT2SIMap", new T2(), false, null );
+        testMethod( "privateT2SIMap", new T2(), true, "privateT2SIMap" );
+    }
+
+
+    public void test_privateT2SSI()
+    {
+        testMethod( "privateT2SSI", new T1(), false, null );
+        testMethod( "privateT2SSI", new T1(), true, null );
+        testMethod( "privateT2SSI", new T2(), false, null );
+        testMethod( "privateT2SSI", new T2(), true, "privateT2SSI" );
+    }
+
+
+    public void test_privateT2SSIMap()
+    {
+        testMethod( "privateT2SSIMap", new T1(), false, null );
+        testMethod( "privateT2SSIMap", new T1(), true, null );
+        testMethod( "privateT2SSIMap", new T2(), false, null );
+        testMethod( "privateT2SSIMap", new T2(), true, "privateT2SSIMap" );
+    }
+
+
+    public void test_packageT1()
+    {
+        testMethod( "packageT1", new T1(), false, null );
+        testMethod( "packageT1", new T1(), true, null );
+        testMethod( "packageT1", new T2(), false, null );
+        testMethod( "packageT1", new T2(), true, null );
+        testMethod( "packageT1", new T3(), false, null );
+        testMethod( "packageT1", new T3(), true, null );
+        testMethod( "packageT1", new T1a(), false, null );
+        testMethod( "packageT1", new T1a(), true, null );
+    }
+
+
+    public void test_packageT1SR()
+    {
+        testMethod( "packageT1SR", new T1(), false, null );
+        testMethod( "packageT1SR", new T1(), true, "packageT1SR" );
+        testMethod( "packageT1SR", new T2(), false, null );
+        testMethod( "packageT1SR", new T2(), true, null );
+        testMethod( "packageT1SR", new T3(), false, null );
+        testMethod( "packageT1SR", new T3(), true, null );
+        testMethod( "packageT1SR", new T1a(), false, null );
+        testMethod( "packageT1SR", new T1a(), true, "packageT1SR" );
+    }
+
+
+    public void test_packageT1SI()
+    {
+        testMethod( "packageT1SI", new T1(), false, null );
+        testMethod( "packageT1SI", new T1(), true, "packageT1SI" );
+        testMethod( "packageT1SI", new T2(), false, null );
+        testMethod( "packageT1SI", new T2(), true, null );
+        testMethod( "packageT1SI", new T3(), false, null );
+        testMethod( "packageT1SI", new T3(), true, null );
+        testMethod( "packageT1SI", new T1a(), false, null );
+        testMethod( "packageT1SI", new T1a(), true, "packageT1SI" );
+    }
+
+
+    public void test_packageT1SIMap()
+    {
+        testMethod( "packageT1SIMap", new T1(), false, null );
+        testMethod( "packageT1SIMap", new T1(), true, "packageT1SIMap" );
+        testMethod( "packageT1SIMap", new T2(), false, null );
+        testMethod( "packageT1SIMap", new T2(), true, null );
+        testMethod( "packageT1SIMap", new T3(), false, null );
+        testMethod( "packageT1SIMap", new T3(), true, null );
+        testMethod( "packageT1SIMap", new T1a(), false, null );
+        testMethod( "packageT1SIMap", new T1a(), true, "packageT1SIMap" );
+    }
+
+
+    public void test_packageT1SSI()
+    {
+        testMethod( "packageT1SSI", new T1(), false, null );
+        testMethod( "packageT1SSI", new T1(), true, "packageT1SSI" );
+        testMethod( "packageT1SSI", new T2(), false, null );
+        testMethod( "packageT1SSI", new T2(), true, null );
+        testMethod( "packageT1SSI", new T3(), false, null );
+        testMethod( "packageT1SSI", new T3(), true, null );
+        testMethod( "packageT1SSI", new T1a(), false, null );
+        testMethod( "packageT1SSI", new T1a(), true, "packageT1SSI" );
+    }
+
+
+    public void test_packageT1SSIMap()
+    {
+        testMethod( "packageT1SSIMap", new T1(), false, null );
+        testMethod( "packageT1SSIMap", new T1(), true, "packageT1SSIMap" );
+        testMethod( "packageT1SSIMap", new T2(), false, null );
+        testMethod( "packageT1SSIMap", new T2(), true, null );
+        testMethod( "packageT1SSIMap", new T3(), false, null );
+        testMethod( "packageT1SSIMap", new T3(), true, null );
+        testMethod( "packageT1SSIMap", new T1a(), false, null );
+        testMethod( "packageT1SSIMap", new T1a(), true, "packageT1SSIMap" );
+    }
+
+
+    public void test_packageT2()
+    {
+        testMethod( "packageT2", new T1(), false, null );
+        testMethod( "packageT2", new T1(), true, null );
+        testMethod( "packageT2", new T2(), false, null );
+        testMethod( "packageT2", new T2(), true, null );
+    }
+
+
+    public void test_packageT2SR()
+    {
+        testMethod( "packageT2SR", new T1(), false, null );
+        testMethod( "packageT2SR", new T1(), true, null );
+        testMethod( "packageT2SR", new T2(), false, null );
+        testMethod( "packageT2SR", new T2(), true, "packageT2SR" );
+    }
+
+
+    public void test_packageT2SI()
+    {
+        testMethod( "packageT2SI", new T1(), false, null );
+        testMethod( "packageT2SI", new T1(), true, null );
+        testMethod( "packageT2SI", new T2(), false, null );
+        testMethod( "packageT2SI", new T2(), true, "packageT2SI" );
+    }
+
+
+    public void test_packageT2SIMap()
+    {
+        testMethod( "packageT2SIMap", new T1(), false, null );
+        testMethod( "packageT2SIMap", new T1(), true, null );
+        testMethod( "packageT2SIMap", new T2(), false, null );
+        testMethod( "packageT2SIMap", new T2(), true, "packageT2SIMap" );
+    }
+
+
+    public void test_packageT2SSI()
+    {
+        testMethod( "packageT2SSI", new T1(), false, null );
+        testMethod( "packageT2SSI", new T1(), true, null );
+        testMethod( "packageT2SSI", new T2(), false, null );
+        testMethod( "packageT2SSI", new T2(), true, "packageT2SSI" );
+    }
+
+
+    public void test_packageT2SSIMap()
+    {
+        testMethod( "packageT2SSIMap", new T1(), false, null );
+        testMethod( "packageT2SSIMap", new T1(), true, null );
+        testMethod( "packageT2SSIMap", new T2(), false, null );
+        testMethod( "packageT2SSIMap", new T2(), true, "packageT2SSIMap" );
     }
 
 
     public void test_protectedT1()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "protectedT1", T1.class ).invoke( t1, m_service );
-        assertNull( t1.callPerformed );
+        testMethod( "protectedT1", new T1(), false, null );
+        testMethod( "protectedT1", new T1(), true, null );
+        testMethod( "protectedT1", new T2(), false, null );
+        testMethod( "protectedT1", new T2(), true, null );
     }
 
 
     public void test_protectedT1SR()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "protectedT1SR", T1.class ).invoke( t1, m_service );
-        assertEquals( "protectedT1SR", t1.callPerformed );
+        testMethod( "protectedT1SR", new T1(), false, "protectedT1SR" );
+        testMethod( "protectedT1SR", new T1(), true, "protectedT1SR" );
+        testMethod( "protectedT1SR", new T2(), false, "protectedT1SR" );
+        testMethod( "protectedT1SR", new T2(), true, "protectedT1SR" );
     }
 
 
     public void test_protectedT1SI()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "protectedT1SI", T1.class ).invoke( t1, m_service );
-        assertEquals( "protectedT1SI", t1.callPerformed );
+        testMethod( "protectedT1SI", new T1(), false, "protectedT1SI" );
+        testMethod( "protectedT1SI", new T1(), true, "protectedT1SI" );
+        testMethod( "protectedT1SI", new T2(), false, "protectedT1SI" );
+        testMethod( "protectedT1SI", new T2(), true, "protectedT1SI" );
     }
 
 
     public void test_protectedT1SSI()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "protectedT1SSI", T1.class ).invoke( t1, m_service );
-        assertEquals( "protectedT1SSI", t1.callPerformed );
-    }
-
-
-    public void test_protectedT1SSI_onT2()
-    {
-        System.out.println();
-        final T2 t2 = new T2();
-        createMethod( "protectedT1SSI", T2.class ).invoke( t2, m_service );
-        assertEquals( "protectedT1SSI", t2.callPerformed );
+        testMethod( "protectedT1SSI", new T1(), false, "protectedT1SSI" );
+        testMethod( "protectedT1SSI", new T1(), true, "protectedT1SSI" );
+        testMethod( "protectedT1SSI", new T2(), false, "protectedT1SSI" );
+        testMethod( "protectedT1SSI", new T2(), true, "protectedT1SSI" );
     }
 
 
     public void test_publicT1()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "publicT1", T1.class ).invoke( t1, m_service );
-        assertNull( t1.callPerformed );
+        testMethod( "publicT1", new T1(), false, null );
+        testMethod( "publicT1", new T1(), true, null );
+        testMethod( "publicT1", new T2(), false, null );
+        testMethod( "publicT1", new T2(), true, null );
     }
 
 
     public void test_publicT1SR()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "publicT1SR", T1.class ).invoke( t1, m_service );
-        assertEquals( "publicT1SR", t1.callPerformed );
-    }
-
-
-    public void test_publicT1SR_onT2()
-    {
-        System.out.println();
-        final T2 t2 = new T2();
-        createMethod( "publicT1SR", T2.class ).invoke( t2, m_service );
-        assertEquals( "publicT1SR", t2.callPerformed );
+        testMethod( "publicT1SR", new T1(), false, "publicT1SR" );
+        testMethod( "publicT1SR", new T1(), true, "publicT1SR" );
+        testMethod( "publicT1SR", new T2(), false, "publicT1SR" );
+        testMethod( "publicT1SR", new T2(), true, "publicT1SR" );
     }
 
 
     public void test_publicT1SI()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "publicT1SI", T1.class ).invoke( t1, m_service );
-        assertEquals( "publicT1SI", t1.callPerformed );
+        testMethod( "publicT1SI", new T1(), false, "publicT1SI" );
+        testMethod( "publicT1SI", new T1(), true, "publicT1SI" );
+        testMethod( "publicT1SI", new T2(), false, "publicT1SI" );
+        testMethod( "publicT1SI", new T2(), true, "publicT1SI" );
     }
 
 
     public void test_publicT1SIMap()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "publicT1SIMap", T1.class ).invoke( t1, m_service );
-        assertEquals( "publicT1SIMap", t1.callPerformed );
-    }
-
-
-    public void test_publicT1SI_onT2()
-    {
-        System.out.println();
-        final T2 t2 = new T2();
-        createMethod( "publicT1SI", T2.class ).invoke( t2, m_service );
-        assertEquals( "publicT1SI", t2.callPerformed );
+        testMethod( "publicT1SIMap", new T1(), false, null );
+        testMethod( "publicT1SIMap", new T1(), true, "publicT1SIMap" );
+        testMethod( "publicT1SIMap", new T2(), false, null );
+        testMethod( "publicT1SIMap", new T2(), true, "publicT1SIMap" );
     }
 
 
     public void test_publicT1SSI()
     {
-        System.out.println();
-        final T1 t1 = new T1();
-        createMethod( "publicT1SSI", T1.class ).invoke( t1, m_service );
-        assertEquals( "publicT1SSI", t1.callPerformed );
+        testMethod( "publicT1SSI", new T1(), false, "publicT1SSI" );
+        testMethod( "publicT1SSI", new T1(), true, "publicT1SSI" );
+        testMethod( "publicT1SSI", new T2(), false, "publicT1SSI" );
+        testMethod( "publicT1SSI", new T2(), true, "publicT1SSI" );
     }
 
 
-    public void test_publicT1SSI_onT2()
+    public void test_publicT1SSIMap()
     {
-        System.out.println();
-        final T2 t2 = new T2();
-        createMethod( "publicT1SSI", T2.class ).invoke( t2, m_service );
-        assertEquals( "publicT1SSI", t2.callPerformed );
-    }
-
-    private static interface SuperFakeService
-    {
-
-    }
-
-    private static interface FakeService extends SuperFakeService
-    {
-
-    }
-
-    private static class T1
-    {
-
-        String callPerformed = null;
-
-
-        private void privateT1()
-        {
-            callPerformed = "privateT1";
-        }
-
-
-        protected void protectedT1()
-        {
-            callPerformed = "protectedT1";
-        }
-
-
-        protected void protectedT1SR( ServiceReference sr )
-        {
-            if ( sr != null )
-            {
-                callPerformed = "protectedT1SR";
-            }
-            else
-            {
-                callPerformed = "protectedT1SR with null param";
-            }
-        }
-
-
-        protected void protectedT1SI( FakeService si )
-        {
-            if ( si != null )
-            {
-                callPerformed = "protectedT1SI";
-            }
-            else
-            {
-                callPerformed = "protectedT1SI with null param";
-            }
-        }
-
-
-        protected void protectedT1SSI( SuperFakeService si )
-        {
-            if ( si != null )
-            {
-                callPerformed = "protectedT1SSI";
-            }
-            else
-            {
-                callPerformed = "protectedT1SSI with null param";
-            }
-        }
-
-
-        protected void publicT1()
-        {
-            callPerformed = "publicT1";
-        }
-
-
-        public void publicT1SR( ServiceReference sr )
-        {
-            if ( sr != null )
-            {
-                callPerformed = "publicT1SR";
-            }
-            else
-            {
-                callPerformed = "publicT1SR with null param";
-            }
-        }
-
-
-        public void publicT1SI( FakeService si )
-        {
-            if ( si != null )
-            {
-                callPerformed = "publicT1SI";
-            }
-            else
-            {
-                callPerformed = "publicT1SI with null param";
-            }
-        }
-
-
-        public void publicT1SIMap( FakeService si, Map props )
-        {
-            if ( si != null && props != null && props.size() > 0 )
-            {
-                callPerformed = "publicT1SIMap";
-            }
-            else if ( si == null )
-            {
-                callPerformed = "publicT1SIMap with null service instance";
-            }
-            else if ( props == null )
-            {
-                callPerformed = "publicT1SIMap with null props";
-            }
-            else
-            {
-                callPerformed = "publicT1SIMap with empty props";
-            }
-
-        }
-
-
-        public void publicT1SSI( SuperFakeService si )
-        {
-            if ( si != null )
-            {
-                callPerformed = "publicT1SSI";
-            }
-            else
-            {
-                callPerformed = "publicT1SSI with null param";
-            }
-        }
-    }
-
-    private class T2 extends T1
-    {
-
+        testMethod( "publicT1SSIMap", new T1(), false, null );
+        testMethod( "publicT1SSIMap", new T1(), true, "publicT1SSIMap" );
+        testMethod( "publicT1SSIMap", new T2(), false, null );
+        testMethod( "publicT1SSIMap", new T2(), true, "publicT1SSIMap" );
     }
 
 
-    public BindMethod createMethod( final String methodName, final Class componentClass )
+    public void test_suitable()
     {
-        return new BindMethod( methodName, componentClass, "reference", FakeService.class.getName(), new SysOutLogger() );
+        // T1 should use its own public implementation
+        testMethod( "suitable", new T1(), false, "suitableT1" );
+        testMethod( "suitable", new T1(), true, "suitableT1" );
+
+        // T2's private implementation is only visible for DS 1.1
+        testMethod( "suitable", new T2(), false, null );
+        testMethod( "suitable", new T2(), true, "suitableT2" );
+
+        // T3 extends T2 and cannot see T2's private method
+        testMethod( "suitable", new T3(), false, null );
+        testMethod( "suitable", new T3(), true, null );
+
+        // T1a extends T1 and uses T1's public method
+        testMethod( "suitable", new T1a(), false, "suitableT1" );
+        testMethod( "suitable", new T1a(), true, "suitableT1" );
+    }
+
+
+    private void testMethod( final String methodName, final T1 component, final boolean isDS11,
+        final String expectCallPerformed )
+    {
+        BindMethod bm = new BindMethod( isDS11, methodName, component.getClass(), "reference", FakeService.class
+            .getName(), new SysOutLogger() );
+        bm.invoke( component, m_service );
+        assertEquals( expectCallPerformed, component.callPerformed );
     }
 
     private static class SysOutLogger implements BindMethod.Logger
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/components/FakeService.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/FakeService.java
new file mode 100644
index 0000000..bf42066
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/FakeService.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.manager.components;
+
+
+public interface FakeService extends SuperFakeService
+{
+
+}
\ No newline at end of file
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/components/SuperFakeService.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/SuperFakeService.java
new file mode 100644
index 0000000..a754977
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/SuperFakeService.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.manager.components;
+
+public interface SuperFakeService
+{
+
+}
\ No newline at end of file
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T1.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T1.java
new file mode 100644
index 0000000..f4d7364
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T1.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.manager.components;
+
+
+import java.util.Map;
+
+import org.osgi.framework.ServiceReference;
+
+
+public class T1
+{
+
+    public String callPerformed = null;
+
+
+    private void privateT1()
+    {
+        callPerformed = "privateT1";
+    }
+
+
+    private void privateT1SR( ServiceReference sr )
+    {
+        if ( sr != null )
+        {
+            callPerformed = "privateT1SR";
+        }
+        else
+        {
+            callPerformed = "privateT1SR with null param";
+        }
+    }
+
+
+    private void privateT1SI( FakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "privateT1SI";
+        }
+        else
+        {
+            callPerformed = "privateT1SI with null param";
+        }
+    }
+
+
+    private void privateT1SIMap( FakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "privateT1SIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "privateT1SIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "privateT1SIMap with null props";
+        }
+        else
+        {
+            callPerformed = "privateT1SIMap with empty props";
+        }
+    }
+
+
+    private void privateT1SSI( SuperFakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "privateT1SSI";
+        }
+        else
+        {
+            callPerformed = "privateT1SSI with null param";
+        }
+    }
+
+
+    private void privateT1SSIMap( SuperFakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "privateT1SSIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "privateT1SSIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "privateT1SSIMap with null props";
+        }
+        else
+        {
+            callPerformed = "privateT1SSIMap with empty props";
+        }
+    }
+
+
+    void packageT1()
+    {
+        callPerformed = "packageT1";
+    }
+
+
+    void packageT1SR( ServiceReference sr )
+    {
+        if ( sr != null )
+        {
+            callPerformed = "packageT1SR";
+        }
+        else
+        {
+            callPerformed = "packageT1SR with null param";
+        }
+    }
+
+
+    void packageT1SI( FakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "packageT1SI";
+        }
+        else
+        {
+            callPerformed = "packageT1SI with null param";
+        }
+    }
+
+
+    void packageT1SIMap( FakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "packageT1SIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "packageT1SIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "packageT1SIMap with null props";
+        }
+        else
+        {
+            callPerformed = "packageT1SIMap with empty props";
+        }
+    }
+
+
+    void packageT1SSI( SuperFakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "packageT1SSI";
+        }
+        else
+        {
+            callPerformed = "packageT1SSI with null param";
+        }
+    }
+
+
+    void packageT1SSIMap( SuperFakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "packageT1SSIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "packageT1SSIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "packageT1SSIMap with null props";
+        }
+        else
+        {
+            callPerformed = "packageT1SSIMap with empty props";
+        }
+    }
+
+
+    protected void protectedT1()
+    {
+        callPerformed = "protectedT1";
+    }
+
+
+    protected void protectedT1SR( ServiceReference sr )
+    {
+        if ( sr != null )
+        {
+            callPerformed = "protectedT1SR";
+        }
+        else
+        {
+            callPerformed = "protectedT1SR with null param";
+        }
+    }
+
+
+    protected void protectedT1SI( FakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "protectedT1SI";
+        }
+        else
+        {
+            callPerformed = "protectedT1SI with null param";
+        }
+    }
+
+
+    protected void protectedT1SIMap( FakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "protectedT1SIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "protectedT1SIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "protectedT1SIMap with null props";
+        }
+        else
+        {
+            callPerformed = "protectedT1SIMap with empty props";
+        }
+    }
+
+
+    protected void protectedT1SSI( SuperFakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "protectedT1SSI";
+        }
+        else
+        {
+            callPerformed = "protectedT1SSI with null param";
+        }
+    }
+
+
+    protected void protectedT1SSIMap( SuperFakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "protectedT1SSIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "protectedT1SSIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "protectedT1SSIMap with null props";
+        }
+        else
+        {
+            callPerformed = "protectedT1SSIMap with empty props";
+        }
+    }
+
+
+    public void publicT1()
+    {
+        callPerformed = "publicT1";
+    }
+
+
+    public void publicT1SR( ServiceReference sr )
+    {
+        if ( sr != null )
+        {
+            callPerformed = "publicT1SR";
+        }
+        else
+        {
+            callPerformed = "publicT1SR with null param";
+        }
+    }
+
+
+    public void publicT1SI( FakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "publicT1SI";
+        }
+        else
+        {
+            callPerformed = "publicT1SI with null param";
+        }
+    }
+
+
+    public void publicT1SIMap( FakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "publicT1SIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "publicT1SIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "publicT1SIMap with null props";
+        }
+        else
+        {
+            callPerformed = "publicT1SIMap with empty props";
+        }
+    }
+
+
+    public void publicT1SSI( SuperFakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "publicT1SSI";
+        }
+        else
+        {
+            callPerformed = "publicT1SSI with null param";
+        }
+    }
+
+
+    public void publicT1SSIMap( SuperFakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "publicT1SSIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "publicT1SSIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "publicT1SSIMap with null props";
+        }
+        else
+        {
+            callPerformed = "publicT1SSIMap with empty props";
+        }
+    }
+
+
+    public void suitable( ServiceReference sr )
+    {
+        callPerformed = "suitableT1";
+    }
+}
\ No newline at end of file
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T1a.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T1a.java
new file mode 100644
index 0000000..ffa6beb
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T1a.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.manager.components;
+
+
+public class T1a extends T1
+{
+
+    // this class sees package private methods from T1
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T3.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T3.java
new file mode 100644
index 0000000..587c5e3
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/components/T3.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.manager.components;
+
+
+import org.apache.felix.scr.impl.manager.components2.T2;
+
+
+public class T3 extends T2
+{
+
+    // this class does not see any package private methods of other classes
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/manager/components2/T2.java b/scr/src/test/java/org/apache/felix/scr/impl/manager/components2/T2.java
new file mode 100644
index 0000000..a093af4
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/components2/T2.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.manager.components2;
+
+
+import java.util.Map;
+
+import org.apache.felix.scr.impl.manager.components.FakeService;
+import org.apache.felix.scr.impl.manager.components.SuperFakeService;
+import org.apache.felix.scr.impl.manager.components.T1;
+import org.osgi.framework.ServiceReference;
+
+
+public class T2 extends T1
+{
+    private void privateT2()
+    {
+        callPerformed = "privateT2";
+    }
+
+
+    private void privateT2SR( ServiceReference sr )
+    {
+        if ( sr != null )
+        {
+            callPerformed = "privateT2SR";
+        }
+        else
+        {
+            callPerformed = "privateT2SR with null param";
+        }
+    }
+
+
+    private void privateT2SI( FakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "privateT2SI";
+        }
+        else
+        {
+            callPerformed = "privateT2SI with null param";
+        }
+    }
+
+
+    private void privateT2SIMap( FakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "privateT2SIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "privateT2SIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "privateT2SIMap with null props";
+        }
+        else
+        {
+            callPerformed = "privateT2SIMap with empty props";
+        }
+    }
+
+
+    private void privateT2SSI( SuperFakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "privateT2SSI";
+        }
+        else
+        {
+            callPerformed = "privateT2SSI with null param";
+        }
+    }
+
+
+    private void privateT2SSIMap( SuperFakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "privateT2SSIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "privateT2SSIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "privateT2SSIMap with null props";
+        }
+        else
+        {
+            callPerformed = "privateT2SSIMap with empty props";
+        }
+    }
+
+
+    void packageT2()
+    {
+        callPerformed = "packageT2";
+    }
+
+
+    void packageT2SR( ServiceReference sr )
+    {
+        if ( sr != null )
+        {
+            callPerformed = "packageT2SR";
+        }
+        else
+        {
+            callPerformed = "packageT2SR with null param";
+        }
+    }
+
+
+    void packageT2SI( FakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "packageT2SI";
+        }
+        else
+        {
+            callPerformed = "packageT2SI with null param";
+        }
+    }
+
+
+    void packageT2SIMap( FakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "packageT2SIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "packageT2SIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "packageT2SIMap with null props";
+        }
+        else
+        {
+            callPerformed = "packageT2SIMap with empty props";
+        }
+    }
+
+
+    void packageT2SSI( SuperFakeService si )
+    {
+        if ( si != null )
+        {
+            callPerformed = "packageT2SSI";
+        }
+        else
+        {
+            callPerformed = "packageT2SSI with null param";
+        }
+    }
+
+
+    void packageT2SSIMap( SuperFakeService si, Map props )
+    {
+        if ( si != null && props != null && props.size() > 0 )
+        {
+            callPerformed = "packageT2SSIMap";
+        }
+        else if ( si == null )
+        {
+            callPerformed = "packageT2SSIMap with null service instance";
+        }
+        else if ( props == null )
+        {
+            callPerformed = "packageT2SSIMap with null props";
+        }
+        else
+        {
+            callPerformed = "packageT2SSIMap with empty props";
+        }
+    }
+
+
+    // this method must hide the T1#suitable method !
+    private void suitable( FakeService si )
+    {
+        callPerformed = "suitableT2";
+    }
+}
\ No newline at end of file