OSGi JAX-RS and bnd declarative service
Asked Answered
G

3

6

I want to migrate my EE application to OSGi. my application consist of business libraries, database JPA/Entities and a REST/WS interfaces. it also has a web client.

I start by prototyping the structure and making all the interfaces and bundles talking to each other in OSGi clean way. I want to use a clean specification as much as possible without any specific vendor or framework.

I am using bnd maven plugin to generate the manifest and the declarative services. I want to make a call from my rest resources to an OSGI service (on another bundle) using injection like so:

@Path("some-resources")
@Component
public class SomeResources{

   private SomeService service = null;

   @Reference
   public void setController(SomeService service) {   // <- this is never called
    this.service = service;
   }

   @GET
   @Produces(javax.ws.rs.core.MediaType.APPLICATION_XML)
   public Object getSomeService() {                  // <- called 
    try {
        service.process("Hello World");              // <- Error null object
   }
    ...

}

Can i annotate the resource with bnd @Component and can the @Resource be injected? everything works fine but the service is always null.

What should be the way to declare my bundle for BND to make it a web/wab package?

I use maven bundle:

<packaging>bundle</packaging>

...

        <plugin>                    
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.3.7</version>
                <extensions>true</extensions>
                <dependencies>
                    <dependency>
                        <groupId>biz.aQute</groupId>
                        <artifactId>bndlib</artifactId>
                        <version>1.50.0</version>
                    </dependency>
                </dependencies> 
                <configuration>
                    <supportedProjectTypes>
                        <supportedProjectType>ejb</supportedProjectType>
                        <supportedProjectType>war</supportedProjectType>
                        <supportedProjectType>wab</supportedProjectType>
                        <supportedProjectType>bundle</supportedProjectType>
                        <supportedProjectType>jar</supportedProjectType>
                    </supportedProjectTypes>
                    <instructions>
                        <_include>-osgi.bundle</_include>
                    </instructions>
                </configuration>
                <executions>
                    <execution>
                        <id>bundle-manifest</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>manifest</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>bundle-install</id>
                        <phase>install</phase>
                        <goals>
                            <goal>install</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>                    

 ...

with bnd instructions

Web-ContextPath: my-root-http/rest/
Service-Component: *
Gammy answered 17/4, 2012 at 22:50 Comment(3)
I recently came across a similar problem (service didn't get bound) and tracked it down to the split package problem. Have you tried putting your service-interface into a separate package?Slicer
thanks @BjörnPollex for the suggestion. I have 3 bundles, one with interfaces only, one with the service implementation and the REST bundel. I acctually moved it back to the REST bundel to see if that wil work but no luck. If what I am doing is currect than maybe the issue is with the SCR not finding my DS xml.Gammy
I have some progress on the above. the packages are deployed and some services are running. but I cannot get the REST class above to run. I am using maven-bundle-plugin. what do I need to specify to get a war/wab package to deploy DS services?Gammy
E
5

OSGi has a part of specification called Remote Services. In very short it works in the way that you can register services with special service-properties and based on the properties technologies should pick up your service and create an endpoint from them. It is not only about REST but about any technology that handles remote calls. You may find information in the OSGi Core specification under the "Remote Services" chapter.

Well it is a specification but who implements it? Currently there are two bigger projects I tried. CXF DOSGi and Eclipse ECF. They offer several technologies that support Remote Services Specification. CXF especially upports Jax-RS based on it's implementation on both server and client side.

As I did not want to use spring specific solutions inside OSGi I did not use CXF on the end but created my own solution. It is based on Jersey and the Remote Services specification. When an OSGi Service is specified with the service.exported.interfaces=* and the service.exported.configs=org.everit.osgi.remote.jersey it will create a rest endpoint under the /rest/ path with HttpService. Your bundle does not have to be a wab it can be a simple bundle.

I must mention that if you expose your services via any of the Remote Services implementation you should take the Jax-RS annotations into an interface that is implemented by your original class and expose your service based on that interface.

Instead of @Resource and @Component annotations inside OSGi I suggest that you should use Blueprint (part of OSGi spec) that is amazingly similar to Spring. Currently Apache Aries and Gemini Blueprint implements it. With blueprint you can easily create beans and wire them to each other. If register your remote service in this way you can set any of the property with the help of blueprint (just like the property of a bean in the spring applicationcontext.xml).

You can find a sample application that I made at https://source.everit.biz/svn/everit-osgi/trunk/samples/jaxrs/ (user/passwd: guest/guest). There is a guide that explains how this samples can be started and developed at http://cookbook.everit.org

I hope the sample application helps you to get started with the Remote Services specification chapter.

To see how to use JPA and Injection (Blueprint) you should check the OSGi compendium specification for possibilities and find the implementation you like. I also made a sample project based on blueprint and hibernate-jpa that you can find as the sibling of the sample url I already provided.

Update

There is also a JAXRS extender implementation I made at https://github.com/everit-org/osgi-remote-jersey. See the README for documentation. It is different from the first in the way that this works based on whiteboard service properties.

Ekaterinoslav answered 29/5, 2012 at 22:43 Comment(0)
G
2

I have experienced a similar problem with OSGi, Declarative Services and Jersey.

A resource can be annotated with @Component and @Reference annotations. This will instruct DS to create an instance of SomeResource class and to inject a valid reference into this instance when all dependecies (references) are satisfied.

The reason why your references are null is because JAX-RS implementation will create a new instance of SomeResource class for each web request. This instance of SomeResource class is not the same as the one created by DS.

I solved this problem by making reference variable static with Java static keyword:

private static SomeService service = null;

This ensured that a dependency reference is tied to a class object instead to an instance and then all instances could see the injected value.

This solution introduced a new problem. This reference must be cleared on unbind event (when service becomes unavailable) because it will not be destroyed when an instance is destroyed.

Gunthar answered 26/6, 2012 at 10:58 Comment(1)
Storing static references to osgi components isn't a good idea.Eximious
C
0

The problem would be solved when the @Path annotated type would be registered as a service itself. With DS you can than just inject other services. I faced this problem myself nearly a year ago. Thats why I wrote a small OSGi JAX-RS Connector which gives you exactly what I have described. Give it a try if you like: https://github.com/hstaudacher/osgi-jax-rs-connector

Clever answered 28/8, 2012 at 8:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.