Fix FELIX-2461
Add a specification attribute to the @ServiceController
Modify the PRovidedServiceHandler to support this attribute
Parse this attribute in the manipulator
Add the test cases 

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@960227 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.java
index 06e238e..bd0dd5f 100644
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.java
+++ b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/ServiceController.java
@@ -32,5 +32,12 @@
      * Sets the initial value of the controller.

      */

     boolean value() default true;

+    

+    

+    /**

+     * Sets the targeted specification.

+     * If not set, target all specifications.

+     */

+    Class specification() default Object.class;

 

 }

diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
index 7357c9e..1f6169d 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedService.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Dictionary;
@@ -117,7 +118,7 @@
     /**
      * Service Controller.
      */
-    private ServiceController m_controller;
+    private Map /*<Specification, ServiceController>*/ m_controllers = new HashMap/*<Specification, ServiceController>*/();
 
     /**
      * Post-Registration callback.
@@ -327,16 +328,21 @@
      * This method also notifies the creation strategy of the publication.
      */
     protected synchronized void registerService() {
+        // Unregister if registered
+        if (m_serviceRegistration != null) {
+            unregisterService();
+        }
+        
         if (m_handler.getInstanceManager().getState() == ComponentInstance.VALID
-                && m_serviceRegistration == null  && (m_controller == null || m_controller.getValue())) {
+                && m_serviceRegistration == null  && isAtLeastAServiceControllerValid()) {
             // Build the service properties list
 
             BundleContext bc = m_handler.getInstanceManager().getContext();
             // Security check
             if (SecurityHelper.hasPermissionToRegisterServices(m_serviceSpecifications, bc)) {
                 Properties serviceProperties = getServiceProperties();
-                m_strategy.onPublication(getInstanceManager(), m_serviceSpecifications, serviceProperties);
-                m_serviceRegistration = bc.registerService(m_serviceSpecifications, this, serviceProperties);
+                m_strategy.onPublication(getInstanceManager(), getServiceSpecificationsToRegister(), serviceProperties);
+                m_serviceRegistration = bc.registerService(getServiceSpecificationsToRegister(), this, serviceProperties);
                 // An update may happen during the registration, re-check and apply.
                 if (m_wasUpdated) {
                     m_serviceRegistration.setProperties(getServiceProperties());
@@ -366,7 +372,7 @@
     protected synchronized void unregisterService() {
     	// Create a copy of the service reference in the case we need
     	// to inject it to the post-unregistration callback.
-
+        
     	ServiceReference ref = null;
         if (m_serviceRegistration != null) {
     		ref = m_serviceRegistration.getReference();
@@ -497,13 +503,87 @@
      * Sets the service controller on this provided service.
      * @param field the field attached to this controller
      * @param value the value the initial value
+     * @param specification the target specification, if <code>null</code>
+     * affect all specifications.
      */
-    public void setController(String field, boolean value) {
-        m_controller = new ServiceController(field, value);
+    public void setController(String field, boolean value, String specification) {
+        if (specification == null) {
+            m_controllers.put("ALL", new ServiceController(field, value));
+        } else {
+            m_controllers.put(specification, new ServiceController(field, value));
+
+        }
     }
 
-    public ServiceController getController() {
-        return m_controller;
+    public ServiceController getController(String field) {
+        Collection controllers = m_controllers.values();
+        Iterator iterator = controllers.iterator();
+        while (iterator.hasNext()) {
+            ServiceController controller = (ServiceController) iterator.next();
+            if (field.equals(controller.m_field)) {
+                return controller;
+            }
+        }
+        return null;
+    }
+    
+    public ServiceController getControllerBySpecification(String spec) {
+        return (ServiceController) m_controllers.get(spec);
+    }
+    
+    /**
+     * Checks if at least one service controller is valid.
+     * @return <code>true</code> if one service controller at least
+     * is valid.
+     */
+    private boolean isAtLeastAServiceControllerValid() {
+        Collection controllers = m_controllers.values();
+        
+        // No controller
+        if (controllers.isEmpty()) {
+            return true;
+        }
+        
+        Iterator iterator = controllers.iterator();
+        while (iterator.hasNext()) {
+            ServiceController controller = (ServiceController) iterator.next();
+            if (controller.getValue()) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private String[] getServiceSpecificationsToRegister() {
+        if (m_controllers.isEmpty()) {
+            return m_serviceSpecifications;
+        }
+        
+        ArrayList l = new ArrayList();
+        if (m_controllers.containsKey("ALL")) {
+            ServiceController ctrl = (ServiceController) m_controllers.get("ALL");
+            if (ctrl.m_value) {
+                l.addAll(Arrays.asList(m_serviceSpecifications));
+            }
+        }
+        
+        Iterator iterator = m_controllers.keySet().iterator();
+        while (iterator.hasNext()) {
+            String spec = (String) iterator.next();
+            ServiceController ctrl = (ServiceController) m_controllers.get(spec);
+            if (ctrl.m_value) {
+                if (! "ALL".equals(spec)) { // Already added.
+                    if (! l.contains(spec)) {
+                        l.add(spec);
+                    }
+                }
+            } else {
+                l.remove(spec);
+            }
+        }
+                
+        return (String[]) l.toArray(new String[l.size()]);
+        
     }
 
     public void setPostRegistrationCallback(Callback cb) {
@@ -562,7 +642,14 @@
                     if (m_value) {
                         registerService();
                     } else {
-                        unregisterService();
+                        // If we are still some specification valid, register those one
+                        // The registerService will call unregister.
+                        if (getServiceSpecificationsToRegister().length != 0) {
+                            registerService();
+                        } else {
+                            // If not, then unregister all
+                            unregisterService();
+                        }
                     }
                 }
             }
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java
index e184428..2973b41 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceDescription.java
@@ -109,10 +109,23 @@
      * @return the value
      */
     public String getController() {
-        if (m_ps.getController() == null) {
+        if (m_ps.getControllerBySpecification("ALL") == null) {
             return null; 
         } else {
-            return String.valueOf(m_ps.getController().getValue());
+            return String.valueOf(m_ps.getControllerBySpecification("ALL").getValue());
+        }
+    }
+    
+    /**
+     * Gets the controller value as a String.
+     * @param specification
+     * @return the value
+     */
+    public String getController(String specification) {
+        if (m_ps.getControllerBySpecification(specification) == null) {
+            return null; 
+        } else {
+            return String.valueOf(m_ps.getControllerBySpecification(specification).getValue());
         }
     }
 
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
index 97c25fd..e91beab 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/handlers/providedservice/ProvidedServiceHandler.java
@@ -35,6 +35,7 @@
 import org.apache.felix.ipojo.architecture.PropertyDescription;
 import org.apache.felix.ipojo.handlers.dependency.Dependency;
 import org.apache.felix.ipojo.handlers.dependency.DependencyHandler;
+import org.apache.felix.ipojo.handlers.providedservice.ProvidedService.ServiceController;
 import org.apache.felix.ipojo.metadata.Attribute;
 import org.apache.felix.ipojo.metadata.Element;
 import org.apache.felix.ipojo.parser.FieldMetadata;
@@ -189,20 +190,22 @@
 
             Element[] controllers = providedServices[i].getElements("Controller");
             if (controllers != null) {
-                if (controllers.length > 1) {
-                    throw new ConfigurationException("Cannot have several controller per 'provides' element");
+                for (int k = 0; k < controllers.length; k++) {
+                    String field = controllers[k].getAttribute("field");
+                    if (field == null) {
+                        throw new ConfigurationException("The field attribute of a controller is mandatory");
+                    }
+
+                    String v = controllers[k].getAttribute("value");
+                    boolean value = ! (v != null  && v.equalsIgnoreCase("false"));
+                    String s = controllers[k].getAttribute("specification");
+                    if (s == null) {
+                        s ="ALL";
+                    }
+                    svc.setController(field, value, s);
+
+                    getInstanceManager().register(new FieldMetadata(field, "boolean"), this);
                 }
-
-                String field = controllers[0].getAttribute("field");
-                if (field == null) {
-                    throw new ConfigurationException("The field attribute of a controller is mandatory");
-                }
-
-                String v = controllers[0].getAttribute("value");
-                boolean value = ! (v != null  && v.equalsIgnoreCase("false"));
-                svc.setController(field, value);
-
-                getInstanceManager().register(new FieldMetadata(field, "boolean"), this);
             }
 
             if (checkProvidedService(svc)) {
@@ -428,9 +431,10 @@
             if (update) {
                 svc.update();
             }
-            if (svc.getController() != null  && svc.getController().getField().equals(fieldName)) {
+            ServiceController ctrl = svc.getController(fieldName);
+            if (ctrl != null) {
                 if (value instanceof Boolean) {
-                    svc.getController().setValue((Boolean) value);
+                    ctrl.setValue((Boolean) value);
                 } else {
                     warn("Boolean value expected for the service controler " + fieldName);
                 }
@@ -459,8 +463,9 @@
                     return prop.onGet(pojo, fieldName, value);
                 }
             }
-            if (svc.getController() != null  && svc.getController().getField().equals(fieldName)) {
-                return new Boolean(svc.getController().getValue());
+            ServiceController ctrl = svc.getController(fieldName);
+            if (ctrl != null) {
+                return new Boolean(ctrl.getValue());
             }
         }
         // Else it is not a property
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java
index a2918e8..2d4e7d8 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java
@@ -408,7 +408,12 @@
         /**

          * Property value.  

          */

-        private String m_value;        

+        private String m_value;

+        

+        /**

+         * Specification value.  

+         */

+        private String m_spec;   

         

         /**

          * Constructor.

@@ -431,6 +436,10 @@
                 m_value = arg1.toString();

                 return;

             }

+            if (arg0.equals("specification")) {

+                m_spec = ((Type) arg1).getClassName();

+                return;

+            } 

         }

 

         /**

@@ -446,6 +455,9 @@
             if (m_value != null) {

                 controller.addAttribute(new Attribute("value", m_value));

             }

+            if (m_spec != null) {

+                controller.addAttribute(new Attribute("specification", m_spec));

+            }

         }

     }

 }

diff --git a/ipojo/tests/core/annotations/pom.xml b/ipojo/tests/core/annotations/pom.xml
index a7f69ff..b3aa3da 100644
--- a/ipojo/tests/core/annotations/pom.xml
+++ b/ipojo/tests/core/annotations/pom.xml
@@ -113,6 +113,7 @@
       <plugin>

         <groupId>org.apache.felix</groupId>

         <artifactId>maven-ipojo-plugin</artifactId>

+        <version>1.7.0-SNAPSHOT</version>

         <executions>

           <execution>

             <goals>

diff --git a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java
index 15c5813..e94c603 100644
--- a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java
+++ b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java
@@ -91,6 +91,17 @@
         assertEquals(1, provs[0].getElements("controller").length);

         assertEquals("false", provs[0].getElements("controller")[0].getAttribute("value"));

     }

+    

+    public void testServiceControllerWithSpecification() {

+        Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.PSServiceControllerSpec");

+        Element[] provs = meta.getElements("provides");

+        assertNotNull("Provides exists ", provs);

+        System.out.println(provs[0].toString());

+        assertNotNull(provs[0].getElements("controller"));

+        assertEquals(2, provs[0].getElements("controller").length);

+        assertEquals("false", provs[0].getElements("controller")[0].getAttribute("value"));

+        assertEquals(FooService.class.getName(), provs[0].getElements("controller")[0].getAttribute("specification"));

+    }

 

     private Element getPropertyByName(Element[] props, String name) {

         for (int i = 0; i < props.length; i++) {

diff --git a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/PSServiceControllerSpec.java b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/PSServiceControllerSpec.java
new file mode 100644
index 0000000..80e6540
--- /dev/null
+++ b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/PSServiceControllerSpec.java
@@ -0,0 +1,57 @@
+package org.apache.felix.ipojo.test.scenarios.component;

+

+import java.util.Properties;

+

+import org.apache.felix.ipojo.annotations.Component;

+import org.apache.felix.ipojo.annotations.Provides;

+import org.apache.felix.ipojo.annotations.ServiceController;

+import org.apache.felix.ipojo.test.scenarios.annotations.service.BarService;

+import org.apache.felix.ipojo.test.scenarios.annotations.service.FooService;

+

+@Component

+@Provides(specifications= {FooService.class, BarService.class})

+public class PSServiceControllerSpec implements FooService, BarService {

+

+    @ServiceController(value=false, specification=FooService.class)

+    public boolean controller1;

+    

+    @ServiceController(value=true)

+    public boolean controller2;

+    

+    public boolean foo() {

+        return false;

+    }

+

+    public Properties fooProps() {

+        return null;

+    }

+

+    public boolean getBoolean() {

+        return false;

+    }

+

+    public double getDouble() {

+        return 0;

+    }

+

+    public int getInt() {

+        return 0;

+    }

+

+    public long getLong() {

+        return 0;

+    }

+

+    public Boolean getObject() {

+        return null;

+    }

+

+    public boolean bar() {

+        return false;

+    }

+

+    public Properties getProps() {

+        return null;

+    }

+

+}

diff --git a/ipojo/tests/core/service-providing/src/main/java/org/apache/felix/ipojo/test/scenarios/ps/ServiceControllerTest.java b/ipojo/tests/core/service-providing/src/main/java/org/apache/felix/ipojo/test/scenarios/ps/ServiceControllerTest.java
index c325ece..4261d3e 100644
--- a/ipojo/tests/core/service-providing/src/main/java/org/apache/felix/ipojo/test/scenarios/ps/ServiceControllerTest.java
+++ b/ipojo/tests/core/service-providing/src/main/java/org/apache/felix/ipojo/test/scenarios/ps/ServiceControllerTest.java
@@ -127,6 +127,100 @@
         ci.dispose();

     }

     

+    public void testComponentWithTwoControllersUsingBothSpecificationsTrueFalse() {

+        ComponentInstance ci = helper.createComponentInstance("PS-Controller-2-spec1");

+

+        waitForService(CheckService.class.getName(), null, 5000);

+        

+        assertFalse(isServiceAvailable(FooService.class.getName()));

+

+        CheckService check = (CheckService) getServiceObject(CheckService.class.getName(), null);

+        assertNotNull(check);

+        

+        check.getProps();

+        

+        assertFalse(isServiceAvailable(CheckService.class.getName()));

+        assertTrue(isServiceAvailable(FooService.class.getName()));

+       

+        FooService fs = (FooService) getServiceObject(FooService.class.getName(), null);

+        fs.fooProps();

+        

+        assertTrue(isServiceAvailable(CheckService.class.getName()));

+        assertTrue(isServiceAvailable(FooService.class.getName()));

+        

+        ci.dispose();

+    }

+    

+    public void testComponentWithTwoControllersUsingBothSpecificationsTrueTrue() {

+        ComponentInstance ci = helper.createComponentInstance("PS-Controller-2-spec2");

+

+        waitForService(CheckService.class.getName(), null, 5000);

+        waitForService(FooService.class.getName(), null, 5000);

+

+        CheckService check = (CheckService) getServiceObject(CheckService.class.getName(), null);

+        assertNotNull(check);

+        

+        check.check();

+        // CheckService not available

+        assertNull(getServiceReference(CheckService.class.getName()));

+        assertNotNull(getServiceReference(FooService.class.getName()));

+

+        FooService fs = (FooService) getServiceObject(FooService.class.getName(), null);

+        fs.foo();

+        

+        assertNull(getServiceReference(CheckService.class.getName()));

+        assertNull(getServiceReference(FooService.class.getName()));

+        

+        ci.dispose();

+    }

+    

+    public void testComponentWithTwoControllersUsingSpecificationAndAllTrueTrue() {

+        ComponentInstance ci = helper.createComponentInstance("PS-Controller-2-spec3");

+

+        waitForService(CheckService.class.getName(), null, 5000);

+        waitForService(FooService.class.getName(), null, 5000);

+

+        CheckService check = (CheckService) getServiceObject(CheckService.class.getName(), null);

+        assertNotNull(check);

+        

+        check.check();

+        // CheckService not available

+        assertNull(getServiceReference(CheckService.class.getName()));

+        assertNotNull(getServiceReference(FooService.class.getName()));

+

+        FooService fs = (FooService) getServiceObject(FooService.class.getName(), null);

+        fs.foo();

+        

+        assertNull(getServiceReference(CheckService.class.getName()));

+        assertNull(getServiceReference(FooService.class.getName()));

+        

+        ci.dispose();

+    }

+    

+    public void testComponentWithTwoControllersUsingSpecificationAndAllTrueFalse() {

+        ComponentInstance ci = helper.createComponentInstance("PS-Controller-2-spec4");

+

+        waitForService(CheckService.class.getName(), null, 5000);

+        

+        assertFalse(isServiceAvailable(FooService.class.getName()));

+

+        CheckService check = (CheckService) getServiceObject(CheckService.class.getName(), null);

+        assertNotNull(check);

+        

+        check.getProps();

+        

+        assertFalse(isServiceAvailable(CheckService.class.getName()));

+        assertTrue(isServiceAvailable(FooService.class.getName()));

+       

+        FooService fs = (FooService) getServiceObject(FooService.class.getName(), null);

+        fs.fooProps();

+        

+        assertTrue(isServiceAvailable(CheckService.class.getName()));

+        assertTrue(isServiceAvailable(FooService.class.getName()));

+        

+        ci.dispose();

+    }

+    

     public void testArchitecture() {

         ComponentInstance ci = helper.createComponentInstance("PS-Controller-1-default");

         // Controller set to true.

diff --git a/ipojo/tests/core/service-providing/src/main/resources/metadata.xml b/ipojo/tests/core/service-providing/src/main/resources/metadata.xml
index 9ed6050..1d6fa95 100644
--- a/ipojo/tests/core/service-providing/src/main/resources/metadata.xml
+++ b/ipojo/tests/core/service-providing/src/main/resources/metadata.xml
@@ -1,6 +1,5 @@
 <ipojo

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

- xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/SNAPSHOT/core.xsd"

  xmlns="org.apache.felix.ipojo"

 >

     <!-- Simple provider  -->

@@ -209,6 +208,48 @@
             <controller field="controllerCS" value="true"/>

         </provides>

     </component>

+    

+    <component classname="org.apache.felix.ipojo.test.scenarios.component.controller.DoubleControllerCheckService"

+     name="PS-Controller-2-spec1">

+        <provides>

+            <property name="test2" type="string" value="test2"/>

+            <controller field="controllerFoo" value="false" specification="org.apache.felix.ipojo.test.scenarios.ps.service.FooService"/>

+            <controller field="controllerCS" value="true" specification="org.apache.felix.ipojo.test.scenarios.ps.service.CheckService"/>

+            <property name="test" type="string" value="test"/>

+        </provides>

+    </component>

+    

+    <component classname="org.apache.felix.ipojo.test.scenarios.component.controller.DoubleControllerCheckService"

+     name="PS-Controller-2-spec2">

+        <provides>

+            <property name="test2" type="string" value="test2"/>

+            <controller field="controllerFoo" value="true" specification="org.apache.felix.ipojo.test.scenarios.ps.service.FooService"/>

+            <controller field="controllerCS" value="true" specification="org.apache.felix.ipojo.test.scenarios.ps.service.CheckService"/>

+            <property name="test" type="string" value="test"/>

+        </provides>

+    </component>

+    

+    <component classname="org.apache.felix.ipojo.test.scenarios.component.controller.DoubleControllerCheckService"

+     name="PS-Controller-2-spec3">

+        <provides>

+            <property name="test2" type="string" value="test2"/>

+            <controller field="controllerFoo" value="true" specification="org.apache.felix.ipojo.test.scenarios.ps.service.FooService"/>

+            <controller field="controllerCS" value="true"/>

+            <property name="test" type="string" value="test"/>

+        </provides>

+    </component>

+    

+    <component classname="org.apache.felix.ipojo.test.scenarios.component.controller.DoubleControllerCheckService"

+     name="PS-Controller-2-spec4">

+        <provides>

+            <property name="test2" type="string" value="test2"/>

+            <controller field="controllerFoo" value="false" specification="org.apache.felix.ipojo.test.scenarios.ps.service.FooService"/>

+            <controller field="controllerCS" value="true"/>

+            <property name="test" type="string" value="test"/>

+        </provides>

+    </component>

+    

+    

 

     <!-- Anonymous classes -->

     <component