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.

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.

Web Applications with OSGi – Static Files

Context

I currently work on migrating an existing web application to OSGi (*).
The current solution relies on Jersey to expose REST services. It also exposes static resources for the web. And what I need to do is to drop Tomcat and deploy it inside an OSGi container. This is the last step to perform to complete our OSGi migration.

(*) Our motivation to use OSGi is classloaders isolation.

Beyond deploying a web application and Jersey services, we also have to plug it with iPojo. iPojo is the framework we chose to define components and services in our application. And to be complete in our description, we will deploy it on Apache Karaf 3.0.x, with Felix as the default container.

Before breaking my application, and given all the things that are involved, I wanted to create a short sample to make sure verything works together. Most of all, and because I did not find a ready-to-use solution on the web, I wanted to determine HOW everything could work together. To be honest, I did not succeeed on the first time. In fact, I have been forced to proceed step by step.

So, this post is the first of a series about web applications in OSGi. And it will start with very simple cases.

Serving Static Files in OSGi

I am NOT going to tell you to write a bundle activator and register a servlet with the HTTP service. Now, OSGi containers support the deployment of web bundles (WAB). This is a ZIP archive that contain both web resources (like in a WAR) and an OSGi bundle. Said differently, it is a WAR file with OSGi metadata in a MANIFEST. And it does not have to use a specific file extension. So, it could also be a JAR file with the structure of a WAR. Using one extension or another depends on your context.

So, here is an example of a WAB that will server static resources. No servlet, no JSP… Since it is very simple, and not dependent on OSGi, I will create a WAR file and add OSGi metadata. This way, I will be able to deploy it in an OSGi container with web support, or in any application server such as Tomcat.

Let’s start with 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>serving-static-files</artifactId>
	<name>OSGi Sample :: Web App :: Static Files</name>
	<packaging>war</packaging>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<configuration>
					<archive>
						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
					</archive>
				</configuration>
			</plugin>
			
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<executions>
					<execution>
						<id>bundle-manifest</id>
						<phase>process-sources</phase>
						<goals>
							<goal>manifest</goal>
						</goals>
						<configuration>
							<instructions>
								<Web-ContextPath>static-files</Web-ContextPath>
								<Webapp-Context>static-files</Webapp-Context>
							</instructions>
						</configuration>
					</execution>
				</executions>
				<configuration>
					<supportedProjectTypes>
						<supportedProjectType>jar</supportedProjectType>
						<supportedProjectType>bundle</supportedProjectType>
						<supportedProjectType>war</supportedProjectType>
					</supportedProjectTypes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

The project structure must be…

pom.xml
src/main/webapp/
* Your static resources (HTML, CSS, JPG…)
* WEB-INF/web.xml

And here is the content of the web.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xmlns="http://java.sun.com/xml/ns/javaee" 
		xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
		xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
		id="WebApp_ID" version="3.0">

	<display-name>Welcome Page</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
	
	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>*.jpg</url-pattern>
	</servlet-mapping>
	
	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>*.html</url-pattern>
	</servlet-mapping>
</web-app>

The sources are available on GitHub.

Run mvn clean install.
Pick up the WAR file under the target directory and you can directly deploy it in a Tomcat server, or in an OSGi container with web support. How to enable web support in an OSGi container? Well, you can rely on Pax Web. Pax Web is a project that embeds application servers such as Jetty and Tomcat in bundles. Since I deploy on Apache Karaf, I used the WAR feature, which relies on Pax Web.

So, if you are using Apache Karaf, start it and type in feature:install war (or if you are using Karaf 2.x, features:install war). This will enable web support in your installation. And to deploy your web bundle in Karaf, just drop it in Karaf’s deploy directory.

If you want to make sure it is correctly deployed, you can first check in your browser that your resources are displayed at http://localhost:8181/static-files (assuming you kept the default settings for Jetty). And you can also install Karaf’s web console (feature:install web-console) and go to http://localhost:8181/system/console/http (default credentials: karaf / karaf). You will see the HTTP and web contexts there.

Serving a JSP in OSGi

Now, let’s assume you want to use JSP files and have a custom servlet in your web application. What was defined before is still valid. The only modification to bring is in the POM file.

Simply add the following dependency.

<dependencies>
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>servlet-api</artifactId>
		<version>2.5</version>
		<scope>provided</scope>
	</dependency>
</dependencies>

Make sure the scope is provided.
The Maven-bundle-plugin will automatically add the package imports in the MANIFEST. And these imports will be provided at runtime by other bundles.

This post is quite detailed.
But I found a lof of information on this blog entry. You may be interested by its reading.