FELIX-925 Allow activate and deactivate methods to have different signatures
 * Refactored reflection use for finding and invoking methods
 * Allow for private and package private methods
 * Unit tests

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@785125 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/AbstractComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/AbstractComponentManager.java
index bd6dc97..759014d 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/AbstractComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/AbstractComponentManager.java
@@ -18,9 +18,6 @@
  */
 package org.apache.felix.scr.impl;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Enumeration;
@@ -961,66 +958,4 @@
 
         m_state = newState;
     }
-
-    /**
-     * Finds the named public or protected method in the given class or any
-     * super class. If such a method is found, its accessibility is enfored by
-     * calling the <code>Method.setAccessible</code> method if required and
-     * the method is returned. Enforcing accessibility is required to support
-     * invocation of protected methods.
-     *
-     * @param clazz The <code>Class</code> which provides the method.
-     * @param name The name of the method.
-     * @param parameterTypes The parameters to the method. Passing
-     *      <code>null</code> is equivalent to using an empty array.
-     * @param only Whether to only look at the declared methods of the given
-     *      class or also inspect the super classes.
-     *
-     * @return The named method with enforced accessibility
-     *
-     * @throws NoSuchMethodException If no public or protected method with
-     *      the given name can be found in the class or any of its super classes.
-     * @throws InvocationTargetException If an unexpected Throwable is caught
-     *      trying to access the desired method.
-     */
-    static Method getMethod( Class clazz, String name, Class[] parameterTypes, boolean only )
-            throws NoSuchMethodException, InvocationTargetException
-    {
-        for ( ; clazz != null; clazz = clazz.getSuperclass() )
-        {
-            try
-            {
-                // find the declared method in this class
-                Method method = clazz.getDeclaredMethod( name, parameterTypes );
-
-                // accept public and protected methods only and ensure accessibility
-                if ( Modifier.isPublic( method.getModifiers() ) || Modifier.isProtected( method.getModifiers()) )
-                {
-                    method.setAccessible( true );
-                    return method;
-                }
-
-                // if only the clazz is to be scanned terminate here
-                if ( only )
-                {
-                    break;
-                }
-            }
-            catch ( NoSuchMethodException nsme )
-            {
-                // ignore for now
-            }
-            catch ( Throwable throwable )
-            {
-                // unexpected problem accessing the method, don't let everything
-                // blow up in this situation, just throw a declared exception
-                throw new InvocationTargetException( throwable,
-                        "Unexpected problem trying to get method " + name );
-            }
-        }
-
-        // walked up the complete super class hierarchy and still not found
-        // anything, sigh ...
-        throw new NoSuchMethodException( name );
-    }
 }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java
index 90e90f3..2107196 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/DependencyManager.java
@@ -72,7 +72,7 @@
 
     // the object on which the bind/undind methods are to be called
     private transient Object m_componentInstance;
-    
+
     // the bind method
     private Method m_bind;
 
@@ -90,7 +90,7 @@
 
     // the target service filter
     private Filter m_targetFilter;
