logo.pngOSGi has a service spec called http.service (see Service Compendium document of the OSGi Alliance). Felix has an implementation for it that is based around jetty 4.x. Since we are at jetty 6.1.7 at the moment I thought about trying to create an implementation of my own. Not that it is really necessary, you can expose resources without complying to the spec (see my other post).

But the spec is there for a reason, so let’s try to adhere to it first. The specification is up to the following two interfaces and one exception.

  • HttpContext – Enables bundles to use provided information about a servlet or resource during registration.
  • HttpService – Enables other bundles to dynamically register sevlets or resources into the Http Service URI namespace.
  • NamespaceException – Thrown when a problem arises during registration of a servlet or resource into the Http Service UIR namespace.

At first I tried to create my own implementation for these interfaces. It did not look to hard, but in the end I found the following patch which made my life a lot easier.
jetty6 patch

jetty_logo.gifTo be able to understand the implementation of an Http Service using jetty, you should understand the basics with respect to Jetty. In a forthcoming post I’ll talk more about these details. Within this post I’ll concentrate on the using of the service. I do need to make a few remarks. What if you do not want to apply the patch, change the pom, etc. Well you can download the one I have created from here : org.apache.felix.http.jetty-0.9.0-GRIDSHORE.jar. Another thing I would like to stress is the current state of the bundle. There are a lot of TODO’d in there. It looks like security is not implemented as it should. There is a completely different implementation at the ops4j website. One disadvantage is the level of control, need to look into that thing again. It is much more complicated, uses other bundles of the pax project. It does look interesting, but for me it is not easy enough. Another implementation is available at the sling website. But again a lot to graps before you can start. So for now I have chosen to continue with the slightly limited implementation, adhere to the spec and my bundles should work with the others as well.

Read more to learn about the sample using http.service and maven to build the bundle.

The sample

The goal was to play around with servlets and resources. I want to get a better idea about what OSGi can do. So I used the runner I introduced in my previous post (starting-with-osgi-using-apache-felix-step-1). Of course there are some other bundles and there is an extra property to configure the jetty intance:

configMap.put("org.osgi.service.http.port","9081"); // set the port to listen to for jetty
configMap.put(AutoActivator.AUTO_START_PROP + ".1",
    "file:" + BUNDLE_ROOT + "training-service/1.0-SNAPSHOT/training-service-1.0-SNAPSHOT.jar " +
    "file:" + BUNDLE_ROOT + "http-servlets/1.0-SNAPSHOT/http-servlets-1.0-SNAPSHOT.jar " +
    "file:" + JETTY_ROOT + "jetty/6.1.7/jetty-6.1.7.jar " +
    "file:" + JETTY_ROOT + "jetty-util/6.1.7/jetty-util-6.1.7.jar " +
    "file:" + JETTY_ROOT + "servlet-api-2.5/6.1.7/servlet-api-2.5-6.1.7.jar " +
    "file:bundle/org.osgi.compendium-1.0.0.jar " +
    "file:bundle/slf4j-api-1.4.3.jar " +
    "file:bundle/slf4j-simple-1.4.3.jar " +
    "file:bundle/org.apache.felix.http.jetty-0.9.0-GRIDSHORE.jar " + // use the created patched http.service
    "file:bundle/org.apache.felix.shell-1.0.0.jar " +
    "file:bundle/org.apache.felix.shell.tui-1.0.0.jar ");

The training service is a very easy service with one method returning a list of trainings. The more interesting bundle is the http-servlets bundle. This bundle contains the servlet that uses the training-service and some static resources like html pages, style sheets, etc. Let’s start with a look at the internal structure of the bundle. Please remember that we use the maven-bundle-plugin plugin. Therefore we adhere to basic maven project structure. The java files are in the src/main/java folder. The static resources are in the src/main/resources folder. The plugin than creates the right MANIFEST.MF file.

