HTTP Service specification explained by Example Print E-mail
Written by Valery Abu-Eid   
Saturday, 11 October 2008 22:24

HTTP Service specification of the OSGi Compendium Services aims to provide developers with a standard way to run/provide Web Components in OSGi Environments - Currently only Servlets and Resources. Like any other OSGi Compendium Service, HTTP Service has a standard API and implementations provided by different vendors, in this article we will use the implementation provided by Pax Web project since it's compatible with the three major OSGi Frameworks.

HTTP Service is the best solution for providing Servlets and Web Resources, but for Web Applications development it's always better to use OSGi-compatible Web Frameworks (like Wicket, Struts and Jasper) instead of working with a low level API.

HttpService and HttpContext interfaces

Working with HTTP Service you will by using two interfaces: org.osgi.service.http.HttpService and org.osgi.service.http.HttpContext. The HttpService will be provided as an OSGi Service by the implementation of the HTTP Service specification, you will use this service to register Servlets and Resources (e.g., images, html files, java script files, etc.). When registering Servlets and resources you will associate them to an HttpContext, you can either provide the HttpContext yourself or use a default one provided by the HTTP Service implementation (which should be fine in most cases) - More on Http Context is covered below.

The example application and its source code are contained in http-service-example.zip. The example application was tested with Equinox, Felix and Knopflerfish.

Providing Servlets

To register a Servlet you need to:

  • Acquire a reference to the HttpService service.
  • Use the registerServlet method of the HttpService to register your servlet. The first parameter is the alias of the servlet to be registered, it should start with "/" - The alias will be used to map requests to the servlet based on URL matching rules. The second parameter is the instance of the servlet to be registered. The third is init parameters of the servlet (parameters that will be passed to the init method of the servlet), pass null if none. The fourth parameter is the HttpContext, pass null if you are fine with the default one.
Alias Matching Rules

When processing requests the HTTP Service will look for the exact match of the requested URI. If a servlet with such alias not found, it will look for the longest alias wich is a parent for the requested URI. Below are few examples:

"/hello" is the exact match of "/hello"

"/hello" matches "/"

"/author/title" matches "/author"

"/author/title" doesn't match "/auth"

"/author/title" is a more suitable match than "/author" for "/author/title/page"
The same servlet instance shouldn't be registered twice!

When a servlet is registered its init(...) method will be invoked, so the same servlet instance (the same object not the the same type) shouldn't be registered so its init method wouldn't be called twice.

Below are two example classes, the Activator which runs the registrar class and binds the HTTP Service to it and GreetingsServletRegistrar which registers the servlet.


import org.osgi.service.http.HttpService;
import ...;

public class Activator implements BundleActivator {
	
	//@Override
	public void start(BundleContext bundleContext) throws Exception {
		/// OsgiServiceBinder is a utility class that binds OSGi Services
		/// to properties using setter methods. We will use it here to 
		/// bind the HttpService to the GreetingsServletRegistrar.
		binder = new OsgiServiceBinder(bundleContext);
		binder.bind(getGreetingsServletRegistrar(), "setHttpService",
				ServiceFilter.forInterface(HttpService.class.getName()));
	}
	
	private final GreetingsServletRegistrar greetingsServletRegistrar =
			new GreetingsServletRegistrar();
	protected GreetingsServletRegistrar getGreetingsServletRegistrar() {
		return greetingsServletRegistrar;
	}
	
	private OsgiServiceBinder binder;
	
	// ...
}


import org.osgi.service.http.HttpService;

public class GreetingsServletRegistrar {
	
	/**
	 * This method is invoked when the HTTP Service is updated.
	 */
	protected void httpServiceUpdated() {
		if (getHttpService() != null) {
			registerGreetingsServlet();
		}
	}
	
	protected void registerGreetingsServlet() {
		try {
			String servletAlias = "/greetings";
			
			/// Since the HTTP Service is available from "http://localhost:8080"
			/// the Greetings servlet will be available from 
			/// "http://localhost:8080/greetings"
			getHttpService().registerServlet(servletAlias,
					new GreetingsServlet(),
					null /* No Init Params we will pass to the servlet */,
					null /* By passing null we tell the http service to use 
							the default http context, we can use the value 
							getHttpService().createDefaultHttpContext()
							to achieve the same result */);
			
			System.out.println("Registered Greetings Servlet with alias: "
					+ servletAlias);
		} catch (Throwable ex) {
			ex.printStackTrace();
		}
	}
	
	
	private HttpService httpService;
	public HttpService getHttpService() {
		return httpService;
	}
	public void setHttpService(HttpService httpService) {
		this.httpService = httpService;
		httpServiceUpdated();
	}
	
}

Providing Resources

