FELIX-1686 Record in the component metadata whether the activate/deactivate
method was declared or not to be able to decide whether the activate/deactivate
method must be present (if declared) or may be present (if not declared).
This allows failing the component activation if the declared method is missing.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@822285 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java b/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
index 788e872..d4c7dae 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
@@ -229,7 +229,7 @@
             }
         };
         ImmediateComponentManager icm = new ImmediateComponentManager( null, null, metadata );
-        ActivateMethod am = new ActivateMethod( icm, methodName, obj.getClass() );
+        ActivateMethod am = new ActivateMethod( icm, methodName, methodName != null, obj.getClass() );
         am.invoke( obj, new ActivateMethod.ActivatorParameter( m_ctx, -1 ) );
         Method m = get(am, "m_method");
         assertNotNull( m );
@@ -257,7 +257,7 @@
             }
         };
         ImmediateComponentManager icm = new ImmediateComponentManager( null, null, metadata );
-        ActivateMethod am = new ActivateMethod( icm, methodName, obj.getClass() );
+        ActivateMethod am = new ActivateMethod( icm, methodName, methodName != null, obj.getClass() );
         am.invoke( obj, new ActivateMethod.ActivatorParameter( m_ctx, -1 ) );
         assertNull( get( am, "m_method" ) );
         assertNull( obj.getCalledMethod() );
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java b/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java
index ba4b78a..5a1dd86 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/metadata/ComponentMetadataTest.java
@@ -279,11 +279,13 @@
         final ComponentMetadata cm1 = createComponentMetadata( Boolean.TRUE, null );
         cm1.validate( logger );
         assertEquals( "Activate method name", "activate", cm1.getActivate() );
+        assertFalse( "Activate method expected to not be declared", cm1.isActivateDeclared() );
 
         final ComponentMetadata cm2 = createComponentMetadata( Boolean.TRUE, null );
         cm2.setActivate( "someMethod" );
         cm2.validate( logger );
         assertEquals( "Activate method name", "activate", cm2.getActivate() );
+        assertFalse( "Activate method expected to not be declared", cm2.isActivateDeclared() );
     }
 
 
@@ -292,11 +294,13 @@
         final ComponentMetadata cm1 = createComponentMetadata11( Boolean.TRUE, null );
         cm1.validate( logger );
         assertEquals( "Activate method name", "activate", cm1.getActivate() );
+        assertFalse( "Activate method expected to not be declared", cm1.isActivateDeclared() );
 
         final ComponentMetadata cm2 = createComponentMetadata11( Boolean.TRUE, null );
         cm2.setActivate( "someMethod" );
         cm2.validate( logger );
         assertEquals( "Activate method name", "someMethod", cm2.getActivate() );
+        assertTrue( "Activate method expected to be declared", cm2.isActivateDeclared() );
     }
 
 
@@ -305,11 +309,13 @@
         final ComponentMetadata cm1 = createComponentMetadata( Boolean.TRUE, null );
         cm1.validate( logger );
         assertEquals( "Deactivate method name", "deactivate", cm1.getDeactivate() );
+        assertFalse( "Deactivate method expected to not be declared", cm1.isDeactivateDeclared() );
 
         final ComponentMetadata cm2 = createComponentMetadata( Boolean.TRUE, null );
         cm2.setDeactivate( "someMethod" );
         cm2.validate( logger );
         assertEquals( "Deactivate method name", "deactivate", cm2.getDeactivate() );
+        assertFalse( "Deactivate method expected to not be declared", cm2.isDeactivateDeclared() );
     }
 
 
@@ -318,11 +324,13 @@
         final ComponentMetadata cm1 = createComponentMetadata11( Boolean.TRUE, null );
         cm1.validate( logger );
         assertEquals( "Deactivate method name", "deactivate", cm1.getDeactivate() );
+        assertFalse( "Deactivate method expected to not be declared", cm1.isDeactivateDeclared() );
 
         final ComponentMetadata cm2 = createComponentMetadata11( Boolean.TRUE, null );
         cm2.setDeactivate( "someMethod" );
         cm2.validate( logger );
         assertEquals( "Deactivate method name", "someMethod", cm2.getDeactivate() );
+        assertTrue( "Deactivate method expected to be declared", cm2.isDeactivateDeclared() );
     }
 
 
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java b/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
index d8bed49..79106b3 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/metadata/XmlHandlerTest.java
@@ -72,7 +72,9 @@
         assertEquals( "DS Version 1.0", XmlHandler.DS_VERSION_1_0, cm10.getNamespaceCode() );
         assertFalse( "DS Version 1.0", cm10.isDS11() );
         assertEquals( "Expected Activate Method not set", "activate", cm10.getActivate() );
