Developing web components with OSGi can be very simple if you’re using the OSGi Http Whiteboard Specification (see OSGi Compendium Chapter 140). For example the Apache Felix Http Implementation supports this standard.
Building your own Application
With traditional web application development, you usually package your application into its own web application archive (war file) and deploy this into an application server (servlet engine). Each application has its own servlet context and is mounted at some path. You can either mount an application at the root or any other path, e.g. at /my-app.
Using OSGI and the OSGi Http Whiteboard Specification, when you develop a web application you usually but the web components into a bundle and deploy this bundle. Depending on your needs you might also split up the web components into different bundles. But how do you create the equivalent of a servlet context known from web archive deployments?
You can create an own servlet context for your application through the Http Whiteboard. But there is no special deployment format, you simply deploy your app through bundles. And in contrast to traditional webapp deployment, this is leveraging all the well-known OSGi features. And of course source a context is represented through a service.
Let’s start with creating a simple application. The first thing you do is you create your own servlet context. This is done by registering a ServletContextHelper service (again for the examples I’m using Declarative Services and the corresponding annotations):
@Component( service = ServletContextHelper.class, property = { HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + AppServletContext.NAME, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + "=/guessinggame" } ) public class AppServletContext extends ServletContextHelper {}
As you can see from the example, ServletContextHelper is not an interface but an abstract class and your implementation gets all the default behaviour by simply extending this class. An abstract class has been chosen over the interface in order to be able to add methods to the service without breaking existing implementations. More important are the two required properties: a unique name and a context paths. The path is equivalent to the context path of a normal web application. The name is used to reference this servlet context. If you want to associate a servlet, resource, servlet filter, or listener to this context, you can do this by adding an additional property to select this context:
@Component(service = Servlet.class, property = { HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN + "=/game", HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT + "=(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME +"=" + AppServletContext.NAME + ")"}) public class GameServlet extends HttpServlet {...}
The context select property defines a filter expression to select the servlet context. While you can use any filter expression there, it is pretty common to simply filter against the name of the servlet context. If the context select property is missing, your web component is added to the default context.
The path of the servlet context is prepended before the path of the web component, so in the example above the servlet is mounted at “guessinggame/game”.