Manifest-Version: 1.0
Built-By: jettro
Created-By: Apache Maven Bundle Plugin
Bundle-Activator: nl.gridshore.samples.bundles.httpservlets.impl.Activ
 ator
Import-Package: javax.servlet;version="2.5",javax.servlet.http;version
 ="2.5",nl.gridshore.samples.bundles.trainingservice.api,org.osgi.fram
 ework;version="1.3",org.osgi.service.http;version="1.2",org.slf4j;ver
 sion="1.4"
Bnd-LastModified: 1204315796232
Bundle-Version: 1.0.0.SNAPSHOT
Bundle-Name: http-servlets
Build-Jdk: 1.5.0_13
Private-Package: htmls,htmls.css,htmls.images,nl.gridshore.samples.bun
 dles.httpservlets.impl
Bundle-ManifestVersion: 2
Bundle-SymbolicName: http-servlets
Tool: Bnd-0.0.227

Take good notice of the Private-Packge, here the directories are listed that are in the resources folder. So by default these resources are private. We need the http service to expose these resources. The next step is to have a look at the Activator of the http-servlets bundle. Let’s step through the separate methods that are important.

public void start(BundleContext bundleContext) throws Exception {
    this.bundleContext = bundleContext;
    doRegister();
    synchronized (this) {
        bundleContext.addServiceListener(this,
                "(|(objectClass=" + TrainingService.class.getName() + ")" +
                "(objectClass=" + HttpService.class.getName() + "))");
    }
}

Two important things happen in the start method, we first try to register the servlet (more on that later). After that we add a service listener that listens to events related to the TrainingService class and the HttpService class. This means that if one of these instances are started, stopped or updated we are notified. This notification is important to unregister the servlet if the training service disappears. Of course it is also important to register the servlet if the http service becomes available. Let’s move on to the register methods.

private void register() throws InvalidSyntaxException, ServletException, NamespaceException {
    ServiceReference[] trainingReferences = bundleContext.getServiceReferences(TrainingService.class.getName(), null);
    TrainingService trainingService = null;
    if (trainingReferences != null) {
        trainingService = (TrainingService) bundleContext.getService(trainingReferences[0]);
    } else {
        logger.info("No training service available");
    }

    ServiceReference[] httpReferences = bundleContext.getServiceReferences(HttpService.class.getName(), null);
    HttpService httpService = null;
    if (httpReferences != null) {
        httpService = (HttpService) bundleContext.getService(httpReferences[0]);
    } else {
        logger.info("No http service available");
    }

    if ((trainingService != null) && (httpService != null)) {
        logger.info("training servlet will be registered.");
        httpService.registerServlet("/trainings", new TrainingsServlet(trainingService), null, null);
        httpService.registerResources("/","/htmls",null);
    } else {
        logger.info("No servlet to register, problem with training service or http service");
    }
}

private void doRegister() {
    try {
        register();
    } catch (InvalidSyntaxException e) {
        logger.error("Could not register servlet based on an Invalid Syntax",e);
    } catch (ServletException e) {
        logger.error("Could not register servlet based on an Servlet exception",e);
    } catch (NamespaceException e) {
        logger.error("Could not register servlet based on an Namespace exception",e);
    }
}

Concentrate on the register method. First we check for available training service references. If a reference is available, we get the actual service. Then the same is done for the http service. If both of them are availble, we use the http service to register a new servlet that accepts the training service to obtain trainings from. As you can see we first register the servlet, the two null parameters represent the servlet config parameters and the special HttpContext. By providing null a default context is created. We also register a folder with static resources. The first string is the path or alias where the user should access the resources. The second string is the path to the resources within the bundle. The final parameter is the same as for the servlet, the HttpContext, again null so the default value. There are to more methods I want to show, first for unregistering and second for handling change events.

private void unregister() throws InvalidSyntaxException {
    logger.info("Unregister a servlet");
    ServiceReference[] httpReferences = bundleContext.getServiceReferences(HttpService.class.getName(), null);
    if (httpReferences != null) {
        HttpService httpService = (HttpService) bundleContext.getService(httpReferences[0]);
        httpService.unregister("/trainings");
        httpService.unregister("/");
    }
}

