[FELIX-2965] - Apply @Registered/@Unregistered annotation on composition instances.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1126383 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
index 958e16a..981c4ba 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/AnnotationCollector.java
@@ -335,8 +335,13 @@
         // properties attribute
         parseProperties(annotation, EntryParam.properties, writer);
 
-        // provides attribute
-        writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService);
+        // provides attribute.
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            // no service provided: check if @Registered/@Unregistered annotation are used
+            // and raise an error if true.
+            checkRegisteredUnregisteredNotPresent();
+        }
 
         // factorySet attribute
         String factorySetName = writer.putString(annotation, EntryParam.factorySet, null);
@@ -644,7 +649,10 @@
         parseProperties(annotation, EntryParam.properties, writer);
 
         // Parse the provided adapter service (use directly implemented interface by default).
-        writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService);
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            checkRegisteredUnregisteredNotPresent();
+        }
         
         // Parse factoryMethod attribute
         writer.putString(annotation, EntryParam.factoryMethod, null);
@@ -684,7 +692,10 @@
         parseProperties(annotation, EntryParam.properties, writer);
 
         // Parse the optional adapter service (use directly implemented interface by default).
-        writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService);
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            checkRegisteredUnregisteredNotPresent();
+        }
 
         // Parse propagate attribute
         writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
@@ -720,7 +731,10 @@
         parseProperties(annotation, EntryParam.properties, writer);
 
         // Parse the provided adapter service (use directly implemented interface by default).
-        writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService);
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            checkRegisteredUnregisteredNotPresent();
+        }
 
         // Parse propagate attribute
         writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
@@ -754,7 +768,10 @@
         writer.putString(annotation, EntryParam.propagate, Boolean.FALSE.toString());
 
         // Parse the provided adapter service (use directly implemented interface by default).
-        writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService);
+        if (writer.putClassArray(annotation, EntryParam.provides, m_interfaces, m_exportService) == 0)
+        {
+            checkRegisteredUnregisteredNotPresent();
+        }
 
         // Parse Adapter properties.
         parseProperties(annotation, EntryParam.properties, writer);
@@ -1008,6 +1025,27 @@
     }
 
     /**
+     * This method checks if the @Registered and/or @Unregistered annotations have been defined
+     * while they should not, because the component does not provide a service.
+     */
+    private void checkRegisteredUnregisteredNotPresent()
+    {
+        if (m_registeredMethod != null)
+        {
+            throw new IllegalStateException("@Registered annotation can't be used on a Component " +
+                                            " which does not provide a service (class=" + m_className + ")");
+
+        }
+        
+        if (m_unregisteredMethod != null)
+        {
+            throw new IllegalStateException("@Unregistered annotation can't be used on a Component " +
+                                            " which does not provide a service (class=" + m_className + ")");
+
+        }
+    }
+
+    /**
      * Get an annotation attribute, and return a default value if its not present.
      * @param <T> the type of the variable which is assigned to the return value of this method.
      * @param annotation The annotation we are parsing
diff --git a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
index 2a02076..ed40158 100644
--- a/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
+++ b/dependencymanager/annotation/src/main/java/org/apache/felix/dm/annotation/plugin/bnd/EntryWriter.java
@@ -221,8 +221,9 @@
     /**
      * Get a class array attribute value from an annotation and write it into this descriptor entry.
      * Also collect classes found from the array into a given Set.
+     * @return the class array size.
      */
-    public void putClassArray(Annotation annotation, EntryParam param, Object def, Set<String> collect)
+    public int putClassArray(Annotation annotation, EntryParam param, Object def, Set<String> collect)
     {
         checkType(param.toString());
 
@@ -258,7 +259,11 @@
                             + value.toString(), e);
                 }
             }
+            
+            return ((Object[]) value).length;
         }
+        
+        return 0;
     }
 
     /**
diff --git a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AbstractBuilder.java b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AbstractBuilder.java
index 2127c8e..c8e23b6 100644
--- a/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AbstractBuilder.java
+++ b/dependencymanager/runtime/src/main/java/org/apache/felix/dm/runtime/AbstractBuilder.java
@@ -144,18 +144,23 @@
         {
             if (m_registered != null)
             {
-                Object instance = c.getService();
-                try
+                // The component has registered a service: notify all component instances
+                Object[] componentInstances = c.getCompositionInstances();
+                for (Object instance : componentInstances)
                 {
-                    InvocationUtil
-                        .invokeCallbackMethod(instance,
-                                              m_registered, 
-                                              new Class[][]  {{ ServiceRegistration.class },  {}},
-                                              new Object[][] {{ c.getServiceRegistration() }, {}});
-                }
-                catch (Throwable t)
-                {
-                    Log.instance().error("Exception caught while invoking method %s on component %s", t, m_registered, instance);
+                    try
+                    {
+                        Class[][] signatures = new Class[][] { { ServiceRegistration.class }, {} };
+                        Object[][] params = new Object[][] { { c.getServiceRegistration() }, {} };
+                        InvocationUtil.invokeCallbackMethod(instance, m_registered, signatures, params);
+                    }
+                    catch (Throwable t)
+                    {
+                        Log.instance().error("Exception caught while invoking method %s on component %s",
+                                             t,
+                                             m_registered,
+                                             instance);
+                    }
                 }
             }
         }
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/CompositeService.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C1.java
similarity index 87%
rename from dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/CompositeService.java
rename to dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C1.java
index 418ee4d..5496f15 100644
--- a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/CompositeService.java
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C1.java
@@ -18,26 +18,28 @@
 import org.apache.felix.dm.annotation.api.Composition;
 import org.apache.felix.dm.annotation.api.Destroy;
 import org.apache.felix.dm.annotation.api.Init;
+import org.apache.felix.dm.annotation.api.Registered;
 import org.apache.felix.dm.annotation.api.ServiceDependency;
 import org.apache.felix.dm.annotation.api.Start;
 import org.apache.felix.dm.annotation.api.Stop;
 import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+import org.osgi.framework.ServiceRegistration;
 
 /**
  * This service is also composed of the Component object.
  */
 @Component
