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.


About this entry