Fix FELIX-4236 Unvalued properties should be part of the instance's architecture



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1525393 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/core-it/ipojo-core-configuration-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/arch/MyComponentToIntrospect.java b/ipojo/runtime/core-it/ipojo-core-configuration-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/arch/MyComponentToIntrospect.java
new file mode 100644
index 0000000..8ebb157
--- /dev/null
+++ b/ipojo/runtime/core-it/ipojo-core-configuration-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/arch/MyComponentToIntrospect.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ipojo.runtime.core.components.arch;
+
+import org.apache.felix.ipojo.annotations.Component;
+import org.apache.felix.ipojo.annotations.Property;
+import org.apache.felix.ipojo.annotations.Provides;
+import org.apache.felix.ipojo.runtime.core.services.CheckService;
+
+import java.util.Properties;
+
+/**
+ * A component with properties with/without default values to check that the architecture contains all the required
+ * information.
+ *
+ * <ul>
+ *     <li>p1 : the property has no value, the field has one</li>
+ *     <li>p2 : the property has no value, it receives one in the constructor</li>
+ *     <li>p3 : the property has a default value</li>
+ *     <li>p4 : the property has a default value, overridden by the field value</li>
+ *     <li>p5 : the property has a default value, overridden by the instance configuration</li>
+ *     <li>p6 : the property has no value, the field is not initialized (not initialized means no value)</li>
+ *     <li>p62 : the property has no value, the field is initialized to null </li>
+ *     <li>p7 : the property has no value, it receives one in the constructor, but stay unused</li>
+ *     <li>p8 : the property has no value, it does not receive one.</li>
+ *     <li>p9 : the property has no value, it receives one in a method.</li>
+ * </ul>
+ */
+@Component(propagation = true)
+@Provides
+public class MyComponentToIntrospect implements CheckService {
+
+    @Property(name="p2")
+    private final String p2;
+
+    @Property
+    private String p1 = "v1";
+
+    @Property(name="p3", value = "v3")
+    private String p3;
+
+    @Property(value = "v4")
+    private String p4 = "v42";
+
+    @Property(value = "v5")
+    private String p5 = "v52";
+
+    @Property
+    private String p6;
+
+    @Property
+    private String p62 = null;
+
+    public MyComponentToIntrospect(@Property(name="p7") String v7) {
+        p2 = "v2";
+    }
+
+
+    @Property(name="p8")
+    public void setP8(String v8) {
+
+    }
+
+    @Property(name="p9")
+    public void setP9(String v9) {
+
+    }
+
+    @Override
+    public boolean check() {
+        return true;
+    }
+
+    @Override
+    public Properties getProps() {
+        return null;
+    }
+}
diff --git a/ipojo/runtime/core-it/ipojo-core-configuration-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestArchitecture.java b/ipojo/runtime/core-it/ipojo-core-configuration-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestArchitecture.java
index 68a0a5d..8cbfc96 100644
--- a/ipojo/runtime/core-it/ipojo-core-configuration-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestArchitecture.java
+++ b/ipojo/runtime/core-it/ipojo-core-configuration-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestArchitecture.java
@@ -20,18 +20,22 @@
 package org.apache.felix.ipojo.runtime.core;
 
 import org.apache.felix.ipojo.ComponentInstance;
+import org.apache.felix.ipojo.PrimitiveInstanceDescription;
 import org.apache.felix.ipojo.architecture.Architecture;
+import org.apache.felix.ipojo.architecture.PropertyDescription;
 import org.apache.felix.ipojo.handlers.configuration.ConfigurationHandlerDescription;
+import org.apache.felix.ipojo.runtime.core.services.CheckService;
+import org.apache.felix.ipojo.util.Property;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.osgi.framework.ServiceReference;
 
 import java.util.Dictionary;
 import java.util.Hashtable;
 
 import static junit.framework.Assert.*;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
 
 public class TestArchitecture extends Common {
 
@@ -149,5 +153,84 @@
         assertNull(arch);
     }
 
