FELIX-927 Apply patch by Alin Dreghiciu to allow for better bind/unbind
method signatures (thanks for the patch)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@798554 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/ReflectionHelper.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/ReflectionHelper.java
index 745bb09..d92528f 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/ReflectionHelper.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/ReflectionHelper.java
@@ -28,6 +28,7 @@
 import java.util.Set;
 
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.component.ComponentContext;
 
 
@@ -45,6 +46,7 @@
     // class references to simplify parameter checking
     public static final Class COMPONENT_CONTEXT_CLASS = ComponentContext.class;
     public static final Class BUNDLE_CONTEXT_CLASS = BundleContext.class;
+    public static final Class SERVICE_REFERENCE_CLASS = ServiceReference.class;
     public static final Class MAP_CLASS = Map.class;
     public static final Class INTEGER_CLASS = Integer.class;
 
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
new file mode 100644
index 0000000..9ecbb27
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/BindMethod.java
@@ -0,0 +1,361 @@
+/*
+ * 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;
+
+
+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.ReflectionHelper;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * Component method to be invoked on service (un)binding.
+ */
+class BindMethod
+{
+
+    private final String m_methodName;
+    private final Class m_componentClass;
+    private final String m_referenceName;
+    private final String m_referenceClassName;
+    private final Logger m_logger;
+
+    private Method m_method = null;
+    private State m_state;
+
+
+    BindMethod( final String methodName, final Class componentClass, final String referenceName,
+        final String referenceClassName, final Logger logger )
+    {
+        m_methodName = methodName;
+        m_componentClass = componentClass;
+        m_referenceName = referenceName;
+        m_referenceClassName = referenceClassName;
+        m_logger = logger;
+        if ( m_methodName == null )
+        {
+            m_state = new NotApplicable();
+        }
+        else
+        {
+            m_state = new NotResolved();
+        }
+    }
+
+
+    private Method findMethod( final Class targetClass ) throws InvocationTargetException
+    {
+        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 - 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
+                );
+        }
+        catch ( NoSuchMethodException ex )
+        {
+
+            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 )
+                {
+                    loader = ClassLoader.getSystemClassLoader();
+                }
+
+                parameterClass = loader.loadClass( m_referenceClassName );
+                return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
+                    { parameterClass }, false, false );
+            }
+            catch ( NoSuchMethodException ex2 )
+            {
+
+                // 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++ )
+                {
+                    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;
+                            }
+                        }
+                    }
+                }
+
+                // Case 4: same as case 2, but + Map param
+                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();
+                    }
+
+                    parameterClass = loader.loadClass( m_referenceClassName );
+                    return ReflectionHelper.getMethod( targetClass, m_methodName, new Class[]
+                        { parameterClass, Map.class }, false, false );
+                }
+                catch ( NoSuchMethodException ex3 )
+                {
+
+                }
+                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 we can't load the class, perhaps the method is declared in a super class
+                // so we try this class next
+            }
+
+            // 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;
+    }
+
+
+    private boolean invokeMethod( final Object componentInstance, final Service service )
+    {
+        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 )
+            {
+                params[i] = service.getReference();
+            }
+            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;
+            }
+            else
+            {
+                params[i] = service.getInstance();
+                if ( params[i] == null )
+                {
+                    m_logger.log( LogService.LOG_INFO, "Dependency Manager: Service " + service.getReference()
+                        + " has already gone, not " + getMethodNamePrefix() + "binding" );
+                    return false;
+                }
+            }
+        }
+        try
+        {
+            m_method.invoke( componentInstance, params );
+            m_logger.log( LogService.LOG_DEBUG, getMethodNamePrefix() + "bound: " + m_referenceName + "/"
+                + service.getReference().getProperty( Constants.SERVICE_ID ) );
+        }
+        catch ( IllegalAccessException ex )
+        {
+            // 112.3.1 If the method is not is not declared protected or
+            // public, SCR must log an error message with the log service,
+            // if present, and ignore the method
+            m_logger.log( LogService.LOG_ERROR, getMethodNamePrefix() + "bind method " + m_methodName + "] cannot be called",
+                ex );
+        }
+        catch ( InvocationTargetException ex )
+        {
+            // 112.5.7 If a bind method throws an exception, SCR must log an
+            // error message containing the exception [...]
+            m_logger.log( LogService.LOG_ERROR, "DependencyManager : exception while invoking " + m_methodName + "()",
+                ex.getCause() );
+        }
+        return true;
+    }
+
+
+    protected String getMethodNamePrefix()
+    {
+        return "";
+    }
+
+
+    //---------- State management  ------------------------------------
+
+    boolean invoke( final Object componentInstance, final Service service )
+    {
+        return m_state.invoke( componentInstance, service );
+    }
+
+    private static interface State
+    {
+
+        boolean invoke( final Object componentInstance, final Service service );
+    }
+
+    private static class NotApplicable implements State
+    {
+
+        public boolean invoke( final Object componentInstance, final Service service )
+        {
+            return true;
+        }
+    }
+
+    private class NotResolved implements State
+    {
+
+        public boolean invoke( final Object componentInstance, final Service service )
+        {
+            m_logger.log( LogService.LOG_DEBUG, "getting " + getMethodNamePrefix() + "bind: " + m_methodName );
+            try
+            {
+                m_method = findMethod( m_componentClass );
+                if ( m_method == null )
+                {
+                    m_state = new NotFound();
+                }
+                else
+                {
+                    m_state = new Resolved();
+                }
+                return m_state.invoke( componentInstance, service );
+            }
+            catch ( InvocationTargetException ex )
+            {
+                m_state = new NotFound();
+                // 112.5.7 If a bind method throws an exception, SCR must log an
+                // error message containing the exception [...]
+                m_logger.log( LogService.LOG_ERROR, "DependencyManager : exception while finding " + m_methodName
+                    + "()", ex.getCause() );
+            }
+            return true;
+        }
+    }
+
+    private class NotFound implements State
+    {
+
+        public boolean invoke( final Object componentInstance, final Service service )
+        {
+            // 112.3.1 If the method is not found , SCR must log an error
+            // message with the log service, if present, and ignore the
+            // method
+            m_logger.log( LogService.LOG_ERROR, getMethodNamePrefix() + "bind method [" + m_methodName + "] not found" );
+            return true;
+        }
+    }
+
+    private class Resolved implements State
+    {
+
+        public boolean invoke( final Object componentInstance, final Service service )
+        {
+            m_logger.log( LogService.LOG_DEBUG, "invoking " + getMethodNamePrefix() + "bind: " + m_methodName );
+            return invokeMethod( componentInstance, service );
+        }
+    }
+
+    //---------- Service abstraction ------------------------------------
+
+    static interface Service
+    {
+
+        ServiceReference getReference();
+
+
+        Object getInstance();
+
+    }
+
+    //---------- Logger ------------------------------------
+
+    static interface Logger
+    {
+
+        void log( int level, String message );
+
+
+        void log( int level, String message, Throwable ex );
+
+    }
+
+}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
index a889f12..1cea987 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/DependencyManager.java
@@ -77,13 +77,13 @@
     private transient Object m_componentInstance;
 
     // the bind method
