DS Annotations – Component, property and configurations


@Component Annotation

An component is a piece of code that is managed by OSGi container. The container would be responsible for its instantiation and management.

Attributes of a component: 

component-attributes.PNG

The above table have been noted from: https://www.knopflerfish.org/releases/5.2.0/docs/javadoc/org/osgi/service/component/annotations/Component.html

  • configurationPolicy: The attribute can hold following values of ConfigurationPolicy
    • IGNORE: Always allow the component configuration to be satisfied and do not use the corresponding Configuration object even if it is present.
    • OPTIONAL: Use the corresponding Configuration object if present but allow the component to be satisfied even if the corresponding Configuration object is not present.
    • REQUIRE: There must be a corresponding Configuration object for the component configuration to become satisfied. A component is activated only after all its service dependencies are satisfied by the container.
  • factory: used to create a configuration factory. More implementation details are available on link.
  • name: The attribute doesn’t support special characters. If invalid, the component will not be registered.
  • property: A replacement for “@Properties felix annotation used at Class-level”.
  • service: registers component as a Service. More implementation details available on link.

Example:

To create a component, add @Component annotation to a class. Also, configure its attributes as per your need.

In the following example, we have:

  • @Activate annotation: It is used to mark a function which would be called when the component activates. The function can have any name.
  • @Deactivate annotation: It is used to mark a function which would be called when the component deactivates. The function can have any name.
  • Declared a custom property using property attribute.


package blog.techrevel.component.impl;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;
@Component(name = "Sample Component", property = { "testProperty=Hello" })
public class SampleComponentImpl {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleComponentImpl.class);
@Activate
protected void activate(ComponentContext componentContext) {
LOGGER.info("Registering: " + componentContext.getProperties().get("testProperty").toString());
}
@Deactivate
protected void deactivate(ComponentContext componentContext) {
LOGGER.info("Deregistering: " + componentContext.getProperties().get("testProperty").toString());
}
}

More on defining property:

The property annotations have moved to their own class which declutters the component or service. For components with a large amount of properties, create an independent class, while a component with only one or two properties may be fine as a subclass.

A property value is read from componentContext in @Activate/@Deactivate methods.

If you need multiple properties for a component, declare them as an indiviadual value  array of values for property attribute:


package blog.techrevel.component.impl;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;
@Component(property = { "testProperty1=Hello", "testProperty2=World" })
public class MultipleComponentPropertiesImpl {
private static final Logger LOGGER = LoggerFactory.getLogger(MultipleComponentPropertiesImpl.class);
@Activate
protected void activate(ComponentContext componentContext) {
LOGGER.info("Registering: " + componentContext.getProperties().get("testProperty1").toString());
LOGGER.info("Registering: " + componentContext.getProperties().get("testProperty2").toString());
}
@Deactivate
protected void deactivate(ComponentContext componentContext) {
LOGGER.info("Deregistering: " + componentContext.getProperties().get("testProperty1").toString());
LOGGER.info("DeRegistering: " + componentContext.getProperties().get("testProperty2").toString());
}
}

To define multiple values of a property, create each value as a separate element of the property Array.


package blog.techrevel.component.impl;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;
@Component(property = { "testProperty1=hello", "testProperty2=A", "testProperty2=B", "testProperty2=C" })
public class ComponentMultivaluedPropertyImpl {
private static final Logger LOGGER = LoggerFactory.getLogger(ComponentMultivaluedPropertyImpl.class);
@Activate
protected void activate(ComponentContext componentContext) {
LOGGER.info("Registering: " + componentContext.getProperties().get("testProperty1").toString());
Object[] arr = convertToObjectArray(componentContext.getProperties().get("testProperty2"));
for (Object obj : arr) {
LOGGER.info("Registering testProperty2: " + obj);
}
}
@Deactivate
protected void deactivate(ComponentContext componentContext) {
LOGGER.info("Deregistering: " + componentContext.getProperties().get("testProperty1").toString());
Object[] arr = convertToObjectArray(componentContext.getProperties().get("testProperty2"));
for (Object obj : arr) {
LOGGER.info("DeRegistering testProperty2: " + obj);
}
}
static Object[] convertToObjectArray(Object array) {
Class ofArray = array.getClass().getComponentType();
if (ofArray.isPrimitive()) {
List ar = new ArrayList();
int length = Array.getLength(array);
for (int i = 0; i < length; i++) {
ar.add(Array.get(array, i));
}
return ar.toArray();
} else {
return (Object[]) array;
}
}
}

