Registering a servlet filter in Apache Karaf

I have already written several articles about deploying web applications in OSGi, and in particular in Apache Karaf. This blog post is a small addition to indicate how to deploy a filter in such an environment.

My first attempt was trying to register my filter directly in the HTTP service. Unfortunately, there is no method for this. After searching the web, I finally found an example here. Apache Karaf comes with Pax-Web. And Pax-Web has extender modules. Basically, all you have to do is registering your filter as an OSGi service. Pax-Web extender modules will be notified about this service (white board pattern) and automatically register it as a servlet filter in the web server (which is Jetty, by the way).

Personnaly, I use this approach to add authentication to REST services. They are implemented with Jersey 1.x and served by Karaf’s web server. The filter intercepts requests and verifies they have the identity token. Obviously, it aims at being used with HTTPS.

So, here are some code snippets.
Let’s start with the filter itself.

public class AuthenticationFilter implements Filter {

	private final Logger logger = Logger.getLogger( getClass().getName());

	private AuthenticationManager authenticationMngr;
	private boolean authenticationEnabled;
	private long sessionPeriod;


	@Override
	public void doFilter( ServletRequest req, ServletResponse resp, FilterChain chain )
	throws IOException, ServletException {

		if( ! this.authenticationEnabled ) {
			chain.doFilter( req, resp );

		} else {
			HttpServletRequest request = (HttpServletRequest) req;
			HttpServletResponse response = (HttpServletResponse) resp;
			String requestedPath = request.getRequestURI();
			this.logger.info( "Path for auth: " + requestedPath );

			// Find the session ID in the cookies
			String sessionId = null;
			Cookie[] cookies = request.getCookies();
			if( cookies != null ) {
				for( Cookie cookie : cookies ) {
					if( UrlConstants.SESSION_ID.equals( cookie.getName())) {
						sessionId = cookie.getValue();
						break;
					}
				}
			}

			// Is there a valid session?
			boolean loggedIn = false;
			if( ! Utils.isEmptyOrWhitespaces( sessionId )) {
				loggedIn = this.authenticationMngr.isSessionValid( sessionId, this.sessionPeriod );
				this.logger.finest( "Session " + sessionId + (loggedIn ? " was successfully " : " failed to be ") + "validated." );
			} else {
				this.logger.finest( "No session ID was found in the cookie. Authentication cannot be performed." );
			}

			// Valid session, go on. Send an error otherwise.
			// No redirection, we mainly deal with our web socket and REST API.
			boolean loginRequest = requestedPath.endsWith( IAuthenticationResource.PATH + IAuthenticationResource.LOGIN_PATH );
			if( loggedIn || loginRequest ) {
				chain.doFilter( request, response );
			} else {
				response.sendError( 403, "Authentication is required." );
			}
		}
	}


	@Override
	public void destroy() {
		// nothing
	}


	@Override
	public void init( FilterConfig filterConfig ) throws ServletException {
		// nothing
	}

	public void setAuthenticationEnabled( boolean authenticationEnabled ) {
		this.authenticationEnabled = authenticationEnabled;
	}

	public void setAuthenticationManager( AuthenticationManager authenticationMngr ) {
		this.authenticationMngr = authenticationMngr;
	}

	public void setSessionPeriod( long sessionPeriod ) {
		this.sessionPeriod = sessionPeriod;
	}
}

And here is the section in my class that registers my filter.
It is the same method that registers my servlets in the HTTP service.

this.authenticationFilter = new AuthenticationFilter();
this.authenticationFilter.setAuthenticationEnabled( this.enableAuthentication );
this.authenticationFilter.setAuthenticationManager( this.authenticationMngr );
this.authenticationFilter.setSessionPeriod( this.sessionPeriod );

initParams = new Hashtable<> ();
initParams.put( "urlPatterns", "*" );

// Consider the bundle context can be null (e.g. when used outside of OSGi)
if( this.bundleContext != null )
	this.filterServiceRegistration = this.bundleContext.registerService( Filter.class, this.authenticationFilter, initParams );
else
	this.logger.warning( "No bundle context was available, the authentication filter was not registered." );