-    private Method m_bind;
+    private BindMethod m_bind;
 
     // whether the bind method takes a service reference
     private boolean m_bindUsesReference;
 
     // the unbind method
-    private Method m_unbind;
+    private UnbindMethod m_unbind;
 
     // whether the unbind method takes a service reference
     private boolean m_unbindUsesReference;
@@ -114,6 +114,39 @@
             .getComponentMetadata(), null );
     }
 
+    /**
+     * Initialize binding methods.
+     */
+    private void initBindingMethods()
+    {
+        BindMethod.Logger logger = new BindMethod.Logger()
+        {
+
+            public void log( int level, String message )
+            {
+                log( level, message, null );
+            }
+
+            public void log( int level, String message, Throwable ex )
+            {
+                m_componentManager.log( level, message, m_componentManager.getComponentMetadata(), ex );
+            }
+        };
+        m_bind = new BindMethod( m_dependencyMetadata.getBind(),
+                                 m_componentInstance.getClass(),
+                                 m_dependencyMetadata.getName(),
+                                 m_dependencyMetadata.getInterface(),
+                                 logger
+        );
+        m_unbind = new UnbindMethod( m_dependencyMetadata.getUnbind(),
+                                     m_componentInstance.getClass(),
+                                     m_dependencyMetadata.getName(),
+                                     m_dependencyMetadata.getInterface(),
+                                     logger
+        );
+    }
+
+
 
     //---------- ServiceListener interface ------------------------------------
 
@@ -486,10 +519,6 @@
                 ungetService( boundRefs[i] );
             }
         }
-
-        // drop the method references (to help GC)
-        m_bind = null;
-        m_unbind = null;
     }
 
 