Creating configurable properties

The annotations described in this section will help you to create Components whose configurations can be edited via OSGi console. To achieve the same:

  1. Create a separate or an inner interface which would hold configurations. In example, we have created Config interface.
  2. Add @ObjectClassDefinition annotation to the interface. Also, add desired attributes
    • name: The name would help you search the configuration in OSGi’s configuration manager.
  3. Add @Designate to the Component that would consume the configurations.  The ocd attribute should refer to the Configuration interface created in Step-2.
  4. Declare properties that you would like to configure via @AttributeDefinition
    • To define property names similar to blog.name , your method would be named blog_name
    • Following image maps annotation attributes with the OSGi UI.osgi.PNG
    • Please note that there are 2 ways to define default values:
      • defaultValue attribute of @AttributeDefinition: The value is displayed to the user, when he/she tries to configure the interface via Configuration manager. OSGi will NOT pick this default value, if no Configuration exists. Thus, when you install a bundle, the output would appear as:default-values.PNG
      • Specifying default value in variable declarartion: The value is displayed to the user, when he/she tries to configure the interface via Configuration manager. OSGi will pick this default value, even if no Configuration exists.


package blog.techrevel.component.impl;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
@Designate(ocd = ConfigurableComponentPorpertiesImpl.Config.class)
public class ConfigurableComponentPorpertiesImpl {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurableComponentPorpertiesImpl.class);
@ObjectClassDefinition(name = "Techrevel Sample Configuration", description = "This is sample configuration")
public @interface Config {
@AttributeDefinition(name = "Blog name", defaultValue = "techrevel", description = "Name of the blog")
String blog_name();
@AttributeDefinition(name = "Blog Url")
String blog_URL() default "https://techrevel.blog&quot;;
// Multi-values property
@AttributeDefinition(name = "Blog Topics")
String[] blog_Topics() default { "OSGi", "AEM" };
@AttributeDefinition(name = "Blog Count", description = "Total number of blogs", type = AttributeType.INTEGER)
int blogCount() default 0;
// Password
@AttributeDefinition(name = "password", type = AttributeType.PASSWORD)
String password();
// Checkbox
@AttributeDefinition(name = "Blog is active?")
boolean blogIsActive() default true;
// Dropdown
@AttributeDefinition(name = "Blog is hosted at?", options = { @Option(label = "WordPress", value = "wordpress"),
@Option(label = "Blogspot", value = "blogspot") })
String hostedAt() default "";
}
@Activate
protected void activate(final Config config) {
LOGGER.info("Blog name: " + config.blog_name());
LOGGER.info("Blog URL: " + config.blog_URL());
for (String topic : config.blog_Topics()) {
LOGGER.info("Blog Topics: " + topic);
}
LOGGER.info("Blog Count: " + config.blogCount());
LOGGER.info("Blog Password: " + config.password());
LOGGER.info("Blog Is Active? " + config.blogIsActive());
LOGGER.info("Blog is hosted at? " + config.hostedAt());
}
}

Also, note that we no longer need PropertiesUtil to resolve OSGi configurations. 🙂

Notes:

Via Declarative Services, the number of annotations have been reduced. For example: @Component annotation is used for:

  • Component
  • Service
  • Servlet
  • Filter etc..

All of the above can be created by utilizing attributes of @Component details. More details are available on specific links

One thought on “DS Annotations – Component, property and configurations

Leave a comment