In Karaf 4.x, this is enough to register a servlet filter.
There are other alternatives, but this one requires less code. Read Achim’s answer here for more details.

Generating swagger.json files with Enunciate and custom objects mappers

That’s a long title.
To make short, let’s just say I wanted to generate a Swagger file for Roboconf‘s REST API. This API is implemented in Java with Jersey and uses Jackson to handle the mapping between Java and JSon.

After exploring many solutions, I chose Enunciate to generate this file from my API. One of the best aspects of Enunciate is that it uses Javadoc comments (and optionally annotations) to populate the swagger.json file. The generated file can directly be read by Swagger UI.

Custom JSon de/serialization

One of the issues I met was related to our Java-JSon binding.
Indeed, our project uses custom object mappers. These mappers are a way for Jackson to tailor the JSon serialization and deserialization from and to Java objects. It is very convenient as it gives a maximum of control over what is returned by Jersey. However, this resulted in troubles when looking at the type definitions in the swagger.json file. Indeed, be it with Enunciate or Swagger Java tools, they all use Java types introspection to deduce the shape of the JSon objects. That’s a real problem if you use your own object mapper.

Fortunately, since version 2.6, Enunciate now supports Jackson mix-ins. A Jackson mix-in is in fact a Java class that you can annotate to customize the shape of the generated JSon structures. Let’s take an example. Let’s assume you have a model class that is returned by one of your REST operations.

public class MyModel {
     private String firstName, lastName;
     private int age;

     // All the setters and getters here...
}

Let’s assume this class is defined by someone else, in a different project. How can you customize the JSon generation as you cannot (or do not want) to modify the source code? Mix-ins to the rescue! You define another class and annotate it.

public abstract class MyModelMixin {

     // Do not serialize
     @JsonIgnore
     public abstract int getAge();

     // Change the property name
     @JsonProperty( "name" )
     public abstract String getLastName();
}

Jackson provides a way to associate these two classes in an object mapper. And since version 2.6, Enunciate also provides a way to define mix-ins through the enunciate.xml file (the file that configures what Enunciate generates).

<?xml version="1.0"?>
<enunciate 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.6.0.xsd">

	<title>Roboconf REST API</title>
	<description>The REST API for Roboconf's Administration</description>
	<contact name="the Roboconf team" url="http://roboconf.net" />

	<modules>
		<!-- Disabled modules: almost all -->
		<jackson1 disabled="true" />
		<jaxb disabled="true" />
		<jaxws disabled="true" />
		<spring-web disabled="true" />
		<idl disabled="true" />

		<c-xml-client disabled="true" />
		<csharp-xml-client disabled="true" />
		<java-xml-client disabled="true" />
		<java-json-client disabled="true" />
		<gwt-json-overlay disabled="true" />
		<obj-c-xml-client disabled="true" />
		<php-xml-client disabled="true" />
		<php-json-client disabled="true" />
		<ruby-json-client disabled="true" />

		<!-- Enabled modules -->
		<jackson disabled="false" collapse-type-hierarchy="true">
			<mixin source="net.roboconf....MyModelMixin" target="net.roboconf....MyModel" />
		</jackson>
		<jaxrs disabled="false" />
		<docs disabled="false" />
		<swagger disabled="false" basePath="/roboconf-dm" host="localhost:8181" />
	</modules>

</enunciate>

You can define as many mixin elements that you need.

Pros and Cons

I tested this solution yesterday and it works fine.
I however found two drawbacks to it.

First, you have to write a second model just for the documentation. How can you verify that it is coherent with what you defined in your custom object mappers? The best option would be to get rid of your mapper and also rely on Jackson mixins for the Java-JSon conversions.

Second issue, Jackson mix-ins only work when you want to remove or update a JSon property. But they do not allow you to add properties. It is true in Enunciate but also in Jackson. There was a ticket created about it in the Jackson project but it was marked as “won’t be solved” as it would raise many problems.

Let’s illustrate it with our example.
Let’s imagine you defined in your object mapper a new JSon field called “fullName” which is the concatenation of both name parts. Updating your mixin with…

