Web Applications with OSGi – Working with Jersey and iPojo

So far, we have seen how to expose a classic web application in an OSGi container. We have also seen how to expose REST services with Jersey in OSGi. Let’s now take a look at how we can expose REST services that use iPojo services. The complete sources are on GitHub.

More exactly, what we want is that our REST resources access OSGi services. These services will be injected and managed by iPojo. This way, we do not have to write service trackers and so on. The approach described here would probably also apply to Declarative Services (or at least, that’s what I assume).

I did not find a way to make our REST resources iPojo components.
However, we can have an iPojo component that gets our iPojo service and that manually registers a REST servlet within the OSGi’s HTTP service. I found an example illustrating this approach in this Git repository. Many thanks to the author.

The Service

So, let’s start with the example.
I have 3 bundles. One contains the definition of the service I want to use in my REST resources (ipojo-service-api). It is a bundle that exports a single package with a Java interface.

public interface MyService {
	String getDataToDisplay();
}

The POM is quite simple.

<?xml version="1.0" encoding="UTF-8"?>
<project 
		xmlns="http://maven.apache.org/POM/4.0.0" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>net.vzurczak</groupId>
	<artifactId>ipojo-service-api</artifactId>
	<name>OSGi Sample :: API</name>
	<packaging>bundle</packaging>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<configuration>
					<instructions>
						<Export-Package>net.vzurczak.sample.api</Export-Package>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Then, I have one implementation of this service (ipojo-service-impl). It is a bundle that imports the service interface. It also contains the definition of an iPojo component.

public class MyServiceImpl implements MyService {

	@Override
	public String getDataToDisplay() {
		return "Hello World!";
	}
}

And here is the POM.

<?xml version="1.0" encoding="UTF-8"?>
<project 
		xmlns="http://maven.apache.org/POM/4.0.0" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>net.vzurczak</groupId>
	<artifactId>ipojo-service-impl</artifactId>
	<name>OSGi Sample :: iPojo Implementation</name>
	<packaging>bundle</packaging>
	
	<dependencies>
		<dependency>
			<groupId>net.vzurczak</groupId>
			<artifactId>ipojo-service-api</artifactId>
			<version>${project.version}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<configuration>
					<instructions>
						<Import-Package>net.vzurczak.sample.api</Import-Package>
					</instructions>
				</configuration>
			</plugin>
			
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-ipojo-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<goal>ipojo-bundle</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

And here is the iPojo definition for the implementation.

<?xml version="1.0" encoding="UTF-8"?>
<ipojo 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/CURRENT/core.xsd"
		xmlns="org.apache.felix.ipojo">

	<component classname="net.vzurczak.sample.impl.internal.MyServiceImpl" immediate="true" name="my-service-impl" public="false">
		<provides />
	</component>
	
	<instance component="my-service-impl" name="Default Instance for My Service" />
</ipojo>

So far, so good.
Let’s now work on the servlet.

The Servlet

The servlet and REST resources are handled in a third bundle.
It also contains an iPojo component in charge of registering and unregistering a servlet within the HTTP service. Here is its code.

public class RestComponent {

	private HttpService httpService;
	private MyService myService;


	public void starting() throws Exception {

		RestResource res = new RestResource( this.myService );
		RestApplication app = new RestApplication( res );
		ServletContainer jerseyServlet = new ServletContainer( app );

		this.httpService.registerServlet( "/jersey-ipojo", jerseyServlet, null, null );
	}

	public void stopping() {
		this.httpService.unregister( "/jersey-ipojo" );
	}
}

The fields httpService and myService will be injected by iPojo.
When the component instance is valid (i.e. when all its dependencies are resolved), the component registers a Jersey servlet for our REST application. If one of its dependencies disappear, the servlet is unregistered. In fact, I shuld check whether the HTTP service is here before unregistering the servlet. Anyway…

Here is the code of the REST application.
This is a standard REST application, as stated by JAX-RS.