To register resources (e.g., images, css, html, zip files) you need to:

  • Acquire a reference to the HttpService service.
  • Use the registerResources method of the HttpService to register a resource. The first parameter is the alias of the resource which should start with "/", matching rules are the same as for servlet aliases. The second is a descriptive name of the resource. The third parameter is the HttpContext, pass null if you are fine with the default one.
  • Unlike with servlets, many will find the resource loading logic of the default HttpContext not suitable for them, if this is your case then you will need to provide an Http Context that implements your resource loading logic. You can find below an example which handles such case.
Default Resource Retrieving Logic

The default HttpContext maps the resource request to the bundle resource using Bundle.getResource(String).

The example below registers a resource that provides images contained in the "META-INF/images" directory of the example bundle. Please note that unlike with the example above, the activator is not shown in this one since it performs the same task.


import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import ...;

public class ImagesResourceRegistrar {
	
	protected void registerGreetingsServlet() {
		try {
			String imagesResourceAlias = "/images";
			
			
			/// Since the HTTP Service is available from
			/// "http://localhost:8080", the Images resource will be 
			/// available on "http://localhost:8080/images" and since
			/// we have the files "haruhi.jpg" and "nagato.gif" in the
			/// "META-INF/images" directory of the bundle the images
			/// will be available on these URLs:
			/// http://localhost:8080/images/nagato.gif
			/// http://localhost:8080/images/haruhi.jpg
			getHttpService().registerResources(imagesResourceAlias,
					"Images",
					new ImagesResourceHttpContext());
			
			System.out.println("Registered Images Resouce with alias: "
					+ imagesResourceAlias);
		} catch (Throwable ex) {
			ex.printStackTrace();
		}
	}
	
	
	protected class ImagesResourceHttpContext implements HttpContext {
		
		public String getMimeType(String name) {
			if (name.endsWith(".jpg") || name.endsWith(".jpeg")) {
				return "image/jpeg";
			} else if (name.endsWith(".gif")) {
				return "image/gif";
			} else {
				return null;
			}
		}
		
		public URL getResource(String name) {
			int lastSlashIndex = name.lastIndexOf('/');
			/// We need to extract the file name from the resource name.
			String fileName = lastSlashIndex == -1 ? name : name.substring(
					lastSlashIndex + 1);
			
			System.out.println(String.format(
					"Requested Resource: %s, Resource File Name: %s",
					name, fileName));
			
			return getBundleContext().getBundle().getResource(
					"META-INF/images/" + fileName);
		}
		
		public boolean handleSecurity(HttpServletRequest request,
				HttpServletResponse response) throws IOException {
			/// return true otherwise the HTTP Service will think that
			/// security checks has failed.
			return true;
		}
		
	}
	
	
	/// We require the bundle context since we need to retrieve
	/// images from the bundle.
	public ImagesResourceRegistrar(BundleContext bundleContext) {
		this.bundleContext = bundleContext;
	}
	
	private HttpService httpService;
	public HttpService getHttpService() {
		return httpService;
	}
	public void setHttpService(HttpService httpService) {
		this.httpService = httpService;
		httpServiceUpdated();
	}
	
	private final BundleContext bundleContext;
	protected BundleContext getBundleContext() {
		return bundleContext;
	}
	
	/// This method is invoked when the HTTP Service is updated.
	protected void httpServiceUpdated() {
		if (getHttpService() != null) {
			registerGreetingsServlet();
		}
	}
	
}

HttpContext Usage

Below are the most important usages of HttpContext.

  • Servlet Context sharing policy: In traditional Java Web Applications, multiple servlets in the same Web Application share the same Servlet Context. If you want the servlets registered using HTTP Service to share the same Servlet Context then you need to pass the same HttpContext instance to the registerServlet method when registering the servlets.
  • Resource retrieving logic: You can provide your own HttpContext to implement your own resource retrieving logic - This was covered in the section above.
  • Security Validations: By implementing the handleSecurity(HttpServletRequest, HttpServletResponse) method of the HttpContext you can implement Authentication and Authorization logic. If you consider the request to be a failure one from security perspective then fill the response object (HttpServletResponse parameter) with needed data (mostly it will be HTTP Status and Error Description message) and return false so the HTTP Service would stop processing the request. If the request is successful or you don't intend to perform security validations return true.

HTTP Service specification Implementations

The two most notable implementations of the HTT Service specification are: Equinox HTTP Service and Pax Web project. Pax Web is compatible with all OSGi Frameworks, unlike Equinox HTTP Service. You will find that some of the implementations provide different bundle versions: bundles that provide the HTTP Service implementations, bundles that provide implementations and the API and bundles that provide all mentioned above plus an embedded HTTP Engine. All HTTP Service implementations use Jetty as the HTTP Engine.