-    
+
 
     /**
      * Constructor that receives several parameters.
@@ -129,7 +129,7 @@
             case ServiceEvent.REGISTERED:
                 m_componentManager.log( LogService.LOG_DEBUG, "Dependency Manager: Adding " + serviceString,
                     m_componentManager.getComponentMetadata(), null );
-                
+
                 // consider the service if the filter matches
                 if ( targetFilterMatch( ref ) )
                 {
@@ -147,10 +147,10 @@
             case ServiceEvent.MODIFIED:
                 m_componentManager.log( LogService.LOG_DEBUG, "Dependency Manager: Updating " + serviceString,
                     m_componentManager.getComponentMetadata(), null );
-                
+
                 // remove the service first
                 serviceRemoved( ref );
-                
+
                 // recalculate the number of services matching the filter
                 // because we don't know whether this service previously matched
                 // or not
@@ -164,7 +164,7 @@
             case ServiceEvent.UNREGISTERING:
                 m_componentManager.log( LogService.LOG_DEBUG, "Dependency Manager: Removing " + serviceString,
                     m_componentManager.getComponentMetadata(), null );
-                
+
                 // manage the service counter if the filter matchs
                 if ( targetFilterMatch( ref ) )
                 {
@@ -181,7 +181,7 @@
                 // remove the service ignoring the filter match because if the
                 // service is bound, it has to be removed no matter what
                 serviceRemoved( ref );
-                
+
                 break;
         }
     }
@@ -227,7 +227,7 @@
                     m_componentManager.log( LogService.LOG_DEBUG, "Dependency Manager: Service "
                         + m_dependencyMetadata.getName() + " registered, reactivate component", m_componentManager
                         .getComponentMetadata(), null );
-    
+
                     m_componentManager.reactivate();
                 }
                 else
@@ -246,7 +246,7 @@
                     }
                 }
             }
-    
+
             // otherwise bind if we have a bind method and the service needs
             // be bound
             else if ( m_dependencyMetadata.getBind() != null )
@@ -267,15 +267,15 @@
                     {
                         // bind the service, getting it if required
                         invokeBindMethod( reference );
-    
+
                         // unbind the old service reference
                         unbind( boundRefs );
                     }
                 }
             }
         }
-        
-        else 
+
+        else
         {
             m_componentManager.log( LogService.LOG_DEBUG,
 					"Dependency Manager: Ignoring service addition, wrong state "
@@ -283,8 +283,8 @@
 					m_componentManager.getComponentMetadata(), null );
         }
     }
-    
-    
+
+
     /**
      * Called by the {@link #serviceChanged(ServiceEvent)} method if an existing
      * service is unregistered from the system or if a registered service has
@@ -322,11 +322,11 @@
                         "Dependency Manager: Deactivating component due to mandatory dependency on "
                             + m_dependencyMetadata.getName() + "/" + m_dependencyMetadata.getInterface()
                             + " not satisfied", m_componentManager.getComponentMetadata(), null );
-    
+
                 // deactivate the component now
                 m_componentManager.deactivateInternal();
             }
-    
+
             // if the dependency is static, we have to reactivate the component
             // to "remove" the dependency
             else if ( m_dependencyMetadata.isStatic() )
@@ -345,11 +345,11 @@
                         m_componentManager.getComponentMetadata(), ex );
                 }
             }
-    
+
             // dynamic dependency, multiple or single but this service is the bound one
             else
             {
-    
+
                 // try to bind a replacement service first if this is a unary
                 // cardinality reference and a replacement is available.
                 if ( !m_dependencyMetadata.isMultiple() )
@@ -363,24 +363,24 @@
                                 + m_dependencyMetadata.getName() + "/" + m_dependencyMetadata.getInterface()
                                 + " not satisfied", m_componentManager.getComponentMetadata(), null );
                         m_componentManager.deactivateInternal();
-    
+
                         // abort here we do not need to do more
                         return;
                     }
                 }
-    
+
                 // call the unbind method if one is defined
                 if ( m_dependencyMetadata.getUnbind() != null )
                 {
                     invokeUnbindMethod( reference );
                 }
-                
+
                 // make sure the service is returned
                 ungetService( reference );
             }
         }
-        
-        else 
+
+        else
         {
             m_componentManager.log( LogService.LOG_DEBUG,
 					"Dependency Manager: Ignoring service removal, wrong state "
@@ -388,8 +388,8 @@
 					m_componentManager.getComponentMetadata(), null );
         }
     }
-    
-    
+
+
     private boolean handleServiceEvent()
     {
         return ( m_componentManager.getState() & STATE_MASK ) != 0;
@@ -460,7 +460,7 @@
         m_componentManager.log( LogService.LOG_DEBUG, "Registered for service events, currently " + m_size
             + " service(s) match the filter", m_componentManager.getComponentMetadata(), null );
     }
-    
+
     /**
      * Disposes off this dependency manager by removing as a service listener
      * and ungetting all services, which are still kept in the list of our
@@ -635,12 +635,12 @@
      * ranking as specified by the service.ranking property. If both services
      * have the same service ranking, then the service with the lowest service
      * ID as specified by the service.id property is chosen.
-     * 
+     *
      * @param newReference The ServiceReference representing the newly added
      *      Service
      * @param oldReference The ServiceReference representing the service which
      *      is already bound to the component
-     *      
+     *
      * @return <code>true</code> if <code>newReference</code> has higher ranking
      */
     private boolean isHigher( ServiceReference newReference, ServiceReference oldReference )
@@ -680,7 +680,7 @@
      * if the property exists and is of type <code>java.lang.Integer</code>. If
      * the property does not exist or is of another type, zero is returned as
      * the default value for service ranking.
-     * 
+     *
      * @param serviceReference The Service reference whose ranking is to be
      *          returned.
      */