public class RestApplication extends Application {

	private final RestResource myRestResource;

	public RestApplication( RestResource myRestResource ) {
		this.myRestResource = myRestResource;
	}

	@Override
	public Set<Object> getSingletons() {

		HashSet<Object> set = new HashSet<Object> ();
		set.add( this.myRestResource );
		return set;
	}
}

And here is the REST resource.

@Path( "/get-data" )
public class RestResource {

	private MyService myService;

	public RestResource() {
		// nothing
	}

	public RestResource( MyService myService ) {
		this.myService = myService;
	}

	@GET
	public Response getDataToDisplay() {

		String data = "No data.";
		if( this.myService != null )
			data = this.myService.getDataToDisplay();

		return Response.ok().entity( data ).build();
	}

	public MyService getMyService() {
		return this.myService;
	}

	public void setMyService( MyService myService ) {
		this.myService = myService;
	}
}

Once again, the OSGi service is not directly injected in the REST resources. You must use an intermediary component for this. Here is the iPojo definition for this component.

<?xml version="1.0" encoding="UTF-8"?>
<ipojo 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="org.apache.felix.ipojo http://felix.apache.org/ipojo/schemas/CURRENT/core.xsd"
		xmlns="org.apache.felix.ipojo">

	<component classname="net.vzurczak.sample.servlet.internal.RestComponent" name="my-rest-component" immediate="true">
		<requires field="myService" />
		<requires field="httpService" />
		<callback transition="validate" method="starting" />
		<callback transition="invalidate" method="stopping" />
	</component>
	
	<instance component="my-rest-component" name="my-rest-component-instance" />
</ipojo>

Eventually, here is the POM.
There are quite a lot of imports. As in my previous article, Jersey packages are exported by the Jersey bundles.

<?xml version="1.0" encoding="UTF-8"?>
<project 
		xmlns="http://maven.apache.org/POM/4.0.0" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>net.vzurczak</groupId>
	<artifactId>ipojo-servlet</artifactId>
	<name>OSGi Sample :: iPojo Servlet</name>
	<packaging>bundle</packaging>
	
	<properties>
		<jersey.stack.version>1.18.1</jersey.stack.version>
	</properties>
	
	<dependencies>
		<dependency>
			<groupId>net.vzurczak</groupId>
			<artifactId>ipojo-service-api</artifactId>
			<version>${project.version}</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-core</artifactId>
			<version>${jersey.stack.version}</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-servlet</artifactId>
			<version>${jersey.stack.version}</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
	
		<dependency>
			<groupId>org.apache.felix</groupId>
			<artifactId>org.osgi.core</artifactId>
			<version>1.4.0</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>org.apache.felix</groupId>
			<artifactId>org.osgi.compendium</artifactId>
			<version>1.4.0</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<configuration>
					<instructions>
					 	<Import-Package>
							net.vzurczak.sample.api,
							org.osgi.service.http,
							javax.ws.rs.*,
							com.sun.jersey.api.core,
							com.sun.jersey.spi.container.servlet,
							javax.servlet,
							javax.servlet.http,
							javax.naming
						</Import-Package>
					</instructions>
				</configuration>
			</plugin>
			
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-ipojo-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<goal>ipojo-bundle</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

Deployment

Once you have everything, execute mvn clean install on all 3 bundles.
As for my 2 previous articles, I deploy on Apache Karaf. Make sure the HTTP feature is installed. Then, deploy the 3 bundles under Karaf’s deploy directory.

The REST services will be available under http://localhost:8181/jersey-ipojo (e.g. http://localhost:8181/jersey-ipojo/get-data for my GET operation).

Notice

I did not succeed to make the ipojo-maven-plugin work with the _wab instruction of the maven-bundle-plugin. At least, not if you use the JAR extension. This is why I stuck with the bundle packaging for my REST services. The error message was…

Cannot find bytecode for class…

I will probably report it or at least, ask the question on the mailing-list of iPojo.


About this entry