+        assertFalse( "Activate method expected to not be declared", cm10.isActivateDeclared() );
         assertEquals( "Expected Deactivate Method not set", "deactivate", cm10.getDeactivate() );
+        assertFalse( "Deactivate method expected to not be declared", cm10.isDeactivateDeclared() );
         assertNull( "Expected Modified Method not set", cm10.getModified() );
         assertEquals( "Expected Configuration Policy not set", ComponentMetadata.CONFIGURATION_POLICY_OPTIONAL, cm10
             .getConfigurationPolicy() );
@@ -83,7 +85,9 @@
         cm11.validate( logger );
         assertEquals( "DS Version 1.1", XmlHandler.DS_VERSION_1_1, cm11.getNamespaceCode() );
         assertEquals( "Expected Activate Method set", "myactivate", cm11.getActivate() );
+        assertTrue( "Activate method expected to be declared", cm11.isActivateDeclared() );
         assertEquals( "Expected Deactivate Method set", "mydeactivate", cm11.getDeactivate() );
+        assertTrue( "Activate method expected to be declared", cm11.isDeactivateDeclared() );
         assertEquals( "Expected Modified Method set", "mymodified", cm11.getModified() );
         assertEquals( "Expected Configuration Policy set", ComponentMetadata.CONFIGURATION_POLICY_IGNORE, cm11
             .getConfigurationPolicy() );
@@ -158,7 +162,9 @@
         // ds 1.1 elements
         assertEquals( "activate method", "myactivate", cm10.getActivate() );
         assertEquals( "deactivate method", "mydeactivate", cm10.getDeactivate() );
+        assertTrue( "Activate method expected to be declared", cm10.isActivateDeclared() );
         assertEquals( "modified method", "mymodified", cm10.getModified() );
