Fix FELIX-2622 Support static service properties that are not mirrored into fields
Introduce a new StaticServiceProperty annotation:
@Component
@Provides(
specifications= {FooService.class, BarService.class},
properties= {
@StaticServiceProperty(name="prop1", value="prop1", type="java.lang.String"),
@StaticServiceProperty(name="prop2", type="java.lang.String"),
@StaticServiceProperty(name="props", value="{prop1, prop2}", type="string[]"),
@StaticServiceProperty(name="mandatory1", mandatory=true, type="string")
})
To improve the error management, we can't use the ServiceProperty annotation as the type attribute is mandatory.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1001175 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
index a2780d7..c58c3ef 100644
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
+++ b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -27,16 +27,16 @@
*/
@Target(ElementType.TYPE)
public @interface Provides {
-
+
/**
* Set the provided specifications.
* Default : all implemented interfaces
*/
Class[] specifications() default { };
-
+
/**
* Set the service object creation strategy.
- * Two value are possible: SINGLETON, SERVICE, METHOD, INSTANCE or the strategy class name.
+ * Two value are possible: SINGLETON, SERVICE, METHOD, INSTANCE or the strategy class name.
* SERVICE means OSGi Service Factory.
* METHOD delegates the creation to the factory-method of the component
* INSTANCE creates one service object per requiring instance
@@ -44,4 +44,14 @@
* Default : SINGLETON
*/
String strategy() default "SINGLETON";
+
+ /**
+ * Allows adding static properties to the service.
+ * Nested properties are static service properties,
+ * so <b>must</b> contain the name and the value as
+ * they are not attached to a field.
+ * The array contains {@link StaticServiceProperty} elements.
+ * Default : No service properties
+ */
+ StaticServiceProperty[] properties() default {};
}
diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java
new file mode 100644
index 0000000..f9ac981
--- /dev/null
+++ b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.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.ipojo.annotations;
+
+
+/**
+ * This annotation declares a static service property.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public @interface StaticServiceProperty {
+
+ /**
+ * Set the property name.
+ * This name is mandatory for static properties.
+ */
+ String name();
+
+ /**
+ * Set the property value.
+ * Default : empty
+ */
+ String value() default "";
+
+ /**
+ * Is the property mandatory?
+ * Default: false
+ */
+ boolean mandatory() default false;
+
+ /**
+ * Set the type.
+ * This value is required only for static properties.
+ */
+ String type();
+}
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 2d4e7d8..31905d6 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
@@ -291,106 +291,6 @@
}
/**
- * Parses a Property annotation.
- */
- private static final class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {
-
- /**
- * Parent element element.
- */
- private Element m_parent;
-
- /**
- * Field name.
- */
- private String m_field;
-
- /**
- * Property name.
- */
- private String m_name;
-
- /**
- * Property value.
- */
- private String m_value;
-
- /**
- * Property mandatory aspect.
- */
- private String m_mandatory;
-
-
- /**
- * Constructor.
- * @param parent : parent element.
- * @param field : field name.
- */
- private PropertyAnnotationParser(String field, Element parent) {
- m_parent = parent;
- m_field = field;
- }
-
- /**
- * Visit one "simple" annotation.
- * @param arg0 : annotation name
- * @param arg1 : annotation value
- * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
- */
- public void visit(String arg0, Object arg1) {
- if (arg0.equals("name")) {
- m_name = arg1.toString();
- return;
- }
- if (arg0.equals("value")) {
- m_value = arg1.toString();
- return;
- }
- if (arg0.equals("mandatory")) {
- m_mandatory = arg1.toString();
- return;
- }
- }
-
- /**
- * End of the annotation.
- * Create a "property" element
- * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
- */
- public void visitEnd() {
- if (m_name == null) {
- m_name = m_field;
- }
-
- Element[] props = m_parent.getElements("Property");
- Element prop = null;
- for (int i = 0; prop == null && props != null && i < props.length; i++) {
- String name = props[i].getAttribute("name");
- if (name != null && name.equals(m_name)) {
- prop = props[i];
- }
- }
-
- if (prop == null) {
- prop = new Element("property", "");
- m_parent.addElement(prop);
- if (m_name != null) {
- prop.addAttribute(new Attribute("name", m_name));
- }
- }
-
- prop.addAttribute(new Attribute("field", m_field));
- if (m_value != null) {
- prop.addAttribute(new Attribute("value", m_value));
- }
- if (m_mandatory != null) {
- prop.addAttribute(new Attribute("mandatory", m_mandatory));
- }
-
- }
- }
-
- /**
* Parses a ServiceController annotation.
*/
private static final class ServiceControllerAnnotationParser extends EmptyVisitor implements AnnotationVisitor {
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
index 1543052..aaa8767 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
@@ -1,4 +1,4 @@
-/*
+/*
* 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
@@ -64,9 +64,9 @@
* True if the visited class is a component type declaration (i.e. contains the @component annotation).
*/
private boolean m_containsComponentAnnotation = false;
-
+
private boolean m_ignoredBecauseOfMissingComponent = false;
-
+
/**
* Map of [element ids, element].
* This map is used to easily get an already created element.
@@ -79,7 +79,7 @@
* Stored element are added under referred element.
*/
private Map m_elements = new HashMap();
-
+
/**
* Instance declaration.
*/
@@ -94,23 +94,23 @@
public Element getComponentTypeDeclaration() {
return m_elem;
}
-
+
public Element getInstanceDeclaration() {
return m_instance;
}
-
+
public boolean isComponentType() {
return m_containsComponentAnnotation;
}
-
+
public boolean isIgnoredBecauseOfMissingComponent() {
return m_ignoredBecauseOfMissingComponent;
}
-
+
public String getClassName() {
return m_className;
}
-
+
/**
* Start visiting a class.
* Initialize the getter/setter generator, add the _cm field, add the pojo interface.
@@ -165,7 +165,7 @@
if (desc.equals("Lorg/apache/felix/ipojo/annotations/HandlerDeclaration;")) {
return new HandlerDeclarationVisitor();
}
-
+
// @Instantiate
if (desc.equals("Lorg/apache/felix/ipojo/annotations/Instantiate;")) {
return new InstantiateVisitor();
@@ -220,7 +220,7 @@
m_ignoredBecauseOfMissingComponent = true;
return;
}
-
+
// Recompute the tree
Set elems = getElements().keySet();
Iterator it = elems.iterator();
@@ -308,6 +308,14 @@
public AnnotationVisitor visitArray(String arg0) {
if (arg0.equals("specifications")) {
return new InterfaceArrayVisitor();
+ } else if (arg0.equals("properties")) {
+ // Create a new simple visitor to visit the nested ServiceProperty annotations
+ // Collected properties are collected in m_prov
+ return new EmptyVisitor() {
+ public AnnotationVisitor visitAnnotation(String ignored, String desc) {
+ return new PropertyAnnotationParser(m_prov);
+ }
+ };
} else {
return null;
}
@@ -354,17 +362,18 @@
}
+
}
-
+
/**
* Parse the @Instantitate annotation.
*/
private class InstantiateVisitor extends EmptyVisitor implements AnnotationVisitor {
/**
- * Instance name.
+ * Instance name.
*/
private String m_name;
-
+
/**
* Visit an annotation attribute.
* @param arg0 the attribute name
@@ -377,7 +386,7 @@
return;
}
}
-
+
/**
* End of the visit. Creates the instance element.
* @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
@@ -393,7 +402,7 @@
}
}
-
+
/**
* Parse the @component annotation.
*/
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/PropertyAnnotationParser.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/PropertyAnnotationParser.java
new file mode 100644
index 0000000..548fd23
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/PropertyAnnotationParser.java
@@ -0,0 +1,150 @@
+/*
+ * 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.manipulation.annotations;
+
+import org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * Parses a Property or ServiceProperty annotation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {
+
+ /**
+ * Parent element element.
+ */
+ private Element m_parent;
+
+ /**
+ * Field name.
+ */
+ private String m_field;
+
+ /**
+ * Property name.
+ */
+ private String m_name;
+
+ /**
+ * Property value.
+ */
+ private String m_value;
+
+ /**
+ * Property mandatory aspect.
+ */
+ private String m_mandatory;
+
+ /**
+ * Property type.
+ */
+ private String m_type;
+
+
+ /**
+ * Constructor.
+ * @param parent : parent element.
+ * @param field : field name.
+ */
+ PropertyAnnotationParser(String field, Element parent) {
+ m_parent = parent;
+ m_field = field;
+ }
+
+ /**
+ * Constructor without field
+ * @param parent : parent element..
+ */
+ PropertyAnnotationParser(Element parent) {
+ m_parent = parent;
+ m_field = null;
+ }
+
+ /**
+ * Visit one "simple" annotation.
+ * @param arg0 : annotation name
+ * @param arg1 : annotation value
+ * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
+ */
+ public void visit(String arg0, Object arg1) {
+ if (arg0.equals("name")) {
+ m_name = arg1.toString();
+ return;
+ }
+ if (arg0.equals("value")) {
+ m_value = arg1.toString();
+ return;
+ }
+ if (arg0.equals("mandatory")) {
+ m_mandatory = arg1.toString();
+ return;
+ }
+ if (arg0.equals("type")) {
+ m_type = arg1.toString();
+ return;
+ }
+ }
+
+ /**
+ * End of the annotation.
+ * Create a "property" element
+ * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
+ */
+ public void visitEnd() {
+ if (m_field != null && m_name == null) {
+ m_name = m_field;
+ }
+
+
+ Element[] props = m_parent.getElements("Property");
+ Element prop = null;
+ for (int i = 0; prop == null && props != null && i < props.length; i++) {
+ String name = props[i].getAttribute("name");
+ if (name != null && name.equals(m_name)) {
+ prop = props[i];
+ }
+ }
+
+ if (prop == null) {
+ prop = new Element("property", "");
+ m_parent.addElement(prop);
+ if (m_name != null) {
+ prop.addAttribute(new Attribute("name", m_name));
+ }
+ }
+
+ if (m_field != null) {
+ prop.addAttribute(new Attribute("field", m_field));
+ }
+ if (m_type != null) {
+ prop.addAttribute(new Attribute("type", m_type));
+ }
+
+ if (m_value != null) {
+ prop.addAttribute(new Attribute("value", m_value));
+ }
+ if (m_mandatory != null) {
+ prop.addAttribute(new Attribute("mandatory", m_mandatory));
+ }
+
+ }
+}
\ No newline at end of file
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 e94c603..edcd7ef 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
@@ -10,25 +10,25 @@
import org.apache.felix.ipojo.test.scenarios.annotations.service.FooService;
public class ServiceProdiving extends OSGiTestCase {
-
+
private IPOJOHelper helper;
-
+
public void setUp() {
helper = new IPOJOHelper(this);
}
-
+
public void testProvidesSimple() {
Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesSimple");
Element[] provs = meta.getElements("provides");
assertNotNull("Provides exists ", provs);
}
-
+
public void testProvidesDouble() {
Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesDouble");
Element[] provs = meta.getElements("provides");
assertNotNull("Provides exists ", provs);
}
-
+
public void testProvidesTriple() {
Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesTriple");
Element[] provs = meta.getElements("provides");
@@ -38,7 +38,7 @@
List list = ParseUtils.parseArraysAsList(itfs);
assertTrue("Provides CS ", list.contains(CheckService.class.getName()));
}
-
+
public void testProvidesQuatro() {
Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesQuatro");
Element[] provs = meta.getElements("provides");
@@ -49,7 +49,7 @@
assertTrue("Provides CS ", list.contains(CheckService.class.getName()));
assertTrue("Provides Foo ", list.contains(FooService.class.getName()));
}
-
+
public void testProperties() {
Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesProperties");
Element[] provs = meta.getElements("provides");
@@ -73,15 +73,60 @@
Element baa = getPropertyByName(props, "baa");
assertEquals("Check baa field", "m_baa", baa.getAttribute("field"));
assertEquals("Check baa name", "baa", baa.getAttribute("name"));
-
+
//Bar
Element baz = getPropertyByName(props, "baz");
assertEquals("Check baz field", "m_baz", baz.getAttribute("field"));
- assertEquals("Check baz name", "baz", baz.getAttribute("name"));
-
-
+ assertEquals("Check baz name", "baz", baz.getAttribute("name"));
}
-
+
+ public void testStaticProperties() {
+ Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesStaticProperties");
+ Element[] provs = meta.getElements("provides");
+ assertNotNull("Provides exists ", provs);
+ Element prov = provs[0];
+ Element[] props = prov.getElements("property");
+ assertEquals("Number of properties", props.length, 9);
+ //Prop1
+ Element foo = getPropertyByName(props, "prop1");
+ assertNull(foo.getAttribute("field"));
+ assertEquals("prop1", foo.getAttribute("value"));
+
+ //Prop2
+ Element prop2 = getPropertyByName(props, "prop2");
+ assertNull(prop2.getAttribute("field"));
+ assertNull(prop2.getAttribute("value"));
+
+ // Props
+ Element prop = getPropertyByName(props, "props");
+ assertNull(prop.getAttribute("field"));
+ assertEquals("{prop1, prop2}", prop.getAttribute("value"));
+
+ // Mandatory
+ Element mandatory = getPropertyByName(props, "mandatory1");
+ assertNull(mandatory.getAttribute("field"));
+ assertNull(mandatory.getAttribute("value"));
+ assertEquals("true", mandatory.getAttribute("mandatory"));
+
+ //Bar
+ Element bar = getPropertyByName(props, "bar");
+ assertEquals("Check bar field", "bar", bar.getAttribute("field"));
+ assertEquals("Check bar value", "4", bar.getAttribute("value"));
+ assertEquals("Check mandatory value", "true", bar.getAttribute("mandatory"));
+ //Boo
+ Element boo = getPropertyByName(props, "boo");
+ assertEquals("Check boo field", "boo", boo.getAttribute("field"));
+ //Baa
+ Element baa = getPropertyByName(props, "baa");
+ assertEquals("Check baa field", "m_baa", baa.getAttribute("field"));
+ assertEquals("Check baa name", "baa", baa.getAttribute("name"));
+
+ //Bar
+ Element baz = getPropertyByName(props, "baz");
+ assertEquals("Check baz field", "m_baz", baz.getAttribute("field"));
+ assertEquals("Check baz name", "baz", baz.getAttribute("name"));
+ }
+
public void testServiceController() {
Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.PSServiceController");
Element[] provs = meta.getElements("provides");
@@ -91,7 +136,7 @@
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");
@@ -117,7 +162,7 @@
fail("Property " + name + " not found");
return null;
}
-
-
+
+
}
diff --git a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/ProvidesStaticProperties.java b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/ProvidesStaticProperties.java
new file mode 100644
index 0000000..c9e02f2
--- /dev/null
+++ b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/ProvidesStaticProperties.java
@@ -0,0 +1,74 @@
+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.ServiceProperty;
+import org.apache.felix.ipojo.annotations.StaticServiceProperty;
+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},
+ properties= {
+ @StaticServiceProperty(name="prop1", value="prop1", type="java.lang.String"),
+ @StaticServiceProperty(name="prop2", type="java.lang.String"),
+ @StaticServiceProperty(name="props", value="{prop1, prop2}", type="string[]"),
+ @StaticServiceProperty(name="mandatory1", mandatory=true, type="string")
+ })
+public class ProvidesStaticProperties implements FooService, BarService {
+
+ @ServiceProperty(name = "foo")
+ public int m_foo = 0;
+
+ @ServiceProperty(value = "4", mandatory=true)
+ public int bar;
+
+ @ServiceProperty(name="baz")
+ int m_baz;
+
+ @ServiceProperty
+ public int boo;
+
+ @ServiceProperty(name="baa", value="5")
+ public int m_baa;
+
+ 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;
+ }
+
+}