+    /**
+     * Checks the introspection possibilities before and after object creation.
+     * Especially check the 'unvalued' case.
+     */
+    @Test
+    public void testIntrospection() {
+        Dictionary<String, String> configuration = new Hashtable<String, String>();
+        configuration.put("p5", "v5i");
+        configuration.put("p7", "v7i");
+        configuration.put("p9", "v9i");
+
+        ComponentInstance instance = ipojoHelper.createComponentInstance(
+                "org.apache.felix.ipojo.runtime.core.components.arch.MyComponentToIntrospect",
+                configuration);
+
+        // We don't get the service object until we finished the pre-instantiation tests.
+        ServiceReference reference = osgiHelper.waitForService(CheckService.class.getName(),
+                "(instance.name=" + instance.getInstanceName() + ")", 1000);
+
+        assertNotNull(reference);
+
+        PropertyDescription[] properties = ((PrimitiveInstanceDescription) instance.getInstanceDescription())
+                .getProperties();
+
+        assertNotNull(properties);
+
+        // Check the properties.
+        assertEquals(getProperty(properties, "p1").getValue(), Property.UNVALUED);
+        assertEquals(getProperty(properties, "p2").getValue(), Property.UNVALUED);
+        assertEquals(getProperty(properties, "p3").getValue(), "v3"); // Default value
+        assertEquals(getProperty(properties, "p4").getValue(), "v4"); // Default value
+        assertEquals(getProperty(properties, "p5").getValue(), "v5i"); // Instance value
+        assertEquals(getProperty(properties, "p6").getValue(), Property.UNVALUED);
+        assertEquals(getProperty(properties, "p6").getValue(), Property.UNVALUED);
+        assertEquals(getProperty(properties, "p7").getValue(), "v7i"); // Instance value
+        assertEquals(getProperty(properties, "p8").getValue(), Property.UNVALUED);
+        assertEquals(getProperty(properties, "p9").getValue(), "v9i"); // Instance value
+
+        // Check the propagation
+        assertNull(reference.getProperty("p1"));
+        assertNull(reference.getProperty("p2"));
+        assertEquals(reference.getProperty("p3"), "v3");
+        assertEquals(reference.getProperty("p4"), "v4");
+        assertEquals(reference.getProperty("p5"), "v5i");
+        assertNull(reference.getProperty("p6"));
+        assertNull(reference.getProperty("p62"));
+        assertEquals(reference.getProperty("p7"), "v7i");
+        assertNull(reference.getProperty("p8"));
+        assertEquals(reference.getProperty("p9"), "v9i");
+
+        // Trigger instantiation
+        assertTrue(((CheckService) context.getService(reference)).check());
+
+        // Check new value.
+        assertEquals(getProperty(properties, "p1").getValue(), "v1");
+        assertEquals(getProperty(properties, "p2").getValue(), "v2");
+        assertEquals(getProperty(properties, "p3").getValue(), "v3"); // Default value
+        assertEquals(getProperty(properties, "p4").getValue(), "v42"); // Default value
+        assertEquals(getProperty(properties, "p5").getValue(), "v52"); // Field value
+        assertEquals(getProperty(properties, "p6").getValue(), Property.UNVALUED); // Specific value used for null
+        assertEquals(getProperty(properties, "p62").getValue(), "null"); // Specific value used for null
+        assertEquals(getProperty(properties, "p7").getValue(), "v7i"); // Instance value
+        assertEquals(getProperty(properties, "p8").getValue(), Property.UNVALUED);
+        assertEquals(getProperty(properties, "p9").getValue(), "v9i"); // Instance value
+
+        // New valued properties are not propagated.
+        // It avoids having fluctuation ins the service registrations
+        // To enable this propagation use @ServiceProperty
+    }
+
+    private PropertyDescription getProperty(PropertyDescription[] props, String name) {
+        for (PropertyDescription desc : props) {
+            if (desc.getName().equals(name)) {
+                return desc;
+            }
+        }
+        return null;
+    }
+
 
 }
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java
index 4b037b4..a5a528f 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/architecture/PropertyDescription.java
@@ -132,6 +132,8 @@
             Object value =  m_property.getValue();
             if (value == null) {
                 return "null";
+            } else if (value == Property.NO_VALUE) {
+                return Property.UNVALUED;
             } else {
                 return value.toString();
             }
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
index 6ee8e4e..5942b3e 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/handlers/configuration/ConfigurationHandler.java
@@ -354,7 +354,19 @@
                     m_toPropagate.put(prop.getName(), prop.getValue());
                 }
             }
-            reconfigure(m_toPropagate);
+
+            // We cannot use the reconfigure method directly, as there are no real changes.
+            Properties extra = reconfigureProperties(m_toPropagate);
+            propagate(extra, m_propagatedFromInstance);
+            m_propagatedFromInstance = extra;
+
+            if (getInstanceManager().getPojoObjects() != null) {
+                try {
+                    notifyUpdated(null);
+                } catch (Throwable e) {
+                    error("Cannot call the updated method : " + e.getMessage(), e);
+                }
+            }
         }
 
 
@@ -446,7 +458,6 @@
                     || name.equals(MANAGED_SERVICE_PID)) {
                 continue;
             }
-
             // Do we have a property.
             Property p = getPropertyByName(name);
             if (p != null) {
@@ -623,7 +634,7 @@
     /**
      * Handler createInstance method.
      * This method is override to allow delayed callback invocation.
-     * Invokes the updated method is needed.
+     * Invokes the updated method if needed.
      *
      * @param instance : the created object
      * @see org.apache.felix.ipojo.PrimitiveHandler#onCreation(Object)
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java
index 301b5b3..0ebc880 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/util/Property.java
@@ -46,6 +46,11 @@
     public static final Object NO_VALUE = new Object();

 

     /**

+     * String value returned for property without values.

+     */

+    public static final String UNVALUED = "UNVALUED";

+

+    /**

      * The name of the property (field name if not set).

      * Cannot change once set.

      */