@@ -855,6 +884,7 @@
     boolean open( Object instance )
     {
         m_componentInstance = instance;
+        initBindingMethods();
         return bind();
     }
 
@@ -872,6 +902,9 @@
         finally
         {
             m_componentInstance = null;
+            m_bind = null;
+            m_unbind = null;
+
         }
     }
 
@@ -969,120 +1002,6 @@
         }
     }
 
-
-    /**
-     * Gets a bind or unbind method according to the policies described in the
-     * specification
-     *
-     * @param methodname The name of the method
-     * @param targetClass the class to which the method belongs to
-     * @param parameterClassName the name of the class of the parameter that is
-     *            passed to the method
-     * @param serviceBundle the bundle of the registered service.
-     * @return the method or null
-     * @throws ClassNotFoundException if the class for parameterClassName cannot
-     *      be found.
-     * @throws InvocationTargetException If an unexpected error occurrs trying
-     *      to get the method from the targetClass.
-     */
-    private Method getBindingMethod( String methodname, Class targetClass, String parameterClassName )
-        throws InvocationTargetException
-    {
-        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 - ServiceReference parameter
-            return ReflectionHelper.getMethod( targetClass, methodname, new Class[]
-                { SERVICE_REFERENCE_CLASS }, false, false );
-        }
-        catch ( NoSuchMethodException ex )
-        {
-
-            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 )
-                {
-                    loader = ClassLoader.getSystemClassLoader();
-                }
-
-                parameterClass = loader.loadClass( parameterClassName );
-                return ReflectionHelper.getMethod( targetClass, methodname, new Class[]
-                    { parameterClass }, false, false );
-            }
-            catch ( NoSuchMethodException ex2 )
-            {
-
-                // 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++ )
-                {
-                    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( 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;
-                            }
-                        }
-                    }
-                }
-            }
-            catch ( ClassNotFoundException ex2 )
-            {
-                // 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
-        targetClass = targetClass.getSuperclass();
-        return ( targetClass != null ) ? getBindingMethod( methodname, targetClass, parameterClassName ) : null;
-    }
-
-
     /**
      * Calls the bind method. In case there is an exception while calling the
      * bind method, the service is not considered to be bound to the instance
@@ -1099,86 +1018,30 @@
      *      be handed over to the bind method but the service cannot be
      *      retrieved using the service reference.
      */
