SeamFramework.orgCommunity Documentation

Chapter 3. Weld-OSGi addressing OSGi service layer complexity

3.1. Publishing CDI beans as OSGi services
3.2. Consuming Weld-OSGi auto published services
3.3. Select the service instance
3.4. Injecting OSGi service in bean bundle

You can now use CDI in an OSGi environment with Weld-OSGi and bean bundles. But Weld-OSGi also provide numerous solution helping you make your multi bundles OSGi application using CDI way.

In this chapter you will see:

CDI beans can be seen like services, with an interface defining the service contract and one or many implementations performing the service. So Weld-OSGi allows to easily publish your CDI beans from bean bundles as OSGi services. To do so you just to put an annotation on your bean implementation classes, avoiding the whole OSGi publishing process.

Modify the hello-world-multilingual bean bundle to auto publish the HelloWorld services as OSGi services. It will be the hello-world-provider bean bundle.

Update the com.sample.impl.HelloWorldImpl*.java implementation classes

...
@Language("*")
@Publish (1)
public class HelloWorld* implements HelloWorld {
    ...
}

Simply put the Publish annotation on the implementation classes (1) and that is it ! Every time Weld-OSGi finds a CDI bean with the Publish annotation, it registers it as a new OSGi service.

Your project should now looks like that:

hello-world-provider
      pom.xml
    - src
        - main
            - java
                - com.sample
                  App.java
                    - api
                      HelloWorld.java
                      Language.java
                      Presentation.java
                    - impl
                      HelloWorldEnglish.java
                      HelloWorldFrench.java
                      HelloWorldGerman.java
                      PresentationInterceptor.java
            - resources
                - META-INF
                  beans.xml
                  hello-world-provider.bnd 

Try your new hello-world-provider bean bundle in the OSGi environment:

In the next section you will see these new OSGi service in action by consuming them in a second bundle. But before that you will what options Weld-OSGi give when you auto publish OSGi service.

The Publish annotation allows to things:

Create a new regular OSGi bundle that will consume the auto published services of the hello-world-provider bean bundle. It will be the hello-world-consumer bundle.

You need to write the entry point of your bundle (i.e the activator class of the bundle). That is the com.sample.Activator.java main class

package com.sample;

import com.sample.api.HelloWorld; (1)
import org.osgi.framework.BundleActivator; (2)
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class Activator implements BundleActivator {

    HelloWorld helloWorld;

    @Override
    public void start(BundleContext context) throws Exception { (3)
        ServiceReference helloWorldReference = context.getServiceReference(HelloWorld.class.getName()); (4)
        helloWorld = (HelloWorld)context.getService(helloWorldReference);
        helloWorld.sayHello(); (5)
    }

    @Override
    public void stop(BundleContext context) throws Exception { (6)
        helloWorld.sayGoodbye(); (7)
    }
}

You import your service interface (1) and the OSGi dependencies (2). You ask the OSGi environment for the HelloWorldservice (4). Then you greet (5) and say goodbye (7) to the World at the start (3) and stop (6) of your bundle.

You should also do a quick update of the com.sample.impl.PresentationInterceptor.java interceptor

...
public class PresentationInterceptor {

    @AroundInvoke
    public Object present(InvocationContext ctx) throws Exception {
...
                if(lang.equals("FRENCH")) {
                    System.out.println("Je suis le bundle hello-world-provider");
                    return null;
                } else if(lang.equals("GERMAN")) {
                    System.out.println("Ich bin das bundle hello-world-provider");
                    return null;
                }
            }
        }
        System.out.println("I am the bundle hello-world-provider");
        return null;
    }
}

Your bean bundle may present itself right.

Finally you write the configuration files of your bundle:

Your project should now looks like that:

hello-world-consumer
      pom.xml
    - src
        - main
            - java
                - com.sample
                  Activator.java
            - resources
                - META-INF
                  hello-world-consumer.bnd 

Try your new hello-world-consumer bean bundle in the OSGi environment:

Now you need to decide what language your consumer bundle will speak. To do so you cannot use CDI qualifier like in provider bundle because you are using OSGi mechanisms to obtain the service instance. Fortunately Weld-OSGi provides a binding between CDI service qualification and OSGi service properties.

Conversion CDI qualifiers to OSGi service properties

A CDI qualifier will generate an OSGi service property for each of its valued element (an element with a default value is always considered valued) following these rules:

Using these rules, modify your hello-world-consumer bundle in order to make it speak the three languages like the hello-world-provider bean bundle. It will be the hello-world-consumer-multilingual bundle.

All the work happens in the com.sample.Activator.java main class

package com.sample;

import com.sample.api.HelloWorld; (1)
import org.osgi.framework.BundleActivator; (2)
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class Activator implements BundleActivator {

    HelloWorld helloWorldEnglish;
    HelloWorld helloWorldFrench;
    HelloWorld helloWorldGerman;

    @Override
    public void start(BundleContext context) throws Exception { (3)
        ServiceReference helloWorldEnglishReference = context.getServiceReferences(HelloWorld.class.getName(),"(language.value=ENGLISH)")[0]; (4)
        ServiceReference helloWorldFrenchReference = context.getServiceReferences(HelloWorld.class.getName(),"(language.value=FRENCH)")[0];
        ServiceReference helloWorldGermanReference = context.getServiceReferences(HelloWorld.class.getName(),"(language.value=GERMAN)")[0];

        helloWorldEnglish = (HelloWorld)context.getService(helloWorldEnglishReference);
        helloWorldFrench = (HelloWorld)context.getService(helloWorldFrenchReference);
        helloWorldGerman = (HelloWorld)context.getService(helloWorldGermanReference);

        helloWorldEnglish.sayHello(); (5)
        helloWorldFrench.sayHello();
        helloWorldGerman.sayHello();
    }

    @Override
    public void stop(BundleContext context) throws Exception { (6)
        helloWorldEnglish.sayGoodbye(); (7)
        helloWorldFrench.sayGoodbye();
        helloWorldGerman.sayGoodbye();
    }
}