+        assertTrue( "Deactivate method expected to be declared", cm10.isDeactivateDeclared() );
         assertEquals( "configuration policy", "ignore", cm10.getConfigurationPolicy() );
 
         // from the implementation element
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentActivationTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentActivationTest.java
new file mode 100644
index 0000000..510764c
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentActivationTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.integration;
+
+
+import junit.framework.TestCase;
+
+import org.apache.felix.scr.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+
+
+@RunWith(JUnit4TestRunner.class)
+public class ComponentActivationTest extends ComponentTestBase
+{
+
+    static
+    {
+        // use different components
+        descriptorFile = "/integration_test_activation_components.xml";
+
+        // uncomment to enable debugging of this test class
+        // paxRunnerVmOption = DEBUG_VM_OPTION;
+    }
+
+
+    @Test
+    public void test_activator_not_declared()
+    {
+        final String componentname = "ActivatorComponent.no.decl";
+
+        final Component component = findComponentByName( componentname );
+
+        TestCase.assertNotNull( component );
+        TestCase.assertFalse( component.isDefaultEnabled() );
+
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+
+        component.disable();
+
+        delay();
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+    }
+
+
+    @Test
+    public void test_activate_missing()
+    {
+        final String componentname = "ActivatorComponent.activate.missing";
+
+        final Component component = findComponentByName( componentname );
+
+        TestCase.assertNotNull( component );
+        TestCase.assertFalse( component.isDefaultEnabled() );
+
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        // activate must fail
+        TestCase.assertEquals( Component.STATE_UNSATISFIED, component.getState() );
+
+        component.disable();
+
+        delay();
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+    }
+
+
+    @Test
+    public void test_deactivate_missing()
+    {
+        final String componentname = "ActivatorComponent.deactivate.missing";
+
+        final Component component = findComponentByName( componentname );
+
+        TestCase.assertNotNull( component );
+        TestCase.assertFalse( component.isDefaultEnabled() );
+
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+
+        component.disable();
+
+        delay();
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+    }
+
+
+    @Test
+    public void test_activator_declared()
+    {
+        final String componentname = "ActivatorComponent.decl";
+
+        final Component component = findComponentByName( componentname );
+
+        TestCase.assertNotNull( component );
+        TestCase.assertFalse( component.isDefaultEnabled() );
+
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+
+        component.disable();
+
+        delay();
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+    }
+
+
+    @Test
+    public void test_activate_fail()
+    {
+        final String componentname = "ActivatorComponent.activate.fail";
+
+        final Component component = findComponentByName( componentname );
+
+        TestCase.assertNotNull( component );
+        TestCase.assertFalse( component.isDefaultEnabled() );
+
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        // activate has failed
+        TestCase.assertEquals( Component.STATE_UNSATISFIED, component.getState() );
+
+        component.disable();
+
+        delay();
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+    }
+
+
+    @Test
+    public void test_deactivate_fail()
+    {
+        final String componentname = "ActivatorComponent.deactivate.fail";
+
+        final Component component = findComponentByName( componentname );
+
+        TestCase.assertNotNull( component );
+        TestCase.assertFalse( component.isDefaultEnabled() );
+
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+
+        component.enable();
+        delay();
+
+        TestCase.assertEquals( Component.STATE_ACTIVE, component.getState() );
+
+        component.disable();
+
+        delay();
+        TestCase.assertEquals( Component.STATE_DISABLED, component.getState() );
+    }
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/ActivatorComponent.java b/scr/src/test/java/org/apache/felix/scr/integration/components/ActivatorComponent.java
new file mode 100644
index 0000000..0aa747f
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/integration/components/ActivatorComponent.java
@@ -0,0 +1,51 @@
+/*
+ * 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.integration.components;
+
+
+import java.util.Map;
+
+
+public class ActivatorComponent
+{
+
+    public static final String FLAG_FAIL_ACTIVATE = "failActivate";
+
+    public static final String FLAG_FAIL_DEACTIVATE = "failDeactivate";
+
+
+    @SuppressWarnings("unused")
+    private void myActivate( Map<?, ?> configuration )
+    {
+        if ( configuration.containsKey( FLAG_FAIL_ACTIVATE ) )
+        {
+            throw new IllegalStateException( "myActivate fails" );
+        }
+    }
+
+
+    @SuppressWarnings("unused")
+    private void myDeactivate( Map<?, ?> configuration )
+    {
+        if ( configuration.containsKey( FLAG_FAIL_DEACTIVATE ) )
+        {
+            throw new IllegalStateException( "myDeactivate fails" );
+        }
+    }
+}
diff --git a/scr/src/test/resources/integration_test_activation_components.xml b/scr/src/test/resources/integration_test_activation_components.xml
new file mode 100644
index 0000000..00ccba9
--- /dev/null
+++ b/scr/src/test/resources/integration_test_activation_components.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+
+    <!-- 
+        Components used for the ComponentActivationTest integration test.
+        This tests components with and without activate/deactivate method
+        configured and with and without activate/deactivate methods present
+    -->
+    
+    <!-- no declaration of activate/deactivate methods -->
+    <scr:component name="ActivatorComponent.no.decl"
+        enabled="false">
+        <implementation class="org.apache.felix.scr.integration.components.ActivatorComponent" />
+    </scr:component>
+
+    <!-- wrong declaration of activate method -->
+    <scr:component name="ActivatorComponent.activate.missing"
+        enabled="false"
+        activate="nonExistingActivate">
+        <implementation class="org.apache.felix.scr.integration.components.ActivatorComponent" />
+    </scr:component>
+
+    <!-- wrong declaration of deactivate method -->
+    <scr:component name="ActivatorComponent.deactivate.missing"
+        enabled="false"
+        deactivate="nonExistingDeactivate">
+        <implementation class="org.apache.felix.scr.integration.components.ActivatorComponent" />
+    </scr:component>
+
+    <!-- correct declaration of activate and deactivate method -->
+    <scr:component name="ActivatorComponent.decl"
+        enabled="false"
+        activate="myActivate"
+        deactivate="myDeactivate">
+        <implementation class="org.apache.felix.scr.integration.components.ActivatorComponent" />
+    </scr:component>
+
+    <!-- correct declaration of activate and deactivate method, activate failure -->
+    <scr:component name="ActivatorComponent.activate.fail"
+        enabled="false"
+        activate="myActivate"
+        deactivate="myDeactivate">
+        <implementation class="org.apache.felix.scr.integration.components.ActivatorComponent" />
+        <property name="failActivate" value="true" />
+    </scr:component>
+
+    <!-- correct declaration of activate and deactivate method, deactivate failure -->
+    <scr:component name="ActivatorComponent.deactivate.fail"
+        enabled="false"
+        activate="myActivate"
+        deactivate="myDeactivate">
+        <implementation class="org.apache.felix.scr.integration.components.ActivatorComponent" />
+        <property name="failDeactivate" value="true" />
+    </scr:component>
+</components>