@@ -697,10 +697,10 @@
 
     /**
      * Returns the value of the <code>service.id</code> service property.
-     * 
+     *
      * @param serviceReference The Service reference whose service id is to be
      *          returned.
-     *          
+     *
      * @throws ClassCastException if the <code>service.id</code> property exists
      *          but is not a <code>java.lang.Long</code> value. This is not
      *          expected since the framework should guarantee this property and
@@ -990,8 +990,8 @@
         try
         {
             // Case 1 - ServiceReference parameter
-            return AbstractComponentManager.getMethod( targetClass, methodname, new Class[]
-                { SERVICE_REFERENCE_CLASS }, true );
+            return ReflectionHelper.getMethod( targetClass, methodname, new Class[]
+                { SERVICE_REFERENCE_CLASS }, false, false );
         }
         catch ( NoSuchMethodException ex )
         {
@@ -1009,8 +1009,8 @@
                 }
 
                 parameterClass = loader.loadClass( parameterClassName );
-                return AbstractComponentManager.getMethod( targetClass, methodname, new Class[]
-                    { parameterClass }, true );
+                return ReflectionHelper.getMethod( targetClass, methodname, new Class[]
+                    { parameterClass }, false, false );
             }
             catch ( NoSuchMethodException ex2 )
             {
@@ -1062,6 +1062,10 @@
                 // if we can't load the class, perhaps the method is declared in a super class
                 // so we try this class next
             }
+
+
+            // TODO: Case 4: same as case 2, but + Map param
+            // TODO: Case 5: same as case 3, but + Map param
         }
 
         // if we get here, we have no method, so check the super class
@@ -1289,7 +1293,7 @@
         }
     }
 
-    
+
     //------------- Service target filter support -----------------------------
 
     /**
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ImmediateComponentManager.java b/scr/src/main/java/org/apache/felix/scr/impl/ImmediateComponentManager.java
index 23a9166..f56ea18 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/ImmediateComponentManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ImmediateComponentManager.java
@@ -24,7 +24,9 @@
 import java.util.Dictionary;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
+import org.osgi.framework.BundleContext;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.component.ComponentConstants;
 import org.osgi.service.component.ComponentContext;
@@ -37,12 +39,51 @@
  */
 class ImmediateComponentManager extends AbstractComponentManager
 {
+    private static final Class COMPONENT_CONTEXT_CLASS = ComponentContext.class;
+
+    private static final Class BUNDLE_CONTEXT_CLASS = BundleContext.class;
+
+    private static final Class MAP_CLASS = Map.class;
+
+    // this is an internal field made available only to the unit tests
+    static final Class[][] ACTIVATE_PARAMETER_LIST = {
+        { COMPONENT_CONTEXT_CLASS },
+        { BUNDLE_CONTEXT_CLASS },
+        { MAP_CLASS },
+
+        { COMPONENT_CONTEXT_CLASS, BUNDLE_CONTEXT_CLASS },
+        { COMPONENT_CONTEXT_CLASS, MAP_CLASS },
+
+        { BUNDLE_CONTEXT_CLASS, COMPONENT_CONTEXT_CLASS },
+        { BUNDLE_CONTEXT_CLASS, MAP_CLASS },
+
+        { MAP_CLASS, COMPONENT_CONTEXT_CLASS },
+        { MAP_CLASS, BUNDLE_CONTEXT_CLASS },
+
+        { COMPONENT_CONTEXT_CLASS, BUNDLE_CONTEXT_CLASS, MAP_CLASS },
+        { COMPONENT_CONTEXT_CLASS, MAP_CLASS, BUNDLE_CONTEXT_CLASS },
+
+        { BUNDLE_CONTEXT_CLASS, COMPONENT_CONTEXT_CLASS, MAP_CLASS },
+        { BUNDLE_CONTEXT_CLASS, MAP_CLASS, COMPONENT_CONTEXT_CLASS },
+
+        { MAP_CLASS, COMPONENT_CONTEXT_CLASS, BUNDLE_CONTEXT_CLASS },
+        { MAP_CLASS, BUNDLE_CONTEXT_CLASS, COMPONENT_CONTEXT_CLASS },
+
+        {}
+    };
+
     // The object that implements the service and that is bound to other services
     private Object m_implementationObject;
 
     // The context that will be passed to the implementationObject
     private ComponentContext m_componentContext;
 
+    // the activate method
+    private Method activateMethod = ReflectionHelper.SENTINEL;
+
+    // the deactivate method
+    private Method deactivateMethod = ReflectionHelper.SENTINEL;
+
     // optional properties provided in the ComponentFactory.newInstance method
     private Dictionary m_factoryProperties;
 
@@ -186,34 +227,17 @@
             }
         }
 
+        // get the method
+        if ( activateMethod == ReflectionHelper.SENTINEL )
+        {
+            activateMethod = getMethod( implementationObject, getComponentMetadata().getActivate() );
+        }
+
         // 4. Call the activate method, if present
-        // Search for the activate method
-        final String activateMethodName = getComponentMetadata().getActivate();
-        try
-        {
-            Method activateMethod = getMethod( implementationObject.getClass(), activateMethodName, new Class[]
-                    { ComponentContext.class }, false );
-            activateMethod.invoke( implementationObject, new Object[]
-                { componentContext } );
-        }
-        catch ( NoSuchMethodException ex )
-        {
-            // We can safely ignore this one
-            log( LogService.LOG_DEBUG, activateMethodName + " method is not implemented", getComponentMetadata(), null );
-        }
-        catch ( IllegalAccessException ex )
-        {
-            // Ignored, but should it be logged?
-            log( LogService.LOG_DEBUG, activateMethodName + " method cannot be called", getComponentMetadata(), null );
-        }
-        catch ( InvocationTargetException ex )
+        if ( activateMethod != null && !invokeMethod( activateMethod, implementationObject, componentContext ) )
         {
             // 112.5.8 If the activate method throws an exception, SCR must log an error message
             // containing the exception with the Log Service and activation fails
-            log( LogService.LOG_ERROR, "The " + activateMethodName + " method has thrown an exception",
-                getComponentMetadata(), ex.getCause() );
-
-            // make sure, we keep no bindings
             it = getDependencyManagers();
             while ( it.hasNext() )
             {
@@ -221,7 +245,7 @@
                 dm.close();
             }
 
-            return null;
+            implementationObject = null;
         }
 
         return implementationObject;
