| /* |
| * 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.inject.methods; |
| |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.felix.scr.impl.inject.ActivatorParameter; |
| import org.apache.felix.scr.impl.inject.internal.ComponentMethodsImpl; |
| import org.apache.felix.scr.impl.logger.ComponentLogger; |
| import org.apache.felix.scr.impl.logger.MockComponentLogger; |
| import org.apache.felix.scr.impl.manager.ComponentActivator; |
| import org.apache.felix.scr.impl.manager.ComponentContainer; |
| import org.apache.felix.scr.impl.manager.ComponentContextImpl; |
| import org.apache.felix.scr.impl.manager.SingleComponentManager; |
| import org.apache.felix.scr.impl.metadata.ComponentMetadata; |
| import org.apache.felix.scr.impl.metadata.DSVersion; |
| import org.apache.felix.scr.impl.metadata.instances.AcceptMethod; |
| import org.apache.felix.scr.impl.metadata.instances.BaseObject; |
| import org.apache.felix.scr.impl.metadata.instances.Level1Object; |
| import org.apache.felix.scr.impl.metadata.instances.Level3Object; |
| import org.apache.felix.scr.impl.metadata.instances2.Level2Object; |
| import org.mockito.Mockito; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.service.component.ComponentContext; |
| |
| import junit.framework.TestCase; |
| |
| |
| public class ActivateMethodTest extends TestCase |
| { |
| |
| private static final Class ACCEPT_METHOD_CLASS = AcceptMethod.class; |
| |
| private Bundle m_bundle; |
| |
| BaseObject base = new BaseObject(); |
| |
| Level1Object level1 = new Level1Object(); |
| |
| Level2Object level2 = new Level2Object(); |
| |
| Level3Object level3 = new Level3Object(); |
| |
| @Override |
| protected void setUp() throws Exception |
| { |
| super.setUp(); |
| m_bundle = Mockito.mock( Bundle.class ); |
| BundleContext context = Mockito.mock( BundleContext.class ); |
| Mockito.when(context.getBundle()).thenReturn(m_bundle); |
| } |
| |
| |
| 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( "", BaseMethod.getPackageName( dpc ) ); |
| |
| assertEquals( "org.apache.felix.scr.impl.metadata.instances", BaseMethod.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 ); |
| } |
| |
| |
| public void test_suitable_method_selection() throws Exception |
| { |
| // this would be the protected BaseObject.activate_suitable |
| checkMethod( base, "activate_suitable" ); |
| checkMethod( level1, "activate_suitable" ); |
| |
| // this would be the private Level2Object.activate_suitable |
| checkMethod( level2, "activate_suitable" ); |
| |
| // this must fail to find a method, since Level2Object's activate_suitable |
| // is private and terminates the search for Level3Object |
| ensureMethodNotFoundMethod( level3, "activate_suitable" ); |
| } |
| |
| public void test_unsuitable_method_selection() throws Exception |
| { |
| //check that finding an unsuitable method does not prevent finding |
| // a lower precedence suitable method. |
| |
| checkMethod( level2, "activate_comp_unsuitable" ); |
| |
| checkMethod( level3, "activate_comp_unsuitable" ); |
| } |
| |
| public void test_precedence() throws Exception |
| { |
| //All tested methods are only in base. They differ in arguments and visibility. |
| //R4.2 compendium 112.5.8 |
| //private method, arg ComponentContext |
| checkMethod( base, "activate_precedence_1", "activate_precedence_1_comp" ); |
| //package method, arg BundleContext |
| checkMethod( level1, "activate_precedence_1", "activate_precedence_1_bundleContext" ); |
| //protected method, arg Map |
| checkMethod( level2, "activate_precedence_1", "activate_precedence_1_map" ); |
| |
| //private method, arg Map |
| checkMethod( base, "activate_precedence_2", "activate_precedence_2_map" ); |
| //package method, args ComponentContext and Map |
| checkMethod( level1, "activate_precedence_2", "activate_precedence_2_comp_bundleContext" ); |
| //protected method, no args |
| checkMethod( level2, "activate_precedence_2", "activate_precedence_2_empty" ); |
| } |
| |
| //---------- 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( BaseObject obj, String methodName ) |
| { |
| checkMethod( obj, methodName, methodName, DSVersion.DS11 ); |
| } |
| |
| /** |
| * Checks whether a method with the given name can be found for the |
| * activate/deactivate method parameter list and whether the method returns |
| * the expected description when called. |
| * |
| * @param obj |
| * @param methodName |
| * @param methodDesc |
| */ |
| private void checkMethod( BaseObject obj, String methodName, String methodDesc ) |
| { |
| checkMethod(obj, methodName, methodDesc, DSVersion.DS11); |
| } |
| |
| |
| /** |
| * Checks whether a method with the given name can be found for the |
| * activate/deactivate method parameter list and whether the method returns |
| * the expected description when called. |
| * |
| * @param obj |
| * @param methodName |
| * @param methodDesc |
| * @param version DSVersion tested |
| */ |
| private void checkMethod( BaseObject obj, String methodName, String methodDesc, DSVersion version ) |
| { |
| ComponentContainer<?> container = newContainer(); |
| SingleComponentManager<?> icm = new SingleComponentManager( container, new ComponentMethodsImpl() ); |
| ActivateMethod am = new ActivateMethod( methodName, methodName != null, obj.getClass(), version, false, false ); |
| |
| am.invoke( obj, new ActivatorParameter( new ComponentContextImpl(icm, m_bundle, null), -1 ), null ); |
| Method m = am.getMethod(); |
| assertNotNull( m ); |
| assertEquals( methodName, m.getName() ); |
| assertEquals( methodDesc, obj.getCalledMethod() ); |
| } |
| |
| |
| private ComponentContainer newContainer() |
| { |
| final ComponentMetadata metadata = newMetadata(); |
| ComponentContainer container = new ComponentContainer() { |
| |
| @Override |
| public ComponentActivator getActivator() |
| { |
| final ComponentActivator ca = Mockito.mock(ComponentActivator.class); |
| Mockito.when(ca.getBundleContext()).thenReturn(Mockito.mock(BundleContext.class)); |
| return ca; |
| } |
| |
| @Override |
| public ComponentMetadata getComponentMetadata() |
| { |
| return metadata; |
| } |
| |
| @Override |
| public void disposed(SingleComponentManager component) |
| { |
| } |
| |
| public boolean isEnabled() |
| { |
| return false; |
| } |
| |
| @Override |
| public ComponentLogger getLogger() { |
| return new MockComponentLogger(); |
| } |
| }; |
| return container; |
| } |
| |
| |
| private ComponentMetadata newMetadata() { |
| ComponentMetadata metadata = new ComponentMetadata( DSVersion.DS11 ); |
| metadata.setName("foo"); |
| metadata.setImplementationClassName(Object.class.getName()); |
| metadata.validate(); |
| return metadata; |
| } |
| |
| |
| /** |
| * 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( BaseObject obj, String methodName ) |
| { |
| ensureMethodNotFoundMethod(obj, methodName, DSVersion.DS11); |
| } |
| |
| |
| /** |
| * Ensures no method with the given name accepting any of the |
| * activate/deactive method parameters can be found. |
| * |
| * @param obj |
| * @param methodName |
| * @param version DS version tested |
| * @throws InvocationTargetException |
| * @throws IllegalAccessException |
| */ |
| private void ensureMethodNotFoundMethod( BaseObject obj, String methodName, DSVersion version ) |
| { |
| ComponentContainer container = newContainer(); |
| SingleComponentManager icm = new SingleComponentManager( container, new ComponentMethodsImpl() ); |
| ActivateMethod am = new ActivateMethod( methodName, methodName != null, obj.getClass(), version, false, false ); |
| am.invoke( obj, new ActivatorParameter( new ComponentContextImpl(icm, m_bundle, null), -1 ), null ); |
| Method m = am.getMethod(); |
| assertNull( m ); |
| assertNull( obj.getCalledMethod() ); |
| } |
| |
| |
| private void assertMethod( boolean expected, String methodName, boolean acceptPrivate, boolean acceptPackage ) |
| throws NoSuchMethodException |
| { |
| Method method = ACCEPT_METHOD_CLASS.getDeclaredMethod( methodName, null ); |
| boolean accepted = BaseMethod.accept( method, acceptPrivate, acceptPackage, false ); |
| assertEquals( expected, accepted ); |
| } |
| |
| private static @interface Ann{} |
| private static class Sort |
| { |
| public void a(Ann ann) {}; |
| public void a(int c) {}; |
| public void a(Integer c) {}; |
| public void a(BundleContext c) {}; |
| public void a(Map m) {}; |
| public void a() {}; |
| public void a(ComponentContext cc) {}; |
| public void a(ComponentContext cc, BundleContext c) {}; |
| public void b() {}; |
| |
| } |
| public void testMethodSorting() throws Exception |
| { |
| ActivateMethod am = new ActivateMethod( "a", true, Sort.class, DSVersion.DS11, false, false ); |
| List<Method> ms = am.getSortedMethods(Sort.class); |
| assertEquals(8, ms.size()); |
| assertEquals(1, ms.get(0).getParameterTypes().length); |
| assertEquals(ComponentContext.class, ms.get(0).getParameterTypes()[0]); |
| assertEquals(1, ms.get(1).getParameterTypes().length); |
| assertEquals(BundleContext.class, ms.get(1).getParameterTypes()[0]); |
| assertEquals(1, ms.get(2).getParameterTypes().length); |
| assertEquals(Ann.class, ms.get(2).getParameterTypes()[0]); |
| assertEquals(1, ms.get(3).getParameterTypes().length); |
| assertEquals(Map.class, ms.get(3).getParameterTypes()[0]); |
| assertEquals(1, ms.get(4).getParameterTypes().length); |
| assertEquals(int.class, ms.get(4).getParameterTypes()[0]); |
| assertEquals(1, ms.get(5).getParameterTypes().length); |
| assertEquals(Integer.class, ms.get(5).getParameterTypes()[0]); |
| assertEquals(2, ms.get(6).getParameterTypes().length); |
| assertEquals(0, ms.get(7).getParameterTypes().length); |
| } |
| |
| public void test_13_annos() throws Exception |
| { |
| checkMethod(base, "activate_13_2_annotations", "activate_13_2_annotations", DSVersion.DS13 ); |
| } |
| |
| } |