This articles continues a series on declarative services in OSGi. We started with a basic OSGi bundle, then discussed architecting a multi-bundle application. Then we looked closer at declarative services and how to register them. All of this is fully demonstrated in an example application on GitHub.
With the last article, we left off with a service interface, and implementation, and a few ways to tell the service registry about it. However, we didn’t cover the mechanics of registration for our last method (SCR using Java annotations) and we didn’t cover service lookup. In this article, I’ll be tackling the first of those two topics.
SCR Annotations
Our service implementation looks like this:
@Component(immediate = true)
@Service
@Property(name="language", value="fr")
public class FrenchGreeter implements Greeter {
private static final Logger LOGGER = LoggerFactory.getLogger(FrenchGreeter.class);
@Override
public String greet() {
LOGGER.info("Le 'greeter' en francais!");
return "Bonjour tout le monde!";
}
}
Those @Component
, @Service
, and @Property
annotations by themselves
don’t do anything; there needs to be some code that processes them. In this
case, we need code that scans the classpath, finds annotated classes,
instantiates them, and registers them in the service registry.
Since I’m using Karaf and therefore Apache Felix, I’m using the Felix version
of these annotations. So I’m going to demonstrate the Felix way of using them.
This involves processing the annotations at build time and generating an XML
file that gets packed into the JAR. This XML file is then specified in the
META-INF/MANIFEST.MF
file so the OSGi container knows to process it. (It’s
worth noting that there is a vendor-neutral set of annotations) that can
also be used; the process is very similar. Under the covers, the bnd tool
from Peter Kreeft is ultimately doing a large part of the work.)
To process the annotations at build time, we use Felix’s maven-scr-plugin
.
This plugin provides a goal to generate the XML descriptor files from the
annotations.
The plugin declaration is shown below. For those looking at the source code in GitHub, note that this is specified in the parent POM; it doesn’t hurt anything to apply it to modules where the annotations aren’t used, and specifying it in the parent reduces duplication.
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<version>${maven.scr.version}</version>
<executions>
<execution>
<id>generate-scr-scrdescriptor</id>
<goals>
<goal>scr</goal>
</goals>
<configuration>
<outputDirectory>target/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
With this particular bit of magic applied, when our JAR is generated we get two
special things. First, we get this line in META-INF/MANIFEST.MF
:
Service-Component: OSGI-INF/org.anvard.karaf.greeter.french.FrenchGreeter.xml
Second, we get the actual XML file:
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
<scr:component immediate="true" name="org.anvard.karaf.greeter.french.FrenchGreeter">
<implementation class="org.anvard.karaf.greeter.french.FrenchGreeter"/>
<service servicefactory="false">
<provide interface="org.anvard.karaf.greeter.api.Greeter"/>
</service>
<property name="language" value="fr"/>
<property name="service.pid" value="org.anvard.karaf.greeter.french.FrenchGreeter"/>
</scr:component>
</components>
There’s nothing special about this file. We could have written it ourselves and
used the maven-bundle-plugin
to add the Service-Component
information to the
manifest. But this is obviously much more verbose than Java annotations. It’s
also worth mentioning that the reason I showed Blueprint XML in the previous article
is because Blueprint XML does the same thing less verbosely, and is capable of
doing many other useful things, as I hope to show in a future article.
Wrapping Up
We’ve almost described every piece of our OSGi declarative services example. The one thing that’s missing is to perform the service lookup. I want to save that for another article for two reasons. First, I want to discuss finding multiple service instances and choosing between them based on properties. Second, I want to show both programmatic lookup and service lookup using Blueprint XML. Across both of these, we need to consider the fact that in OSGi, bundles can come and go dynamically, and we need to deal with the fact that the available services could change at any time.