May 3, 2020
Estimated Post Reading Time ~

OSGi Components – Simply Simple – Part I

There is a lot of prejudice and misunderstanding when it comes to OSGi. As with every technology there is a learning curve and some things might be different than you are used to. But change isn’t necessarily a bad thing.

Component-oriented development is a very common and powerful software engineering style where you assemble your application out of components. A component might offer a service, e.g. a scheduler service. Other components can use this scheduler service.

Or a little bit more formal: a component is a piece of software managed by a (component) container. In Java this means this is an instance, created and managed by a container. Therefore it is not the task of the developer to create or configure these instances. This is the task of the container. If the component uses other services, it is also the task of the container to pass these services to the component. Therefore a service is a component which provides one or more services. In Java this means a service implements one or more interfaces where an interface represents the service description or contract.


OSGi Components with Declarative Services
I will show that developing components with OSGi is really simple and straightforward with no additional cruft – and I think it can’t really get much easier than this. We’ll be using the Declarative Services specification(see OSGi Compendium Chapter 112) in it’s latest version (R6). With the use of the annotations also defined in this specification, components are usually POJOs with all its benefits.

I will not go into the usual tooling discussion – there are different tools out there for your favorite development environment for processing these annotations, like Ant, Maven, Gradle. In contrast to other solutions, all the annotations are build time annotations. They are processed by some tooling and create additional files which are put in the final jar (bundle). I don’t explain everything in every detail and leave out things here and there – it is advisable to read the mentioned specs in addition!

Please note, that using Declarative Services (DS) is one way of developing components and services for OSGi, there are other approaches like Blueprint, Apache Felix iPojo, Apache Felix Dependency Manager etc. Each one of these solutions (including DS) has pros in some areas while it has cons in others. I think, DS is a simple and straightforward way of developing components and its a standard maintained by the OSGi alliance. It’s easy to learn, easy to manage and maintain and provides all required features. However, if you plan to use something different than DS, the real good news is: you can mix and match all these approaches – they’re all based on the OSGi service registry and work well together!

With Declarative Services we have a nice component container managing our components and providing them with configurations and required services. As we’re talking about OSGi here, we’re talking about bundles, so the classes for your component or service go into a bundle. Declarative Services (DS) needs to know which classes are components, what services they might provide etc. Therefore your bundle should contain an XML file for DS which exactly defines this. You might know such XML configuration files from other frameworks – however, the DS format is rather simple and – the good part – you never have to write this by hand and never even have to look at it (ok, if things go wrong, it might sometimes help to check it). At runtime the SCR implementation will read these descriptor files and manage the components and services. Oh, I forgot to explain SCR It stands for Service Component Runtime and can be seen as the container defined by DS. Now usually people use SCR and DS as synonyms though that is not quite correct for the purists amongst us.

So let’s start…

Components
If you want to write a component which is managed by the container, just add the @Component annotation to your class:

package com.mycompany.cool.project.impl;
import org.osgi.service.component.annotations.Component;
@Component
public class MyFirstComponent { }


Build your project, deploy your bundle into an OSGi framework which has SCR running (I recommend the implementation from Apache Felix, version 2.0 or higher) – and your component will be instantiated by the container on bundle start. As a component has never public API but is a private implementation, the class should go into a private package which is not exported by the bundle! It is good style to add impl or similar somewhere in the package name, like com.mycompany.cool.project.impl.

Now, while this component runs and works – it’s not really useful as the component does nothing.

Lifecycle
As noted above, the container manages the lifecycle of the component, its instantiation and removal. In order to make this work, the component class needs the default zero argument constructor. However, if the component wants to do something during the creation or the deletion phase, it can implement an activate and/or deactivate method and mark it with the according annotation:

package com.mycompany.cool.project.impl;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Deactivate;

@Component
public class MyFirstComponent {

@Activate
protected void activate() {
// do something
}

@Deactivate
protected void deactivate() {
// do something
}
}


It is good practice to name these methods activate and deactivate – however you could pick any other name if you want. The methods have to be protected with no return value.

Still, we have a component not doing that much, so lets do something “more” usefull:

package com.mycompany.cool.project.impl;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;

@Component
public class MyFirstComponent {

private volatile boolean doRun;

@Activate
protected void activate() {
final Thread t = new Thread() {
public void run() {
doIt();
}
};
t.setDaemon(true);
doRun = true;
t.start();
}

private void doIt() {
while (doRun) {
System.out.println("I'm still alive!");
try {
Thread.sleep(60 * 1000);
} catch (InterruptedException e) {}
}
}

@Deactivate
protected void deactivate() {
doRun = false;
}
}


We create a new thread inactivate and start it – in the thread, we print out a message every minute. In addition we use a boolean variable to stop the thread when the component gets deactivated. While this example is not a production ready implementation, it shows that the thread of the activate and deactivate method “belong” to the component container – so the component should return as quickly as possible and do not block this method. Therefore we created an own thread. Please note, that this is just an example to demonstrate something, for things like timed execution, you would rather use a scheduler invoking your component periodically instead of running an own thread inside your component. The Apache Sling scheduler is a great way of doing things like these.

Using Services
A single component alone does usually not make up an application – it is rather assembled of dozens if not hundreds of components interacting. And interaction happens by using services.
So let’s extend our example – we use the event admin (another OSGi compendium spec with a great implementation from the Apache Felix project). The event admin is a service which allows us to send out events in a publish/subscrib kind of way. So each event gets a topic, and subscribers subscribe with the topics they are interested in.

A component can use other services by using the @Reference annotation. The container uses dependency injection and injects references into fields:

package com.mycompany.cool.project.impl;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;

@Component
public class MyFirstComponent {

@Reference
private EventAdmin eventAdmin;

private volatile boolean doRun;

@Activate
protected void activate() {
final Thread t = new Thread() {
public void run() {
doIt();
}
};
t.setDaemon(true);
doRun = true;
t.start();
}

private void doIt() {
while (doRun) {
final Event event = new Event("alive", null);
this.eventAdmin.sendEvent(event);
try {
Thread.sleep(60 * 1000);
} catch (InterruptedException e) {}
}
}

@Deactivate
protected void deactivate() {
doRun = false;
}
}


The above example looks pretty simple and from a developer’s point of view it is simple. You just declare an instance variable with the type of the service you want (services are registered by the interface(s) they implement) and add the @Reference annotation. Before the component is activated (the activate method is called), this instance field is set with the event admin service from the container. Therefore it is safe to assume that the event admin service is available and setup.

As OSGi is highly dynamic, bundles and services can come and go – so the event admin might be there or not, disappear, reappear etc. You don’t really have to care about this (at least not for the moment). Your component is only active if the event admin is available – if not, your component will either not be activated or will get deactivated. We’ll go deeper into references later on and learn how to cope with all the dynamics of services.

With just these four annotations you already know the building blocks for developing OSGi components and services. I suggest you test out the examples and make yourself familiar with using your favorite development environment. Once you are able to successfully build and deploy these components, you’re ready for the cool fun stuff of DS which I will show in part two.

Stay tuned.


Source: https://blog.osoco.de/2015/08/osgi-components-simply-simple-part-i/


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.