Modular Web Applications with OSGi – Part 2 – Dynamic Web Resources

This is part 2 of a series of posts regarding developing modular Java web applications with OSGi. Please go through the part 1 before proceeding with this post, as we are reusing the sample project.

Additional details regarding advantages of using OSGi, as well as modular design, is available in WhyOSGi page from OSGi Alliance web site.

Part 2 – Introduction

With part 2 of the series we will be reusing the project from part 1 and enhancing same to dynamically integrate and disintegrate web resources to the base application. At the completion of part 2, Servlets as well as static resources defined within the module package will be integrated dynamically whenever a module starts up and relevant resources will be dynamically disintegrated without any downtime when the module stops.


Getting Source Code

Checkout the sample source code (from tag “Part2″) from GitHub using below commands :

git clone Blog_OSGi

cd Blog_OSGi

git checkout tags/Part2


Repeating from part 1 : It is possible to use a text editor to view the source code explained below, but if you prefer Eclipse IDE, you may create a workspace in “Blog_OSGi” directory that you cloned the git repository and import all the projects using “Import Existing Maven Project” option. You might have to install plugin connectors required (which will be automatically handled by IDE) and if your IDE version does not have plugin connectors for maven-bundle-plugin you will have to mark it ignore as suggested by the IDE.


Walk through 

“com.ayomaonline.osgi.main.api.Module” interface has been modified t include following “getModuleId()” method, ” getServletBindings()” method and “getResourceBindings()” method.

Considering “com.ayomaonline.osgi.modules.user.UserModule” class implementation was done as shown below :

public String getModuleId() {
  return "user";

public Map<String, Servlet> getServletBindings() {
  Map<String, Servlet> bindings = new HashMap<String, Servlet>();

  bindings.put("add", new Add());
  bindings.put("manage", new Manage());

  return bindings;

public Map<String, String> getResourceBindings() {
  Map<String, String> bindings = new HashMap<String, String>();

  return bindings;


In the implementation of “getServletBindings()” method, you will notice that “add” URL (path) has been mapped to the servlet named “com.ayomaonline.osgi.modules.user.servlet.Add” and “manage” path has been mapped to “com.ayomaonline.osgi.modules.user.servlet.Manage” servlet.

” com.ayomaonline.osgi.main.tracker.DefaultModuleTrackerCustomizer” from Part 1 has been modified to register Servlet exposed by each module using “getServletBindings()” method and also static resources exposed by
“getREsourceBindings()” method using below segment :


List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
Map<String, String> resourceBindings = service.getResourceBindings();
for (String path : resourceBindings.keySet()) {
  String adjustedPath = "/osgi/" + service.getModuleId() + "/" + path;
  Activator.getApplicationLogger().log(LogService.LOG_DEBUG, "Registering : " + adjustedPath + " / " + resourceBindings.get(path));

  DefaultResourceMapping resourceMapping = new DefaultResourceMapping();

  ServiceRegistration registration = context.registerService(ResourceMapping.class.getName(), resourceMapping, null);

Map<String, Servlet> servletBindings = service.getServletBindings();
for (String path : servletBindings.keySet()) {
  String adjustedPath = "/osgi/" + service.getModuleId() + "/" + path;
  Activator.getApplicationLogger().log(LogService.LOG_DEBUG, "Registering : " + adjustedPath + " / " + servletBindings.get(path));

  Dictionary<String, String> props = new Hashtable<String, String>();
  props.put("alias", adjustedPath);

  ServiceRegistration registration = context.registerService(Servlet.class.getName(), servletBindings.get(path), props);

httpServiceRegistrations.put(service.getClass().getName(), registrations);


Above code uses Pax Web Extender – Whiteboard feature to wire Servlets and web resources as OSGi services. Pax Web Extender take care of mapping URL patterns with OSGi HttpService and handling additional complexities of managing HttpService.

Unregistering resources is done in the “removedService” section, so that the main application release web resources bound, whenever the module goes offline.


for (ServiceRegistration registration : httpServiceRegistrations.get(service.getClass().getName())) {
  Activator.getApplicationLogger().log(Logger.DEBUG, "UnRegistering : " + registration);


Start the application with Pax Runner using below command :

cd com.ayomaonline.osgi.parent

mvn clean install pax:run


Navigate to “”.


You will notice that when you click on each available link, relevant servlet will be executed.


Please proceed with “lb” command and stopping a module using “stop <bundle_id>” command. You will notice that the menu has been dynamically adjusted.


If you try to access a removed resource by manually entering the URL you will get a 404 error, because resources have been unregistered upon module stop.



2 comments on “Modular Web Applications with OSGi – Part 2 – Dynamic Web Resources

  1. sekaijin October 24, 2016 1:44 PM

    I added String contextPath = context.getBundle().getHeaders().get(“Web-ContextPath”).toString(); in to recover the webapp context.

    But there is one thing I do not understand.
    How to recover a static resource in a module?
    add the ModuleAdder
    DefaultResourceMapping resourceMapping DefaultResourceMapping = new ();
    resourceMapping.setAlias (adjustedPath);
    resourceMapping.setPath (adjustedPath);
    but by default the server will look for the resource in the war and not in the module


Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">