-    private boolean invokeBindMethod( ServiceReference ref )
+    private boolean invokeBindMethod( final ServiceReference ref )
     {
         // The bind method is only invoked if the implementation object is not
         // null. This is valid for both immediate and delayed components
-        if ( m_componentInstance != null )
+        if( m_componentInstance != null )
         {
-            try
-            {
-                // Get the bind method
-                m_componentManager.log( LogService.LOG_DEBUG, "getting bind: " + m_dependencyMetadata.getBind(),
-                    m_componentManager.getComponentMetadata(), null );
-                if ( m_bind == null )
+            return m_bind.invoke(
+                m_componentInstance,
+                new BindMethod.Service()
                 {
-                    m_bind = getBindingMethod( m_dependencyMetadata.getBind(), m_componentInstance.getClass(),
-                        m_dependencyMetadata.getInterface() );
-
-                    // 112.3.1 If the method is not found , SCR must log an error
-                    // message with the log service, if present, and ignore the
-                    // method
-                    if ( m_bind == null )
+                    public ServiceReference getReference()
                     {
-                        m_componentManager.log( LogService.LOG_ERROR, "bind() method not found", m_componentManager
-                            .getComponentMetadata(), null );
-                        return true;
+                        bindService( ref );
+                        return ref;
                     }
 
-                    // cache whether the bind method takes a reference
-                    m_bindUsesReference = SERVICE_REFERENCE_CLASS.equals( m_bind.getParameterTypes()[0] );
-                }
-
-                // Get the parameter
-                Object parameter;
-                if ( m_bindUsesReference )
-                {
-                    parameter = ref;
-
-                    // mark this service as bound using the special sentinel
-                    bindService( ref );
-                }
-                else
-                {
-                    // get the service, fail binding if the service is not
-                    // available (any more)
-                    parameter = getService( ref );
-                    if ( parameter == null )
+                    public Object getInstance()
                     {
-                        m_componentManager.log( LogService.LOG_INFO, "Dependency Manager: Service " + ref
-                            + " has already gone, not binding", m_componentManager.getComponentMetadata(), null );
-                        return false;
+                        return getService( ref );
                     }
                 }
-
-                // Invoke the method
-                m_bind.invoke( m_componentInstance, new Object[]
-                    { parameter } );
-
-                m_componentManager.log( LogService.LOG_DEBUG, "bound: " + getName() + "/"
-                    + ref.getProperty( Constants.SERVICE_ID ), m_componentManager.getComponentMetadata(), null );
-
-                return true;
-            }
-            catch ( IllegalAccessException ex )
-            {
-                // 112.3.1 If the method is not is not declared protected or
-                // public, SCR must log an error message with the log service,
-                // if present, and ignore the method
-                m_componentManager.log( LogService.LOG_ERROR, "bind() method cannot be called", m_componentManager
-                    .getComponentMetadata(), ex );
-                return true;
-            }
-            catch ( InvocationTargetException ex )
-            {
-                // 112.5.7 If a bind method throws an exception, SCR must log an
-                // error message containing the exception [...]
-                m_componentManager.log( LogService.LOG_ERROR, "DependencyManager : exception while invoking "
-                    + m_dependencyMetadata.getBind() + "()", m_componentManager.getComponentMetadata(), ex );
-                return true;
-            }
+            );
         }
-        else if ( m_componentManager.getComponentMetadata().isImmediate() == false )
+        else if ( !m_componentManager.getComponentMetadata().isImmediate() )
         {
             m_componentManager.log( LogService.LOG_DEBUG,
                 "DependencyManager : Delayed component not yet created, assuming bind method call succeeded",
@@ -1222,74 +1085,27 @@
      *            unbound
      * @return true if the call was successful, false otherwise
      */
-    private void invokeUnbindMethod( ServiceReference ref )
+    private void invokeUnbindMethod( final ServiceReference ref )
     {
         // The unbind method is only invoked if the implementation object is not
         // null. This is valid for both immediate and delayed components
         if ( m_componentInstance != null )
         {
-            try
-            {
-                // Get the bind method
-                m_componentManager.log( LogService.LOG_DEBUG, "getting unbind: " + m_dependencyMetadata.getUnbind(),
-                    m_componentManager.getComponentMetadata(), null );
-                if ( m_unbind == null )
+            m_unbind.invoke(
+                m_componentInstance,
+                new BindMethod.Service()
                 {
-                    m_unbind = getBindingMethod( m_dependencyMetadata.getUnbind(), m_componentInstance.getClass(),
-                        m_dependencyMetadata.getInterface() );
-
-                    if ( m_unbind == null )
+                    public ServiceReference getReference()
                     {
-                        // 112.3.1 If the method is not found, SCR must log an error
-                        // message with the log service, if present, and ignore the
-                        // method
-                        m_componentManager.log( LogService.LOG_ERROR, "unbind() method not found", m_componentManager
-                            .getComponentMetadata(), null );
+                        return ref;
                     }
-                    // cache whether the unbind method takes a reference
-                    m_unbindUsesReference = SERVICE_REFERENCE_CLASS.equals( m_unbind.getParameterTypes()[0] );
-                }
 
-                // Get the parameter
-                Object parameter = null;
-                if ( m_unbindUsesReference )
-                {
-                    parameter = ref;
-                }
-                else
-                {
-                    parameter = getService( ref );
-                    if ( parameter == null )
+                    public Object getInstance()
                     {
-                        m_componentManager.log( LogService.LOG_INFO, "Dependency Manager: Service " + ref
-                            + " has already gone, not unbinding", m_componentManager.getComponentMetadata(), null );
+                        return getService( ref );
                     }
                 }
-
-                m_unbind.invoke( m_componentInstance, new Object[]
-                    { parameter } );
-
-                m_componentManager.log( LogService.LOG_DEBUG, "unbound: " + getName() + "/"
-                    + ref.getProperty( Constants.SERVICE_ID ), m_componentManager.getComponentMetadata(), null );
-            }
-            catch ( IllegalAccessException ex )
-            {
-                // 112.3.1 If the method is not is not declared protected or
-                // public, SCR must log an error message with the log service,
-                // if present, and ignore the method
-                m_componentManager.log( LogService.LOG_ERROR, "unbind() method cannot be called", m_componentManager
-                    .getComponentMetadata(), ex );
-            }
-            catch ( InvocationTargetException ex )
-            {
-                // 112.5.13 If an unbind method throws an exception, SCR must
-                // log an error message containing the exception [...]
-                m_componentManager
-                    .log( LogService.LOG_ERROR, "DependencyManager : exception while invoking "
-                        + m_dependencyMetadata.getUnbind() + "()", m_componentManager.getComponentMetadata(), ex
-                        .getCause() );
-            }
-
+            );
         }
         else
         {
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/manager/UnbindMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/manager/UnbindMethod.java
new file mode 100644
index 0000000..90797cb
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/manager/UnbindMethod.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+
+/**
+ * Component method to be invoked on service unbinding.
+ */
+class UnbindMethod extends BindMethod
+{
+
+    UnbindMethod( final String methodName, final Class componentClass, final String referenceName,
+        final String referenceClassName, final Logger logger )
+    {
+        super( methodName, componentClass, referenceName, referenceClassName, logger );
+    }
+
+
+    protected String getMethodNamePrefix()
+    {
+        return "un";
+    }
+
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..9fd1acd
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/manager/BindMethodTest.java
@@ -0,0 +1,367 @@
+/*
+ * 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;
+
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+
+public class BindMethodTest extends TestCase
+{
+
+    private ServiceReference m_serviceReference;
+    private FakeService m_serviceInstance;
+    private BindMethod.Service m_service;
+
+
+    public void setUp()
+    {
+        m_serviceReference = ( ServiceReference ) EasyMock.createNiceMock( ServiceReference.class );
+        m_serviceInstance = ( FakeService ) EasyMock.createNiceMock( FakeService.class );
+        m_service = new BindMethod.Service()
+        {
+            public ServiceReference getReference()
+            {
+                return m_serviceReference;
+            }
+
+
+            public Object getInstance()
+            {
+                return m_serviceInstance;
+            }
+        };
+        EasyMock.expect( m_serviceReference.getPropertyKeys() ).andReturn( new String[]
+            { Constants.SERVICE_ID } ).anyTimes();
+        EasyMock.expect( m_serviceReference.getProperty( Constants.SERVICE_ID ) ).andReturn( "Fake Service" )
+            .anyTimes();
+        EasyMock.replay( new Object[]
+            { m_serviceReference } );
+    }
+
+
+    public void test_Unexistent()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "unexistent", T1.class ).invoke( t1, m_service );
+        assertNull( t1.callPerformed );
+    }
+
+
+    public void test_privateT1()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "privateT1", T1.class ).invoke( t1, m_service );
+        assertNull( t1.callPerformed );
+    }
+
+
+    public void test_protectedT1()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "protectedT1", T1.class ).invoke( t1, m_service );
+        assertNull( t1.callPerformed );
+    }
+
+
+    public void test_protectedT1SR()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "protectedT1SR", T1.class ).invoke( t1, m_service );
+        assertEquals( "protectedT1SR", t1.callPerformed );
+    }
+
+
+    public void test_protectedT1SI()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "protectedT1SI", T1.class ).invoke( t1, m_service );
+        assertEquals( "protectedT1SI", t1.callPerformed );
+    }
+
+
+    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 );
+    }
+
+
+    public void test_publicT1()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "publicT1", T1.class ).invoke( t1, m_service );
+        assertNull( t1.callPerformed );
+    }
+
+
+    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 );
+    }
+
+
+    public void test_publicT1SI()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "publicT1SI", T1.class ).invoke( t1, m_service );
+        assertEquals( "publicT1SI", t1.callPerformed );
+    }
+
+
+    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 );
+    }
+
+
+    public void test_publicT1SSI()
+    {
+        System.out.println();
+        final T1 t1 = new T1();
+        createMethod( "publicT1SSI", T1.class ).invoke( t1, m_service );
+        assertEquals( "publicT1SSI", t1.callPerformed );
+    }
+
+
+    public void test_publicT1SSI_onT2()
+    {
+        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
+    {
+
+    }
+
+
+    public BindMethod createMethod( final String methodName, final Class componentClass )
+    {
+        return new BindMethod( methodName, componentClass, "reference", FakeService.class.getName(), new SysOutLogger() );
+    }
+
+    private static class SysOutLogger implements BindMethod.Logger
+    {
+
+        private static final String[] LEVELS = new String[]
+            { "ERROR", "WARNING", "INFO", "DEBUG" };
+
+
+        public void log( int level, String message )
+        {
+            log( level, message, null );
+        }
+
+
+        public void log( int level, String message, Throwable ex )
+        {
+            System.out.println( LEVELS[level - 1] + " - " + message );
+            if ( ex != null )
+            {
+                System.out.println( ex.getClass().getName() + "-" + ex.getMessage() );
+            }
+        }
+    }
+
+}