@@ -231,34 +255,17 @@
     protected void disposeImplementationObject( Object implementationObject, ComponentContext componentContext )
     {
 
+        // get the method
+        if ( deactivateMethod == ReflectionHelper.SENTINEL )
+        {
+            deactivateMethod = getMethod( implementationObject, getComponentMetadata().getDeactivate() );
+        }
+
         // 1. Call the deactivate method, if present
-        // Search for the activate method
-        final String deactivateMethodName = getComponentMetadata().getDeactivate();
-        try
-        {
-            Method deactivateMethod = getMethod( implementationObject.getClass(), deactivateMethodName, new Class[]
-                { ComponentContext.class }, false );
-            deactivateMethod.invoke( implementationObject, new Object[]
-                { componentContext } );
-        }
-        catch ( NoSuchMethodException ex )
-        {
-            // We can safely ignore this one
-            log( LogService.LOG_DEBUG, deactivateMethodName + " method is not implemented", getComponentMetadata(),
-                null );
-        }
-        catch ( IllegalAccessException ex )
-        {
-            // Ignored, but should it be logged?
-            log( LogService.LOG_DEBUG, deactivateMethodName + " method cannot be called", getComponentMetadata(), null );
-        }
-        catch ( InvocationTargetException ex )
-        {
-            // 112.5.12 If the deactivate method throws an exception, SCR must log an error message
-            // containing the exception with the Log Service and continue
-            log( LogService.LOG_ERROR, "The " + deactivateMethodName + " method has thrown an exception",
-                getComponentMetadata(), ex.getCause() );
-        }
+        // don't care for the result, the error (acccording to 112.5.12 If the deactivate
+        // method throws an exception, SCR must log an error message containing the
+        // exception with the Log Service and continue) has already been logged
+        invokeMethod( deactivateMethod, implementationObject, componentContext );
 
         // 2. Unbind any bound services
         Iterator it = getDependencyManagers();
@@ -382,4 +389,104 @@
             reactivate();
         }
     }