public abstract class MyModelMixin {

     // Do not serialize
     @JsonIgnore
     public abstract int getAge();

     // Change the property name
     @JsonProperty( "name" )
     public abstract String getLastName();

     // Add a new property (will not work)
     @JsonProperty( "fullName" )
     public abstract String getFullName();
}

… will not work.
You will not find a property called “fullName” in the swagger.json file. Mix-ins cannot address this use case. Maybe a new upgrade in Enunciate might do the trick for the documentation generation. But Jackson does not support it. You have to use custom object mappers.

So, to summer it up, Jackson mix-ins are to use only when you want to remove or rename JSon properties. Not to add new properties.

Workaround

There is no solution to directly mix custom objects mappers with Enunciate (or other Swagger tools). They all rely on a Java type hierarchies while custom object mappers let your creativity unbound.

So, you have to make a two-step process.
First, let Enunciate make most of the work, and then, fix what was generated. I implemented it with a Java class invoked during the project’s Maven build process through ANT. Let’s take a look at this. Here are the relevant parts of the POM file.

<?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">

	<!-- ... -->
	
	<properties>
		<enunciate.version>2.6.0</enunciate.version>
	</properties>
	
	<dependencies>
		
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.2.2</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>			
			<plugin>
				<groupId>com.webcohesion.enunciate</groupId>
				<artifactId>enunciate-maven-plugin</artifactId>
				<version>${enunciate.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>docs</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<docsDir>${project.build.directory}/docs</docsDir>
					<configFile>${project.basedir}/enunciate.xml</configFile>
				</configuration>
			</plugin>
			
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-antrun-plugin</artifactId>
				<executions>
					<execution>
						<phase>test-compile</phase>
						<configuration>
							<target>
								<property name="test_classpath" refid="maven.test.classpath"/>
								<java classname="net.roboconf.dm.rest.services.swagger.UpdateSwaggerJson" classpath="${test_classpath}" />
							</target>	
						</configuration>
						<goals>
							<goal>run</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>build-helper-maven-plugin</artifactId>
				<version>1.12</version>
				<executions>
					<execution>
						<goals>
							<goal>attach-artifact</goal>
						</goals>
						<configuration>
							<artifacts>
								<artifact>
									<file>${project.build.directory}/docs/apidocs/ui/swagger.json</file>
									<type>json</type>
									<classifier>swagger</classifier>
								</artifact>
							</artifacts>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>

As you can see, there are 3 plug-in invocations in this sample. One is about the Enunciate Maven plug-in. It generates the documentation during the process-sources phase. The second one is the Maven ANT plug-in that executes a class located in the test sources. Indeed, since it is a build helper, there is no reason to put it in the main sources. This is why we perform this step during the test-compile phase. Notice we must pass the class path of the test sources. Eventually, we use the Maven build helper plug-in to attach the generated swagger.json file to the Maven artifacts. This way, this file will be deployed on our Maven repository along with our other built artifacts.

Let’s now take a look at our updater class.
It uses GSon to parse and update the JSon file.

public class UpdateSwaggerJson {

	final Set<Class<?>> processedClasses = new HashSet<> ();


	/**
	 * @param args
	 */
	public static void main( String[] args ) {

		try {
			UpdateSwaggerJson updater = new UpdateSwaggerJson();
			JsonObject newDef = updater.prepareNewDefinitions();
			updater.updateSwaggerJson( newDef );

		} catch( Exception e ) {
			e.printStackTrace();
		}
	}


	/**
	 * Prepares the JSon object to inject as the new definitions in the swagger.json file.
	 * @return a non-null object
	 * @throws IOException if something failed
	 */
	public JsonObject prepareNewDefinitions() throws IOException {

		ObjectMapper mapper = JSonBindingUtils.createObjectMapper();
		StringWriter writer = new StringWriter();
		JsonObject newDef = new JsonObject();

		// Create a model, as complete as possible
		TestApplication app = new TestApplication();
		app.bindWithApplication( "externalExportPrefix1", "application 1" );
		app.bindWithApplication( "externalExportPrefix1", "application 2" );
		app.bindWithApplication( "externalExportPrefix2", "application 3" );

		app.setName( "My Application with special chàràcters" );
		app.getTemplate().externalExports.put( "internalGraphVariable", "variableAlias" );
		app.getTemplate().setExternalExportsPrefix( "externalExportPrefix" );
		app.getTemplate().setDescription( "some description" );

		// Serialize things and generate the examples
		// (*) Applications
		writer = new StringWriter();
		mapper.writeValue( writer, app );
		String s = writer.toString();
		convertToTypes( s, Application.class, newDef );

		// (*) Application Templates
		writer = new StringWriter();
		mapper.writeValue( writer, app.getTemplate());
		s = writer.toString();
		convertToTypes( s, ApplicationTemplate.class, newDef );

		// Etc...

		return newDef;
	}


	/**
	 * @param newDef the new "definitions" object
	 * @throws IOException if something went wrong
	 */
	private void updateSwaggerJson( JsonObject newDef ) throws IOException {

		File f = new File( "target/docs/apidocs/ui/swagger.json" );
		if( ! f.exists())
			throw new RuntimeException( "The swagger.json file was not found." );

		JsonParser jsonParser = new JsonParser();
		String content = Utils.readFileContent( f );

		// Hack the file content directly here.
		// Do whatever raw operations you want.

		JsonElement jsonTree = jsonParser.parse( content );

		Set<String> currentTypes = new HashSet<> ();
		for( Map.Entry<String,JsonElement> entry : jsonTree.getAsJsonObject().get( "definitions" ).getAsJsonObject().entrySet()) {
			currentTypes.add( entry.getKey());
		}

		Set<String> newTypes = new HashSet<> ();
		for( Map.Entry<String,JsonElement> entry : newDef.entrySet()) {
			newTypes.add( entry.getKey());
		}

		currentTypes.removeAll( newTypes );
		for( String s : currentTypes ) {
			System.out.println( "Type not appearing in the updated swagger definitions: " + s );
		}

		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		jsonTree.getAsJsonObject().add( "definitions", jsonParser.parse( gson.toJson( newDef )));
		String json = gson.toJson( jsonTree );
		Utils.writeStringInto( json, f );
	}


	/**
	 * Creates a JSon object from a serialization result.
	 * @param serialization the serialization result
	 * @param clazz the class for which this serialization was made
	 * @param newDef the new definition object to update
	 */
	public void convertToTypes( String serialization, Class<?> clazz, JsonObject newDef ) {
		convertToTypes( serialization, clazz.getSimpleName(), newDef );
		this.processedClasses.add( clazz );
	}


	/**
	 * Creates a JSon object from a serialization result.
	 * @param serialization the serialization result
	 * @param className a class or type name
	 * @param newDef the new definition object to update
	 */
	public void convertToTypes( String serialization, String className, JsonObject newDef ) {

		JsonParser jsonParser = new JsonParser();
		JsonElement jsonTree = jsonParser.parse( serialization );

		// Creating the swagger definition
		JsonObject innerObject = new JsonObject();

		// Start adding basic properties
		innerObject.addProperty( "title", className );
		innerObject.addProperty( "definition", "" );
		innerObject.addProperty( "type", jsonTree.isJsonObject() ? "object" : jsonTree.isJsonArray() ? "array" : "string" );

		// Prevent errors with classic Swagger UI
		innerObject.addProperty( "properties", "" );

		// Inner properties
		innerObject.add( "example", jsonTree.getAsJsonObject());

		// Update our global definition
		newDef.add( "json_" + className, innerObject );
	}
}

And that’s it.
In our implementation, we dropped the property keys of the swagger.json file. Instead, we use the example key. Swagger UI does display it well and does not show any error. Instead of showing some kind of schema, it shows an example of a JSon structure.

Testing the Coherence with our Mapper

How can we be sure this class generates examples for all the types we use in our custom object mapper? Well, let’s just write a unit test for that!

public class UpdateSwaggerJsonTest {

	@Test
	public void verifyProcessedClasses() throws Exception {

		UpdateSwaggerJson updater = new UpdateSwaggerJson();
		updater.prepareNewDefinitions();

		Set<Class<?>> classes = new HashSet<> ();

		// You need a registry somewhere that lists all the classes managed by your object mapper
		classes.addAll( JSonBindingUtils.SERIALIZERS.keySet());
		
		// Remove those you processed in the updater.
		classes.removeAll( updater.processedClasses );

		// They should all have been processed.
		Assert.assertEquals( Collections.emptySet(), classes );
	}
}

Just to show how you how we dealt with this class registry, here is a short snippet taken from our custom object mapper.

public final class JSonBindingUtils {

	public static final Map<Class<?>,? super JsonSerializer<?>> SERIALIZERS = new HashMap<> ();

	static {
		SERIALIZERS.put( Instance.class, new InstanceSerializer());
		SERIALIZERS.put( ApplicationTemplate.class, new ApplicationTemplateSerializer());
		SERIALIZERS.put( Application.class, new ApplicationSerializer());
	}


	/**
	 * Creates a mapper with specific binding for Roboconf types.
	 * @return a non-null, configured mapper
	 */
	@SuppressWarnings( { "unchecked", "rawtypes" } )
	public static ObjectMapper createObjectMapper() {

		ObjectMapper mapper = new ObjectMapper();
		mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
		SimpleModule module = new SimpleModule( "RoboconfModule", new Version( 1, 0, 0, null, null, null ));

		for( Map.Entry<Class<?>,? super JsonSerializer<?>> entry : SERIALIZERS.entrySet())
			module.addSerializer((Class) entry.getKey(), (JsonSerializer) entry.getValue());

		mapper.registerModule( module );
		return mapper;
	}

Conclusion

This solution (or workaround) may not seem ideal.
However, custom object mappers are somehow a workaround themselves. They require you to code things. Therefore, it is not surprising we may have to code some little things to hook up with Swagger-based documentation.

IMO, the code shown here remains quite simple to maintain.
And it allows you to add unit tests to verify assertions on your swagger.json file.

I created a Gist for all the sources here. You can see them in action in the sources of Roboconf’s REST API.

Verify JAX Doclets in Maven builds

In few words, I have a Java REST API that is defined with Jersey.
I use Enunciate to generate a Swagger JSon file. Enunciate uses Javadoc comments (and optionally some annotations) to fill-in the REST documentation. But it also supports JAX doclets for complementary information.

A doclet that is quite useful is the @HTTP one.
This tag allows to define the return codes of a REST operation. And since it makes sense for all the operations of our APIs, I wanted to add an automatic verification at build time. I searched the web for something that could do this with Maven, and after quite a long search, I found the right Checkstyle module for that.

Here is the XML snippet to insert in your POM.

<build>
<plugins>
	<plugin>

		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-checkstyle-plugin</artifactId>
		<version>2.17</version>
		<executions>
			<execution>
				<id>check-rest-resources</id>
				<phase>process-sources</phase>
				<goals>
					<goal>check</goal>
				</goals>
				<configuration>
					<includes>**/I*Resource.java</includes>
					<checkstyleRules>
						
	<module name="Checker">							
		<property name="charset" value="UTF-8" />
		<module name="TreeWalker">
			<module name="WriteTag">
				<property name="tag" value="@HTTP" />
				<property name="tagFormat" value="\S" />
				<property name="severity" value="error" />
				<property name="tagSeverity" value="ignore" />
				<property name="tokens" value="METHOD_DEF" />
			</module>
		</module>
	</module>
								
					</checkstyleRules>
				</configuration>
			</execution>
		</executions>
	</plugin>
		
</plugins>
</build>

So, we use the Maven plug-in for Checkstyle.
This set of rules will only apply to classes whose name is compliant with the I*Resource pattern (this is a convention we use in our project…).

And we use the WriteTag module that verifies if a given tag is used or not in a class. The tag here is @HTTP. If we do not find it (the severity property), we raise an ERROR. If we find it (tagSeverity), we ignore it. And we apply this verification to methods only (not to classes or anything else). You should easily be able to adapt it by reading the documentation of this module.

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.