Migrate from Felix Annotations to OSGi Annotations
Goal
Create an OSGi component that uses the OSGi annotations.
Procedure
Let’s suppose to have our OSGi service already implemented with the Felix Annotation. We should have an interface, e.g:
package com.adobe.training.core.service.general;
public interface Lab2020Configuration {
String getEndpoint1();
String getEndpoint2();
}
And its implementation:
package com.adobe.training.core.service.general.impl;
import com.adobe.training.core.service.general.Lab2020Configuration;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.ComponentContext;
import java.util.Dictionary;
@Component(label = "Lab2020 - Endpoint Configuration", metatype = false, immediate = true)
@Service
public class Lab2020ConfigurationImpl implements Lab2020Configuration {
private static final String DEFAULT_AEM_END_POINT_1 = "/v1/users";
private static final String DEFAULT_AEM_ENDPOINT_2 = "v2/users";
@Property(value = Lab2020ConfigurationImpl.DEFAULT_AEM_END_POINT_1, label = "Default end point 1")
private static final String END_POINT_1 = "lab2020.endpoint.1";
@Property(label = "Default end point 2")
private static final String END_POINT_2 = "lab2020.endpoint.2";
private String endpoint1;
private String endpoint2;
@Activate
protected void activate(final ComponentContext ctx) {
final Dictionary<?, ?> config = ctx.getProperties();
endpoint2 = PropertiesUtil.toString(config.get(END_POINT_1), DEFAULT_AEM_END_POINT_1);
endpoint1 = PropertiesUtil.toString(config.get(END_POINT_2), DEFAULT_AEM_ENDPOINT_2);
}
@Override
public String getEndpoint1() {
return endpoint1;
}
@Override
public String getEndpoint2() {
return endpoint2;
}
}
We should also have the configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
lab2020.endpoint.1="/v1/users"
lab2020.endpoint.2="/v2/users"/>
Now, if we want to create the same OSGi service with the new annotations, first we need to update our pom.xml. If you have created your project with the latest archetypes, you already have all the necessary dependencies, otherwise, if for instance you are migrating an old project, you have to add this dependency:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.cmpn</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
We can now start to rewrite our implementation. The interface can remain the same, but its implementation will be different. Here the new code:
package com.adobe.training.core.service.general.impl;
import com.adobe.training.core.service.general.Lab2020Configuration;
import com.adobe.training.core.service.general.config.Lab2020ConfigurationDesignate;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.Designate;
@Component(
immediate = true,
service = Lab2020Configuration.class
)
@Designate(ocd = Lab2020ConfigurationDesignate.class)
public class Lab2020ConfigurationImpl implements Lab2020Configuration {
private String endpoint1;
private String endpoint2;
@Activate
@Modified
protected void activate(Lab2020ConfigurationDesignate configurationDesignate) {
endpoint1 = configurationDesignate.getEndpoint1();
endpoint2 = configurationDesignate.getEndpoint2();
}
@Override
public String getEndpoint1() {
return endpoint1;
}
@Override
public String getEndpoint2() {
return endpoint2;
}
}
All the properties are moved in a new section, that I prefer to have in a separate file. Here the configuration Interface:
package com.adobe.training.core.service.general.config;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@ObjectClassDefinition(name = "Lab2020 - Configuration")
public @interface Lab2020ConfigurationDesignate {
@AttributeDefinition(name = "Endpoint 1")
String getEndpoint1();
@AttributeDefinition(name = "Endpoint 2")
String getEndpoint2();
//JUST A FEW ADDITIONAL EXAMPLES
@AttributeDefinition(name = "List of properties", description = "add a description")
String[] getListOfProperties();
@AttributeDefinition(name = "integer prop", type = AttributeType.INTEGER)
int integerProp();
@AttributeDefinition(name = "booleanProp", type = AttributeType.BOOLEAN)
boolean booleanProp() default false;
}
The annotations @ObjectClassDefinition and @AttributeDefinition are used to define the configuration, while the annotation @Designate is used in the implementation to create a link to your configuration.
I’ve added also a few other example to show you how to have Arrays, Integer o Boolean properties.
Regarding the configuration file, I’ll show here another way in addition to the XML file.
Config file “com.adobe.training.core.service.general.impl.Lab2020ConfigurationImpl.config”:
# Configuration created by Apache Sling JCR Installer
getEndpoint1="/v1/users"
getEndpoint2="/v2/users"
booleanProp="false"
getListOfProperties=["test2","test5"]
integerProp=I"30000"
That’s it! Just one last thing I want to point out. The first I used them a few years ago, I was migrating a project and I had in the POM the reference of the felix annotations. This created a conflict and I had to removed it. I’m not sure if both dependencies can coexist, so keep in mind this consideration 🙂
Here you can find different articles with some OSGi examples.
Cheers!! 🍷