-public class CompositeService
+public class C1 implements C1Service
 {
     /* We are composed of this object, which will also be injected with our dependencies */
-    private Composite m_composite = new Composite();
+    private C2 m_c2 = new C2();
 
     /* This dependency filter will be configured from our init method */
     @ServiceDependency(name = "D")
     public volatile Runnable m_runnable;
     
     /* Object used to check that methods are called in the proper sequence */
-    @ServiceDependency(filter="(name=CompositeService)")
+    @ServiceDependency(filter="(name=C1)")
     private volatile Sequencer m_sequencer;
 
     /**
@@ -60,7 +62,7 @@
     @Composition
     Object[] getComposition()
     {
-        return new Object[] { this, m_composite };
+        return new Object[] { this, m_c2 };
     }
 
     /** 
@@ -76,12 +78,21 @@
     }
 
     /**
+     * Our provided service has been registered into the OSGi service registry.
+     */
+    @Registered
+    void registered(ServiceRegistration sr)
+    {
+        m_sequencer.step(7);
+    }
+    
+    /**
      *  Our Service is stopping, and our Composites will also be 
      */
     @Stop
     void stop()
     {
-        m_sequencer.step(7);
+        m_sequencer.step(9);
         // Our Component.stop() method should be called once this method returns.
     }
 
@@ -91,7 +102,7 @@
     @Destroy
     void destroy()
     {
-        m_sequencer.step(9);
+        m_sequencer.step(11);
         // Our Component.destroy() method should be called once this method returns.
     }
 }
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C1Service.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C1Service.java
new file mode 100644
index 0000000..a4ce49c
--- /dev/null
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C1Service.java
@@ -0,0 +1,5 @@
+package org.apache.felix.dm.test.bundle.annotation.composite;
+
+public interface C1Service
+{
+}
diff --git a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/Composite.java b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C2.java
similarity index 86%
rename from dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/Composite.java
rename to dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C2.java
index 3366735..9d486d0 100644
--- a/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/Composite.java
+++ b/dependencymanager/test/src/main/java/org/apache/felix/dm/test/bundle/annotation/composite/C2.java
@@ -12,11 +12,12 @@
 package org.apache.felix.dm.test.bundle.annotation.composite;
 
 import org.apache.felix.dm.test.bundle.annotation.sequencer.Sequencer;
+import org.osgi.framework.ServiceRegistration;
 
 /**
  * The CompositeService is also made up of this Class.
  */
-public class Composite
+public class C2
 {
     // Injected dependency (from CompositeService)
     private volatile Sequencer m_sequencer;
@@ -37,15 +38,24 @@
         m_runnable.run(); /* step 6 */
     }
 
+    void registered(ServiceRegistration sr)
+    {
+        if (sr == null)
+        {
+            m_sequencer.throwable(new Exception("null service registration"));
+        }
+        m_sequencer.step(8);
+    }
+
     // lifecycle callback (same method as the one from CompositeService)
     void stop()
     {
-        m_sequencer.step(8);
+        m_sequencer.step(10);
     }
 
     // lifecycle callback (same method as the one from CompositeService)
     void destroy()
     {
-        m_sequencer.step(10);
+        m_sequencer.step(12);
     }
 }
diff --git a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/CompositeAnnotationsTest.java b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/CompositeAnnotationsTest.java
index 6301e4f..6f89054 100644
--- a/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/CompositeAnnotationsTest.java
+++ b/dependencymanager/test/src/test/java/org/apache/felix/dm/test/annotation/CompositeAnnotationsTest.java
@@ -64,7 +64,7 @@
     {
         DependencyManager m = new DependencyManager(context);
         // Provide the Sequencer service to the "Component" service.
-        m.add(makeSequencer(m, "CompositeService"));
+        m.add(makeSequencer(m, "C1"));
         m.add(makeSequencer(m, "Dependency1"));
         m.add(makeSequencer(m, "Dependency2"));
         // Check if the components have been initialized orderly
@@ -72,6 +72,6 @@
         // Stop the bundle
         stopBundle("CompositeAnnotationsTest", context);
         // And check if the components lifecycle callbacks are called orderly
-        m_ensure.waitForStep(10, 10000);
+        m_ensure.waitForStep(12, 10000);
     }
 }