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