+
+
+    /**
+     * Find the method with the given name in the class hierarchy of the
+     * implementation object's class. This method looks for methods which have
+     * one of the parameter lists of the {@link #ACTIVATE_PARAMETER_LIST} array.
+     *
+     * @param implementationObject The object whose class (and its super classes)
+     *      may provide the method
+     * @param methodName Name of the method to look for
+     * @return The named method or <code>null</code> if no such method is available.
+     */
+    private Method getMethod( final Object implementationObject, final String methodName )
+    {
+        try
+        {
+            return ReflectionHelper.getMethod( implementationObject.getClass(), methodName, ACTIVATE_PARAMETER_LIST );
+        }
+        catch ( InvocationTargetException ite )
+        {
+            // We can safely ignore this one
+            log( LogService.LOG_WARNING, methodName + " cannot be found", getComponentMetadata(), ite
+                .getTargetException() );
+        }
+        catch ( NoSuchMethodException ex )
+        {
+            // We can safely ignore this one
+            log( LogService.LOG_DEBUG, methodName + " method is not implemented", getComponentMetadata(), null );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Invokes the given method on the <code>implementationObject</code> using
+     * the <code>componentContext</code> as the base to create method argument
+     * list.
+     *
+     * @param method The method to call. This method must already have been
+     *      made accessible by calling
+     *      <code>Method.setAccessible(boolean)</code>.
+     * @param implementationObject The object on which to call the method.
+     * @param componentContext The <code>ComponentContext</code> used to
+     *      build the argument list
+     *
+     * @return <code>true</code> if the method should be considered invoked
+     *      successfully. <code>false</code> is returned if the method threw
+     *      an exception.
+     *
+     * @throws NullPointerException if any of the parameters is <code>null</code>.
+     */
+    private boolean invokeMethod( final Method method, final Object implementationObject,
+        final ComponentContext componentContext )
+    {
+        final String methodName = method.getName();
+        try
+        {
+            // build argument list
+            Class[] paramTypes = method.getParameterTypes();
+            Object[] param = new Object[paramTypes.length];
+            for ( int i = 0; i < param.length; i++ )
+            {
+                if ( paramTypes[i] == COMPONENT_CONTEXT_CLASS )
+                {
+                    param[i] = componentContext;
+                }
+                else if ( paramTypes[i] == BUNDLE_CONTEXT_CLASS )
+                {
+                    param[i] = componentContext.getBundleContext();
+                }
+                else if ( paramTypes[i] == MAP_CLASS )
+                {
+                    // note: getProperties() returns a Hashtable which is a Map
+                    param[i] = componentContext.getProperties();
+                }
+            }
+
+            method.invoke( implementationObject, new Object[]
+                { componentContext } );
+        }
+        catch ( IllegalAccessException ex )
+        {
+            // Ignored, but should it be logged?
+            log( LogService.LOG_DEBUG, methodName + " method cannot be called", getComponentMetadata(), null );
+        }
+        catch ( InvocationTargetException ex )
+        {
+            // 112.5.8 If the activate method throws an exception, SCR must log an error message
+            // containing the exception with the Log Service and activation fails
+            log( LogService.LOG_ERROR, "The " + methodName + " method has thrown an exception", getComponentMetadata(),
+                ex.getCause() );
+
+            // method threw, so it was a failure
+            return false;
+        }
+
+        // assume success (also if the method is not available or accessible)
+        return true;
+    }
 }
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ReflectionHelper.java b/scr/src/main/java/org/apache/felix/scr/impl/ReflectionHelper.java
new file mode 100644
index 0000000..2c33f52
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ReflectionHelper.java
@@ -0,0 +1,244 @@
+/*
+ * 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;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+
+/**
+ * The <code>ReflectionHelper</code> class provides utility methods to find out
+ * about binding and activation methods in components.
+ */
+final class ReflectionHelper
+{
+
+    static final Method SENTINEL;
+
+    static
+    {
+        Method tmpSentinel = null;
+        try
+        {
+            tmpSentinel = ReflectionHelper.class.getDeclaredMethod( "sentinel", null );
+        }
+        catch ( Throwable t )
+        {
+            // don't care for the reason
+        }
+
+        SENTINEL = tmpSentinel;
+    }
+
+
+    // sentinel method used to assign to the SENTINEL field
+    private static final void sentinel()
+    {
+    }
+
+
+    /**
+     * Finds the named public or protected method in the given class or any
+     * super class. If such a method is found, its accessibility is enfored by
+     * calling the <code>Method.setAccessible</code> method if required and
+     * the method is returned. Enforcing accessibility is required to support
+     * invocation of protected methods.
+     *
+     * @param objectClass The <code>Class</code> at which to start looking for
+     *      a matching method
+     * @param name The name of the method.
+     * @param parameterTypes The list of suitable parmameters. Each class is
+     *      is asked for a declared method of the given name with parameters
+     *      from this list.
+     * @param only Whether to only look at the declared methods of the given
+     *      class or also inspect the super classes.
+     *
+     * @return The named method with enforced accessibility
+     *
+     * @throws NoSuchMethodException If no public or protected method with
+     *      the given name can be found in the class or any of its super classes.
+     * @throws InvocationTargetException If an unexpected Throwable is caught
+     *      trying to access the desired method.
+     */
+    static Method getMethod( final Class objectClass, final String name, final Class[][] parameterTypesList )
+        throws NoSuchMethodException, InvocationTargetException
+    {
+        // whether we accept package private methods
+        boolean acceptPackage = true;
+        String packageName = getPackageName( objectClass );
+
+        for ( Class clazz = objectClass; clazz != null; clazz = clazz.getSuperclass() )
+        {
+            // turns false on first package not equal to the package of objectClass
+            acceptPackage &= packageName.equals( getPackageName( clazz ) );
+
+            for ( int i = 0; i < parameterTypesList.length; i++ )
+            {
+                Class[] parameterTypes = parameterTypesList[i];
+
+                try
+                {
+                    // find the declared method in this class
+                    return getMethod( clazz, name, parameterTypes, clazz == objectClass, acceptPackage );
+                }
+                catch ( NoSuchMethodException nsme )
+                {
+                    // ignore for now
+                }
+                catch ( Throwable throwable )
+                {
+                    // unexpected problem accessing the method, don't let everything
+                    // blow up in this situation, just throw a declared exception
+                    throw new InvocationTargetException( throwable, "Unexpected problem trying to get method " + name );
+                }
+            }
+        }
+
+        // walked up the complete super class hierarchy and still not found
+        // anything, sigh ...
+        throw new NoSuchMethodException( name );
+    }
+
+
+    /**
+     * Finds the named public or protected method in the given class or any
+     * super class. If such a method is found, its accessibility is enfored by
+     * calling the <code>Method.setAccessible</code> method if required and
+     * the method is returned. Enforcing accessibility is required to support
+     * invocation of protected methods.
+     *
+     * @param clazz The <code>Class</code> which provides the method.
+     * @param name The name of the method.
+     * @param parameterTypes The parameters to the method. Passing
+     *      <code>null</code> is equivalent to using an empty array.
+     *
+     * @return The named method with enforced accessibility
+     *
+     * @throws NoSuchMethodException If no public or protected method with
+     *      the given name can be found in the class or any of its super classes.
+     * @throws InvocationTargetException If an unexpected Throwable is caught
+     *      trying to access the desired method.
+     */
+    static Method getMethod( Class clazz, String name, Class[] parameterTypes, boolean acceptPrivate,
+        boolean acceptPackage ) throws NoSuchMethodException, InvocationTargetException
+    {
+        try
+        {
+            // find the declared method in this class
+            Method method = clazz.getDeclaredMethod( name, parameterTypes );
+
+            // accept public and protected methods only and ensure accessibility
+            if ( accept( method, acceptPrivate, acceptPackage ) )
+            {
+                method.setAccessible( true );
+                return method;
+            }
+        }
+        catch ( NoSuchMethodException nsme )
+        {
+            // forward to caller
+            throw nsme;
+        }
+        catch ( Throwable throwable )
+        {
+            // unexpected problem accessing the method, don't let everything
+            // blow up in this situation, just throw a declared exception
+            throw new InvocationTargetException( throwable, "Unexpected problem trying to get method " + name );
+        }
+
+        // walked up the complete super class hierarchy and still not found
+        // anything, sigh ...
+        throw new NoSuchMethodException( name );
+    }
+
+
+    /**
+     * Returns <code>true</code> if the method is acceptable to be returned from the
+     * {@link #getMethod(Class, String, Class[], boolean, boolean)}.
+     * <p>
+     * This method returns <code>true</code> iff:
+     * <ul>
+     * <li>The method has <code>void</code> return type</li>
+     * <li>Is not static</li>
+     * <li>Is public or protected</li>
+     * <li>Is private and <code>acceptPrivate</code> is <code>true</code></li>
+     * <li>Is package private and <code>acceptPackage</code> is <code>true</code></li>
+     * </ul>
+     * <p>
+     * This method is package private for unit testing purposes. It is not
+     * meant to be called from client code.
+     *
+     * @param method The method to check
+     * @param acceptPrivate Whether a private method is acceptable
+     * @param acceptPackage Whether a package private method is acceptable
+     * @return
+     */
+    static boolean accept( Method method, boolean acceptPrivate, boolean acceptPackage )
+    {
+        // method must be void
+        if ( Void.TYPE != method.getReturnType() )
+        {
+            return false;
+        }
+
+        // check modifiers now
+        int mod = method.getModifiers();
+
+        // no static method
+        if ( Modifier.isStatic( mod ) )
+        {
+            return false;
+        }
+
+        // accept public and protected methods
+        if ( Modifier.isPublic( mod ) || Modifier.isProtected( mod ) )
+        {
+            return true;
+        }
+
+        // accept private if accepted
+        if ( Modifier.isPrivate( mod ) )
+        {
+            return acceptPrivate;
+        }
+
+        // accept default (package)
+        if ( acceptPackage )
+        {
+            return true;
+        }
+
+        // else don't accept
+        return false;
+    }
+
+
+    /**
+     * Returns the name of the package to which the class belongs or an
+     * empty string if the class is in the default package.
+     */
+    static String getPackageName( Class clazz )
+    {
+        String name = clazz.getName();
+        int dot = name.lastIndexOf( '.' );
+        return ( dot > 0 ) ? name.substring( 0, dot ) : "";
+    }
+}
diff --git a/scr/src/test/java/DefaultPackageClass.java b/scr/src/test/java/DefaultPackageClass.java
new file mode 100644
index 0000000..85738d3
--- /dev/null
+++ b/scr/src/test/java/DefaultPackageClass.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * The DefaultPackageClass is just present for the
+ * {@link org.apache.felix.scr.impl.ReflectionHelperTest} to be able to test
+ * the <code>ReflectionHelper.getPackageName</code> method.
+ */
+public class DefaultPackageClass
+{
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/ReflectionHelperTest.java b/scr/src/test/java/org/apache/felix/scr/impl/ReflectionHelperTest.java
new file mode 100644
index 0000000..dc5c052
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/ReflectionHelperTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.scr.impl.instances.AcceptMethod;
+import org.apache.felix.scr.impl.instances.BaseObject;
+import org.apache.felix.scr.impl.instances.Level1Object;
+import org.apache.felix.scr.impl.instances.Level3Object;
+import org.apache.felix.scr.impl.instances.MethodNameException;
+import org.apache.felix.scr.impl.instances2.Level2Object;
+
+
+public class ReflectionHelperTest extends TestCase
+{
+
+    private static final Class ACCEPT_METHOD_CLASS = AcceptMethod.class;
+
+    BaseObject base = new BaseObject();
+
+    Level1Object level1 = new Level1Object();
+
+    Level2Object level2 = new Level2Object();
+
+    Level3Object level3 = new Level3Object();
+
+
+    public void test_sentinel() throws Exception
+    {
+        assertNotNull( "Sentinel is null", ReflectionHelper.SENTINEL );
+    }
+
+
+    public void test_private_no_arg() throws Exception
+    {
+        checkMethod( base, "activate_no_arg" );
+
+        // activate_no_arg is private to BaseObject and must not be
+        // accessible from extensions
+        ensureMethodNotFoundMethod( level1, "activate_no_arg" );
+        ensureMethodNotFoundMethod( level2, "activate_no_arg" );
+        ensureMethodNotFoundMethod( level3, "activate_no_arg" );
+    }
+
+
+    public void test_protected_activate_comp() throws Exception
+    {
+        // activate_comp is protected in BaseObject and must be accessible
+        // in all instances
+        checkMethod( base, "activate_comp" );
+        checkMethod( level1, "activate_comp" );
+        checkMethod( level2, "activate_comp" );
+        checkMethod( level3, "activate_comp" );
+    }
+
+
+    public void test_private_activate_level1_bundle() throws Exception
+    {
+        // activate_level1_bundle is private in Level1Object and must be
+        // accessible in Level1Object only
+        ensureMethodNotFoundMethod( base, "activate_level1_bundle" );
+        checkMethod( level1, "activate_level1_bundle" );
+        ensureMethodNotFoundMethod( level2, "activate_level1_bundle" );
+        ensureMethodNotFoundMethod( level3, "activate_level1_bundle" );
+    }
+
+
+    public void test_protected_activate_level1_map() throws Exception
+    {
+        // activate_level1_map is protected in Level1Object and must be
+        // accessible in Level1Object and extensions but not in BaseObject
+        ensureMethodNotFoundMethod( base, "activate_level1_map" );
+        checkMethod( level1, "activate_level1_map" );
+        checkMethod( level2, "activate_level1_map" );
+        checkMethod( level3, "activate_level1_map" );
+    }
+
+
+    public void test_private_activate_comp_map() throws Exception
+    {
+        // private_activate_comp_map is private in Level2Object and must be
+        // accessible in Level2Object only
+        ensureMethodNotFoundMethod( base, "activate_comp_map" );
+        ensureMethodNotFoundMethod( level1, "activate_comp_map" );
+        checkMethod( level2, "activate_comp_map" );
+        checkMethod( level3, "activate_comp_map" );
+    }
+
+
+    public void test_public_activate_collision() throws Exception
+    {
+        // activate_collision is private in Level2Object and must be
+        // accessible in Level2Object only.
+        // also the method is available taking no arguments and a single
+        // map argument which takes precedence and which we expect
+        ensureMethodNotFoundMethod( base, "activate_collision" );
+        ensureMethodNotFoundMethod( level1, "activate_collision" );
+        checkMethod( level2, "activate_collision" );
+        checkMethod( level3, "activate_collision" );
+    }
+
+
+    public void test_package_activate_comp_bundle() throws Exception
+    {
+        // activate_comp_bundle is package private and thus visible in
+        // base and level1 but not in level 2 (different package) and
+        // level 3 (inheritance through different package)
+
+        checkMethod( base, "activate_comp_bundle" );
+        checkMethod( level1, "activate_comp_bundle" );
+        ensureMethodNotFoundMethod( level2, "activate_comp_bundle" );
+        ensureMethodNotFoundMethod( level3, "activate_comp_bundle" );
+    }
+
+
+    public void test_getPackage() throws Exception
+    {
+        Class dpc = getClass().getClassLoader().loadClass( "DefaultPackageClass" );
+        assertEquals( "", ReflectionHelper.getPackageName( dpc ) );
+
+        assertEquals( "org.apache.felix.scr.impl.instances", ReflectionHelper.getPackageName( base.getClass() ) );
+    }
+
+
+    public void test_accept() throws Exception
+    {
+        // public visible unless returning non-void
+        assertMethod( true, "public_void", false, false );
+        assertMethod( false, "public_string", false, false );
+
+        // protected visible unless returning non-void
+        assertMethod( true, "protected_void", false, false );
+        assertMethod( false, "protected_string", false, false );
+
+        // private not visible
+        assertMethod( false, "private_void", false, false );
+        assertMethod( false, "private_string", false, false );
+
+        // private visible unless returning non-void
+        assertMethod( true, "private_void", true, false );
+        assertMethod( false, "private_string", true, false );
+        assertMethod( true, "private_void", true, true );
+        assertMethod( false, "private_string", true, true );
+
+        // private not visible, accept package is ignored
+        assertMethod( false, "private_void", false, true );
+        assertMethod( false, "private_string", false, true );
+
+        // package not visible
+        assertMethod( false, "package_void", false, false );
+        assertMethod( false, "package_string", false, false );
+
+        // package visible unless returning non-void
+        assertMethod( true, "package_void", false, true );
+        assertMethod( false, "package_string", false, true );
+        assertMethod( true, "package_void", true, true );
+        assertMethod( false, "package_string", true, true );
+
+        // package not visible, accept private is ignored
+        assertMethod( false, "package_void", true, false );
+        assertMethod( false, "package_string", true, false );
+    }
+
+
+    //---------- internal
+
+    /**
+     * Checks whether a method with the given name can be found for the
+     * activate/deactivate method parameter list and whether the method returns
+     * its name when called.
+     *
+     * @param obj
+     * @param methodName
+     */
+    private void checkMethod( Object obj, String methodName ) throws NoSuchMethodException, InvocationTargetException,
+        IllegalAccessException
+    {
+        Method method = ReflectionHelper.getMethod( obj.getClass(), methodName,
+            ImmediateComponentManager.ACTIVATE_PARAMETER_LIST );
+        try
+        {
+            method.invoke( obj, new Object[method.getParameterTypes().length] );
+            fail( "Expected MethodNameException being thrown" );
+        }
+        catch ( InvocationTargetException ite )
+        {
+            Throwable target = ite.getTargetException();
+            assertTrue( "Expected MethodNameException", target instanceof MethodNameException );
+            assertEquals( methodName, target.getMessage() );
+        }
+    }
+
+
+    /**
+     * Ensures no method with the given name accepting any of the
+     * activate/deactive method parameters can be found.
+     *
+     * @param obj
+     * @param methodName
+     * @throws InvocationTargetException
+     * @throws IllegalAccessException
+     */
+    private void ensureMethodNotFoundMethod( Object obj, String methodName ) throws InvocationTargetException,
+        IllegalAccessException
+    {
+        try
+        {
+            checkMethod( obj, methodName );
+            fail( "Expected to not find method " + methodName + " for " + obj.getClass() );
+        }
+        catch ( NoSuchMethodException nsme )
+        {
+            // expected not to find a method
+        }
+    }
+
+
+    private void assertMethod( boolean expected, String methodName, boolean acceptPrivate, boolean acceptPackage )
+        throws NoSuchMethodException
+    {
+        Method method = ACCEPT_METHOD_CLASS.getDeclaredMethod( methodName, null );
+        boolean accepted = ReflectionHelper.accept( method, acceptPrivate, acceptPackage );
+        assertEquals( expected, accepted );
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/instances/AcceptMethod.java b/scr/src/test/java/org/apache/felix/scr/impl/instances/AcceptMethod.java
new file mode 100644
index 0000000..e31c601
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/instances/AcceptMethod.java
@@ -0,0 +1,71 @@
+/*
+ * 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.instances;
+
+
+/**
+ * The <code>AcceptMethod</code> class provides methods, which are used to
+ * test the ReflectionHelper.acceptMethod() method.
+ */
+public class AcceptMethod
+{
+
+    public void public_void()
+    {
+    }
+
+
+    public String public_string()
+    {
+        return "";
+    }
+
+
+    protected void protected_void()
+    {
+    }
+
+
+    protected String protected_string()
+    {
+        return "";
+    }
+
+
+    private void private_void()
+    {
+    }
+
+
+    private String private_string()
+    {
+        return "";
+    }
+
+
+    void package_void()
+    {
+    }
+
+
+    String package_string()
+    {
+        return "";
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/instances/BaseObject.java b/scr/src/test/java/org/apache/felix/scr/impl/instances/BaseObject.java
new file mode 100644
index 0000000..a30c84f
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/instances/BaseObject.java
@@ -0,0 +1,50 @@
+/*
+ * 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.instances;
+
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+
+
+/**
+ * The <code>BaseObject</code> is a base class providing a number of methods
+ * to check. All methods take various combinations of arguments and return
+ * a single helper string to indicate what method has been called.
+ */
+public class BaseObject
+{
+
+    private void activate_no_arg()
+    {
+        throw new MethodNameException( "activate_no_arg" );
+    }
+
+
+    protected void activate_comp( ComponentContext ctx )
+    {
+        throw new MethodNameException( "activate_comp" );
+    }
+
+
+    void activate_comp_bundle( ComponentContext ctx, BundleContext bundle )
+    {
+        throw new MethodNameException( "activate_comp_bundle" );
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/instances/Level1Object.java b/scr/src/test/java/org/apache/felix/scr/impl/instances/Level1Object.java
new file mode 100644
index 0000000..fbac013
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/instances/Level1Object.java
@@ -0,0 +1,41 @@
+/*
+ * 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.instances;
+
+
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+
+
+public class Level1Object extends BaseObject
+{
+
+    private void activate_level1_bundle( BundleContext ctx )
+    {
+        throw new MethodNameException("activate_level1_bundle");
+    }
+
+
+    protected void activate_level1_map( Map props )
+    {
+        throw new MethodNameException("activate_level1_map");
+    }
+
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/instances/Level3Object.java b/scr/src/test/java/org/apache/felix/scr/impl/instances/Level3Object.java
new file mode 100644
index 0000000..732b030
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/instances/Level3Object.java
@@ -0,0 +1,49 @@
+/*
+ * 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.instances;
+
+
+import java.util.Map;
+
+import org.apache.felix.scr.impl.instances2.Level2Object;
+import org.osgi.service.component.ComponentContext;
+
+
+public class Level3Object extends Level2Object
+{
+
+    private void activate_comp_map( ComponentContext ctx, Map map )
+    {
+        throw new MethodNameException("activate_comp_map");
+    }
+
+
+    // this method should not be found, since the method taking a
+    // Map has higher precedence
+    public void activate_collision()
+    {
+        throw new MethodNameException("not_expected_to_be_found");
+    }
+
+
+    public void activate_collision( Map map )
+    {
+        throw new MethodNameException("activate_collision");
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/instances/MethodNameException.java b/scr/src/test/java/org/apache/felix/scr/impl/instances/MethodNameException.java
new file mode 100644
index 0000000..3bfb7bb
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/instances/MethodNameException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.instances;
+
+
+public final class MethodNameException extends RuntimeException
+{
+
+    private static final long serialVersionUID = 1L;
+
+
+    public MethodNameException( String message )
+    {
+        super( message );
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/instances2/Level2Object.java b/scr/src/test/java/org/apache/felix/scr/impl/instances2/Level2Object.java
new file mode 100644
index 0000000..456f35e
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/instances2/Level2Object.java
@@ -0,0 +1,50 @@
+/*
+ * 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.instances2;
+
+
+import java.util.Map;
+
+import org.apache.felix.scr.impl.instances.Level1Object;
+import org.apache.felix.scr.impl.instances.MethodNameException;
+import org.osgi.service.component.ComponentContext;
+
+
+public class Level2Object extends Level1Object
+{
+
+    private void activate_comp_map( ComponentContext ctx, Map map )
+    {
+        throw new MethodNameException("activate_comp_map");
+    }
+
+
+    // this method should not be found, since the method taking a
+    // Map has higher precedence
+    public void activate_collision()
+    {
+        throw new MethodNameException("not_expected_to_be_found");
+    }
+
+
+    public void activate_collision( Map map )
+    {
+        throw new MethodNameException("activate_collision");
+    }
+}