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