Skip to main content

OSGI

 OSGI Annotation


Mapping to OSGi Declarative Services Annotations

The below table gives you an overview of how to map the annotations.

Annotation Mapping

APACHE FELIX SCR ANNOTATIONDESCRIPTIONOSGI DECLARATIVE SERVICES ANNOTATIONDESCRIPTION
@ComponentThe @Component annotation marks a Java class to be used as a component.@ComponentThis is more or less a strict one-to-one replacement. Only difference is the default behavior for services. See below.
@ServiceMarks the component as a service and optionally lists the provided services (classes)@ComponentThe OSGi annotation has a service attribute which should be used to list the provided services. Be careful, if your component should not provide any service, set this attribute to an empty array.
@ReferenceReference to services, can be used on unary fields or on class level with bind/unbind methods.@ReferenceField references can directly be migrated, for event based references (methods), the @Reference annotation must be put on the bind method. In addition, more options for field references exist.
@Activate, @Deactivate, and @ModifiedMark a method being the activation, deactivation or modified method@Activate, @Deactivate, and @ModifiedStraight one-to-one migration.
@PropertyConfiguration properties and metatype.Component Property Type and metatype annotationsInstead of using a set of @Property annotations, the configuration is described through an annotation (component property type) and OSGi Metatype annotations can be used to add the metatype info.

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 MyComponent { }

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 MyComponent {
  
    @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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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 MyComponent {
  
    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 in activate 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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 MyComponent {
  
    @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.


Component Configurations

If you want to make your component configurable, the best way of doing this is to leverage the OSGi Configuration Admin (another spec from the OSGi compendium with again a great implementation at Apache Felix). Configuration Admin is a service persisting configurations – these configurations are dictionaries where the key is a string and the value can be one of the simple Java types or an array or collection of such a type. This is usually sufficient for most components.

Configuration Admin nicely abstracts managing the configuration from your component. As a component developer you don’t need to know where the actual configuration is stored and how it ends up there. Without a component container, you would ask the configuration admin for “your” configuration (though there is some injection support through the ManagedService interface) – but fortunately with DS the container takes care of this and provides your component with the configuration. The component might get the configuration as a parameter for the activate method.

As mentioned Configuration Admin stores configurations as dictionaries. Whereas the names are of type String, the value can be any type. While your component might expect an integer, e.g. for a port, the actual value stored might be of type String holding an integer value. Therefore it is good style to not assume a specific type but try to convert whatever you get into the expected type. To avoid putting the burden of doing this on the component developer and to support type safe configurations, the configuration for a component can be described in Java. While the following might look a little bit out of the ordinary, stick with me, you’ll get used to it pretty soon and simply love it.

The way to describe a configuration is by defining a so called Component Property Type: an annotation describing all configuration properties. Let’s assume our component has three configuration properties, then the following annotation would do:

1
2
3
4
5
6
7
8
public @interface MyComponentConfig {
     
    String welcome_message() default "Hello World!";
 
    int welcome_count() default 3;
 
    boolean output_goodbye() default true;
}

Defining an annotation instead of a simple interface has the advantage that we can directly specify default values for each property. If no value for a property is stored in configuration admin, the default value is used. Let’s see how to use this:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
 
@Component
public class MyComponent {
 
    @Activate
    protected void activate( MyComponentConfig config ) {
        for(int i = 0; i < config.welcome_count(); i++ ) {
            System.out.println(config.welcome_message());
        }
    }
}

When the above component is activated, SCR tries to get a configuration from the configuration admin and converts it into the used annotation type. Therefore the passed in object returns the value in the correct type for each property. As mentioned if there is no value, the default is returned. This makes handling of configurations very simple and avoids all the usual boilerplate code. The OSGi specification for Declarative Services explains in detail how the type conversion is done and what happens if a value can’t be converted. It also defines the conversion rule from the name of a property in the annotation to a property in configuration admin. In the example above we see the underscore in the names, this is acutally converted to a dot in the property name.

Taking configurations is as easy as this: define your set of configuration properties as an annotation and pass it as an argument to the activate method. Of course, if you don’t need configuration, leave out these steps and use the zero argument activate method signature – or if you don’t need the activate method at all, leave it out completely.

With this knowledge we can already develop configurable components capable of using other services. But how do you provide a service for others to be used?

Services

Offering a service usually consists of two steps. First you define the service API and second you implement this API. Of course, in some cases the API might already exist as someone else defined the interface. If you define your own interface, put it into a public package and export this package – however, as already noted above for the component, a service implementation should never be public but in a private package. It depends on your use case, if you put the interface and the implementation in the same bundle or create an API bundle and an implementation bundle. If you’re implementing an existing interface, this usually is already in another bundle and you can just import it. And if you create public API, don’t forget to use proper versioning for these packages. Have a look at the semantic versioning whitepaper!

Offering a service is implementing the corresponding interface and registering the component in the OSGi service registry as the service. With the annotations you can just use the @Component annotation. Assume there is an interface EventHandler and your component should be registered as a service for this interface:

1
2
3
4
5
6
7
8
import org.osgi.service.component.annotations.Component;
 
@Component
public class MyService implements EventHandler {
 
   ....// implement the EventHandler interface
 
}

Without further specifying anything at the @Component annotation, a component is registered as a  service for all interfaces it directly implements. In this case this is the EventHandler. While this is very convenient, it comes with the problem, that as soon as your component is implementing an interface directly, it gets registered as this service. In some cases this is not what you want. Therefore I suggest to:

  1. Always explicitely list the service interfaces the component implements:
    @Component(service = EventHandler.class}
  2. As a default always set the service attribute of the component annotations to the empty array. This prevents the automatic registration:
    @Component(service = {})

If you follow these simple rules, you can see directly by looking at the @Component annotation which services this component implements and you avoid accidental service registrations, e.g. when refactoring your implementation.

Lifecycle of a Service

A component which does not provide a service is active for as long as it is satisfied (all referenced services are available and some other conditions we get to later). In contrast, services are instantiated lazy or on demand by DS. This means, as long as no one is using your service, your service is never created nor activated! In most cases this is fine. But there is a catch with this approach one should be aware of: if someone else is starting to use your service, it gets created and activated. As soon as your service is not used anymore, it gets deactivated and destroyed.

The OSGi spec does not mandate this behavior. The implementation of Declarative Service is free to keep your service around for some time until it is disposed. By default the current Apache Felix implementation immediately disposes such components. However it is possible to configure such a detailed disposal of components. But this leaves you with the problem of configuring this correctly which might not be that easy.

On the other hand, frequent creation and disposal of an “expensive” service might create a performance bottleneck. For example if this happens being triggered by an event, a request and/or if your service is doing some computation in the activate method.

In many cases, your actual service is combining a “component” and a “service” part. While the “component” part is the expensive one and should only be done once, the “service” part is lightweight and might simply use the component part. In such cases you might think about splitting your implementation accordingly.

Or you can either think about holding the service by someone else and therefore keep a reference to it around (which in general sounds hacky though there are valid use cases). Or you can use the immediate flag on the @Component:

@Component(service=EventHandler.class, immediate=true)

With immediate set to true, the component is activated as soon as possible and kept as long as possible. Obviously, this increases things like startup time, memory consumption etc. So it should really be used with care and maybe only after problems are encountered in this area.

In the past the only situation where we encountered this was implementing an event handler. But with today’s event admin implementations this isn’t even true anymore either. But for completeness lets have a look at that problem: an implementation of the event admin as defined in the OSGi Event Admin Specification might fetch an event handler (service org.osgi.service.event.EventHandler) each time an event should be delivered to this handler. Clearly, this has the advantage that event handlers are only used if there is an event for such a handler. While this might work with a few events, with very frequent events, especially in a multi threaded environment, the same event handler might receive quiet a lot events, even “in parallel”.

In the past we suggest to make your event handler implementation immediate as otherwise it is potentially created/destroyed with each event for this handler! However, even this is up to the implementation of the event admin. The latest Apache Felix event admin implementation and the Equinox event admin implementation do not create/destroy the instance on each request, they rather create it once the first event for this component arrives and keep a reference to it from that point on. But as this is implementation specific, the above advice might be handy. And again, only use immediate if really required.

And with this the simple introduction to Declarative Services ends: you now know:

  • how to create components
  • how to configure components
  • how to reference services
  • how to provide services






OSGi Components


The topics included:

  • Creating components
  • Component lifecycle
  • Component configuration
  • Using services through references
  • Providing services

In this part we’ll cover another interesting topic:

Metatype Generation

The metatype specification (OSGi Compendium Chapter 105) provides a way to describe the configuration for a component. In general an OSGi configuration is just a dictionary with arbitrary key-value pairs. By defining a metatype description for the configuration, the developer of a component can define which properties together with their type the component expects. Such a metatype information can be used at runtime to generate forms to edit/create the configuration of a component. For example the Apache Felix Web Console does exactly that.

Now if you look at Part II of this series, if a component is configurable it contains already a component annotation for its properties:

1
2
3
4
5
6
7
8
public @interface MyComponentConfig {
      
    String welcome_message() default "Hello World!";
  
    int welcome_count() default 3;
  
    boolean output_goodbye() default true;
}

The above annotation already defines all possible properties together with their default values. Therefore the only thing you have to do is provide some human readable information like a label and a description for both, the configuration as a whole and each property. This can be done with some annotations from the metatype specification:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
@ObjectClassDefinition(name = "Hello World Configuration",
        description = "The configuration for the hello world component.")
public @interface MyComponentConfig {
      
    @AttributeDefinition(name="Welcome Message", description="This message is displayed on startup of the component.")
    String welcome_message() default "Hello World!";
  
    @AttributeDefinition(name="Welcome Message Count", description="This is the number of times, the welcome message will be displayed. " +
                              "If less than one, no message will be displayed.")
    int welcome_count() default 3;
  
    @AttributeDefinition(name="Output Goodbye", description="Set this if the component should output a goodbye message.")
    boolean output_goodbye() default true;
}

The @ObjectClassDefinition annotation is used on the whole configuration annotation and for each attribute, the @AttributeDefinition annotation is used. With these annotations a metatype description for the MyComponentConfig class will be generated. However, we want to bind this configuration to our component which is a different class. Therefore you have to specify the @Designate annotation on your component class:

1
2
3
4
5
6
7
8
9
10
import org.osgi.service.metatype.annotations.Designate;
 
@Component
@Designate( ocd = MyComponentConfig.class )
public class MyFirstComponent {
  
    @Activate
    protected void activate( MyComponentConfig config ) {
    ...
}

And this will generate the metatype information for your component. It’s really easy to do such and it helps the user of your component to easily figure out how your component can be configured.

Comments

Popular posts from this blog

Akamai CDN

  Leveraging Akamai CDN for Enterprise Applications: Features and Benefits Leveraging Akamai CDN for Enterprise Applications: Features and Benefits Table of contents • Introduction • Features of Akamai CDN • Benefits of Akamai CDN • Use Cases of Akamai CDN • Akamai CDN vs Traditional CDN • How to Choose the Right CDN for Your Enterprise Application? • Conclusion Introduction What is a Content Delivery Network (CDN)? It's like having a team of roadies who transport your equipment from one gig to another, making sure everything arrives safely and on time. In the digital world, a CDN does the same thing for website content, delivering it from server to server so that it reaches the end user quickly and efficiently. And when it comes to CDNs, Akamai is definitely the Mick Jagger of the industry. With its cloud-based technology, high-performance content delivery, advanced security, and real-time analytics, Akamai CDN offers an impressive package of features that's hard to beat. So...

future of artificial intelligence

A new wave of artificial intelligence breakthroughs is making it possible for technology to do all sorts of things we at first can't believe and then quickly take for granted. That's making our lives easier and more productive, and it's also raising a new round of excitement and angst about where artificial intelligence is headed. "Right now we're in a phase of very fast advances, and that may well continue for a small number of years," said Christopher Bishop, a distinguished scientist with Microsoft Research Cambridge. With advances in machine learning, including deep neural networks and probabilistic models, computers can now instantly translate spoken and written conversation, recognize and accurately caption photos, identify faces and be your personal assistant. And yet, although they can do some individual tasks as well as or even better than humans, technology still cannot approach the complex thinking that humans have. "It's a long way fro...