private void doUnregister() {
    try {
        unregister();
    } catch (InvalidSyntaxException e) {
        logger.error("Could not unregister servlet",e);
    }
}

public void serviceChanged(ServiceEvent event) {
    String objectClass = ((String[]) event.getServiceReference().getProperty("objectClass"))[0];
    logger.info("Service change event occurred for : {}", objectClass );
    if (event.getType() == ServiceEvent.REGISTERED) {
        doRegister();
    } else if (event.getType() == ServiceEvent.UNREGISTERING) {
        doUnregister();
    } else if (event.getType() == ServiceEvent.MODIFIED) {
        doUnregister();
        doRegister();
    }
}

The unregister function is pretty obvious. So let’s have a better look at the serviceChanged method. I took the easy way out here. Just check the type of event. In case of a new registration, register the servlet and resources. In case of an unregistration, we unregister the servlet and in case of a change we just do an unregister and a register. This works oke for my situation. Using the following screendumps I’ll show what happens when we start all bundles, stop de training-service bundle, and in the end start it again. First the output from the console, then the screens.

Welcome to Felix.
=================

1 [FelixStartLevel] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - No http service available
1 [FelixStartLevel] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - No servlet to register, problem with training service or http service
org.mortbay.log:Logging to org.mortbay.log via org.apache.felix.http.jetty.LogServiceLog
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionIdManager@2b2057
org.mortbay.log:started org.mortbay.jetty.servlet.HashSessionManager@85c0de
org.mortbay.log:starting OsgiServletHandler@395aaf
org.mortbay.log:started OsgiServletHandler@395aaf
org.mortbay.log:starting SessionHandler@70b819
org.mortbay.log:started SessionHandler@70b819
org.mortbay.log:starting org.mortbay.jetty.servlet.Context@1b50a1{/,null}
org.mortbay.log:starting ErrorHandler@46ad8b
org.mortbay.log:started ErrorHandler@46ad8b
org.mortbay.log:started org.mortbay.jetty.servlet.Context@1b50a1{/,null}
org.mortbay.log:jetty-6.1.x
org.mortbay.log:started Realm[OSGi HTTP Service Realm]==[]
org.mortbay.log:started org.mortbay.thread.BoundedThreadPool@ebaf12
org.mortbay.log:starting Server@32e910
org.mortbay.log:started org.mortbay.jetty.nio.SelectChannelConnector$1@19549e
org.mortbay.log:Started SelectChannelConnector@0.0.0.0:9081
org.mortbay.log:started SelectChannelConnector@0.0.0.0:9081
org.mortbay.log:started Server@32e910
191 [FelixStartLevel] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - Service change event occurred for : org.osgi.service.http.HttpService
191 [FelixStartLevel] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - training servlet will be registered.
org.mortbay.log:started /trainings/*
org.mortbay.log:started /htmls
-> stop 1
36499 [Felix Shell TUI] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - Service change event occurred for : nl.gridshore.samples.bundles.trainingservice.api.TrainingService
36499 [Felix Shell TUI] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - Unregister a servlet
-> start 1
org.mortbay.log:started /trainings/*
org.mortbay.log:started /htmls
43500 [Felix Shell TUI] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - Service change event occurred for : nl.gridshore.samples.bundles.trainingservice.api.TrainingService
43500 [Felix Shell TUI] INFO nl.gridshore.samples.bundles.httpservlets.impl.Activator - training servlet will be registered.
-> 

Try to follow the steps, you can see the bundle containing the servlet starts before the training-service of the http-service. Then the change event is received and the servlet is registered. Next we stop the training service and the servlet is unregistered. And the servlet is registered again after a start of the training-service bundle (number 1).

felix-osgi-training-step1.jpgThis is the initial screen. Notice the url, this is a static resource called index.html that loads a stylesheet and an image from the bundle.

felix-osgi-training-step2.jpgNow we have stopped the training service bundle, which has resulted in a unregister of the servlet, therefore the page is not available anymore.

felix-osgi-training-step3.jpgI have started the training service bundle again, so the servlet gets registered and now I have clicked the link for trainings. As you can see we have two very interesting trainings.

So that is about it, it turned out to become a pretty long post. Hope you liked it. Please leave a comment if you did, and of course if you did not. All your ideas and suggestions are welcome. If you want to have a look at the source, you can find everyting in the FelixTryout project at my google code website.

Tagged on:             

8 thoughts on “Creating a jetty based OSGi HttpService for apache felix

  • January 5, 2009 at 9:07 pm
    Permalink

    Thanks for your input. Right now Felix is what we have to work with. So that’s why I’m looking at the jetty server. I’m not using hibernate, just a service that has the state data which needs to be displayed on the GUI. I’m just having trouble with the context handlers and servlets and getting everything connected, and your example is the only one I can find. I start Felix up a little differently, using the config.properities file with felix.jar so thats part where the gap is.

  • December 11, 2008 at 8:35 am
    Permalink

    Not sure if I understand the question completely. Do you want to update controls on your gui? Or do you want to change data using hibernate for instance. Both options can be made available. There are however some substantial buts. With Hibernate there are still some class loading tricks and data sharing things to consider. If you can update the gui using remote services this should be possible. Personally I see this post as a more theoretical case. I’d urge you to have a look at the more advanced projects that have modules available that you can use in for instance glassfish, spring dm, or other servers. Not that felix is not nice, just this combination of jetty and felix is not going to be the future. Check one of the previous comments about pax for more information

  • December 11, 2008 at 1:35 am
    Permalink

    Thanks for posting this helped. I’m new at all this, and I’m trying to update controls on a gui via Jetty Web server based on information thats being passed around the osgi/felix services. Is that possible? I’m trying to 1 – integrate jetty with felix, which your post is helping on. and 2 – I have a GUI using java web start running on Jetty and I want to be able to update status on it. Everything I’ve seen so far thats in osgi seems to still be pretty standalone, meaning there is no data thats being actively updated. Do you have any suggestions on how to do this, or where I can find some more information and examples on it?

    Thanks!!

    • February 11, 2012 at 5:32 am
      Permalink

      Hi,I am Gautham, the proesn behind this project.We are working on the Linux version of Eclifox and intend to support it soon.Regarding Jetty being available in Eclipse, we intended to use the internal Jetty libraries atleast for 3.3. But there was no straight forward way to use the Jetty server.Is there some reference on how the internal Jetty plug-in can be used from within another plug-in in Eclipse?Thanks,Gautham.

  • Pingback: www.jillesvangurp.com : server side osgi, a myth?

  • July 16, 2008 at 11:02 am
    Permalink

    You are right that it is hard to configure the jetty server with listeners and filters. That is because the spec is not a complete war implementation. The spec just specifies a very basic HttpService implementation. Therefore at springsource they have implemented something different for web projects. There are other solutions available if you browse the web. We plan on writing more on this the coming period, but time is my enemy at the moment.

    As for the sources, since the sources are already available, as well as the patch, i’d encourage you to download the sources as well as the patch. I am not planning on providing the patches sources.

    Sorry I cannot help you further, hope to write more about this in the near future.

  • July 16, 2008 at 10:35 am
    Permalink

    Hi,

    Thanks for the org.apache.felix.http.jetty-0.9.0-GRIDSHORE.jar it is very helpful but what I am missing is the possibility to configure the jetty server. Some kind of listener control, filter configuration and so on. Have I something misunderstand? or it is really not possible to configure it. I checked also the svn http://gridshore.googlecode.com/svn/trunk with the idea to find the sources to yours felix.http.jetty bundle to perform the changes that I need but I found nothing. Am I looking on the right place?

    One more time thanks.

    Best regards

    Roman Janos

Comments are closed.