You import your service interface (1) and the OSGi dependencies (2). You ask the OSGi environment for the HelloWorldservices specifying the correct filter (4). Then you greet (5) and say goodbye (7) to the World at the start (3) and stop (6) of your bundle.

Your project should now looks like that:

hello-world-consumer-multilingual
      pom.xml
    - src
        - main
            - java
                - com.sample
                  Activator.java
            - resources
                - META-INF
                  hello-world-consumer-multilingual.bnd 

Try your new hello-world-consumer-multilingual bean bundle in the OSGi environment:

You can now use auto published service from a bean bundle in any other bundle. It opens CDI features to regular OSGi bundles and ensure the compatibility of Weld-OSGi with old OSGi application.

But the OSGi looking process for services is still a complicated, in the next section you will see how you can use CDI programming in order to get your OSGi services with Weld-OSGi.

Create a new bean bundle that will use the auto published services by injection. It will be the hello-world-consumer2-multilingual bean bundle.

The entry point of your bean bundle, the com.sample.App.java main class

package com.sample;

import com.sample.api.HelloWorld;
import com.sample.api.Language;
import org.osgi.cdi.api.extension.Service;
import org.osgi.cdi.api.extension.annotation.OSGiService;
import org.osgi.cdi.api.extension.events.BundleContainerEvents;

import javax.enterprise.event.Observes;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;

public class App {

    @Inject (1)
    @OSGiService
    HelloWorld helloWorld;

    @Inject (2)
    Service<HelloWorld> helloWorldService;

    @Inject (3)
    @OSGiService
    @Language("ENGLISH")
    HelloWorld helloWorldEnglish;

    @Inject
    Service<HelloWorld> helloWorldServiceEnglish;

    @Inject
    @OSGiService
    @Language("FRENCH")
    HelloWorld helloWorldFrench;

    @Inject
    Service<HelloWorld> helloWorldServiceFrench;

    @Inject
    @OSGiService
    @Language("GERMAN")
    HelloWorld helloWorldGerman;

    @Inject
    Service<HelloWorld> helloWorldServiceGerman;

    HelloWorld helloWorld2;
    HelloWorld helloWorldEnglish2;
    HelloWorld helloWorldFrench2;
    HelloWorld helloWorldGerman2;

    public void onStartup(@Observes BundleContainerEvents.BundleContainerInitialized event) { (4)
        helloWorld2 = helloWorldService.get(); (5)
        helloWorldEnglish2 = helloWorldServiceEnglish.select(new LanguageAnnotationEnglish()).get(); (6)
        helloWorldFrench2 = helloWorldServiceFrench.select("(language.value=FRENCH)").get(); (7)
        helloWorldGerman2 = helloWorldServiceGerman.select("(language.value=GERMAN)").get();

        helloWorld.sayHello(); (8)
        helloWorld2.sayHello();
        helloWorldEnglish.sayHello();
        helloWorldEnglish2.sayHello();
        helloWorldFrench.sayHello();
        helloWorldFrench2.sayHello();
        helloWorldGerman.sayHello();
        helloWorldGerman2.sayHello();

        for (HelloWorld service : helloWorldService) { (9)
            service.sayHello();
        }
    }

    public void onShutdown(@Observes BundleContainerEvents.BundleContainerShutdown event) { (10)
        helloWorld.sayGoodbye(); (11)
        helloWorld2.sayGoodbye();
        helloWorldEnglish.sayGoodbye();
        helloWorldEnglish2.sayGoodbye();
        helloWorldFrench.sayGoodbye();
        helloWorldFrench2.sayGoodbye();
        helloWorldGerman.sayGoodbye();
        helloWorldGerman2.sayGoodbye();

        for (HelloWorld service : helloWorldService) {
            service.sayGoodbye();
        }
    }

    private class LanguageAnnotationEnglish extends AnnotationLiteral<Language> implements Language {
        @Override
        public String value() {
            return "ENGLISH";
        }
    }
}

A lot of things to discuss here. There is two ways to get an OSGi service injected:

If you want to choose the implementation:

Finally you say hello (8) and goodbye (11) when its right (4) (10) in all languages with all technique.

Same old configuration files:

Your project should now looks like that:

hello-world-consumer2-multilingual
      pom.xml
    - src
        - main
            - java
                - com.sample
                  App.java
            - resources
                - META-INF
                  beans.xml
                  hello-world-consumer2-multilingual.bnd 

Try your new hello-world-consumer2-multilingual bean bundle in the OSGi environment:

You know now how to use the OSGi service layer using Weld-OSGi. Howover you may refer to the specification to specific usage and niceties. In the next chapters you will see what other things Weld-OSGi can do, helping you using OSGi framework for your application: