Accueil > Equinox ServletBridge > OSGi Equinox in a Servlet Container [step2]

OSGi Equinox in a Servlet Container [step2]


In [step1], I have explained how use Equinox in a Servlet Container with the provided bridge.war and show the sample sample.http in action with WTP Dynamic Web project.

In this article, I will explain :

  • how bridge WEB application works : explains the content of the web.xml which declare the servlet BridgeServlet which is used to startup/shutdown the OSGi container and retrieve the well Servlet from the OSGi services registry according to the request URI path (ex : /hellowrd).
  • how sample.http works. To explains that, we will create from scratch with PDE Tools a new bundle sample.http2 which will provide a new Servlet with path /helloworld2.
  • how add OSGi Web console by using OSGi Felix Webconsole.

We will see several problems with OSGi Equinox in a Servlet Container that we will fix in the next article [step3].

Download

You can download servletbridge_step2.zip which contains following explained projects :

  • brige which is a « Dynamic Web Project » of the the original bridge.war. It contains too the JAR sample.http2 and Felix OSGi Webconsole.
  • sample.http : eclipse bundle OSGi project of the original sample.http.
  • sample.http2 : eclipse bundle OSGi project that we will create.
  • Servers which contains the configuration of the Tomcat 6.0 which was generated with WTP.

bridge

The bridge WEB Application is used to host some bundles and use Servlet with OSGi HTTP services.

Architecture

Here a sheme which explains how bridge WEB application works with BridgeServlet :

When bridge WEB Application is started with HTTP Server (in our case Tomcat), the BridgeServlet is created and BridgeServlet#init() is called. The sheme show you several steps :

  • (1) init() : BridgeServlet#init() is called by HTTP server when server start the bridge WEB application. In this method, the Equinox OSGi container (coming from the /plugins folder) is started by calling org.eclipse.core.runtime.adaptor.EclipseStarter#startup().
    • the OSGi container start the whole OSGi bundles coming from the /plugins folder.
    • (1.1) the sample.http start and register an HelloWorldServlet in the OSGi services registry. More exactly it use HttpService coming from the OSGi services registry to register the servlet. The registration is done width an another servlet called ProxyServlet. When sample.http is stopped with OSGi console, it unregister the HelloWorldServlet.
  • (2.1) service : when BridgeServlet is called with HTTP request /helloworld (Bridge#service(HttpServletRequest req, HttpServletResponse resp)) :
    • (2.2) it search a Servlet registered with /helloworld path from the OSGi registry services. More exactly it use ProxyServlet.
    • (2.3) it returns the Servlet founded in our case HelloWorldServlet from the sample.http is founded.
    • (2.4) it calls HelloWorldServlet#service(HttpServletRequest req, HttpServletResponse resp)) and HTTP server returns the HTTP response to the client.
  • (3) destroy() : BridgeServlet#destroy() is called by HTTP server when server stop the bridge WEB application. In this method, the Equinox OSGi container is stopped by calling org.eclipse.core.runtime.adaptor.EclipseStarter#shutdown().

web.xml

The web.xml of the bridge WEB Application declare the BridgeServlet – servlet » which is used to manage OSGi Equinox in a Servlet Container. This servlet is used for several things :

  • to start/stop OSGi container and dispatch to the well Servlet registered in the OSGi services registry
  • process the whole commands /sp_* to control the Framework.

BridgeServlet – servlet

The BridgeServlet is declared with servlet XML element like this :

<servlet id="bridge">
  <servlet-name>equinoxbridgeservlet</servlet-name>
  <display-name>Equinox Bridge Servlet</display-name>
  <description>Equinox Bridge Servlet</description>
  <servlet-class>org.eclipse.equinox.servletbridge.BridgeServlet</servlet-class>
</servlet>

The servlet-mapping map /* URL :

<servlet-mapping>
  <servlet-name>equinoxbridgeservlet</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

<!-- This is required if your application bundles expose JSPs. -->	
<servlet-mapping>
  <servlet-name>equinoxbridgeservlet</servlet-name>
  <url-pattern>*.jsp</url-pattern>
</servlet-mapping>

You can notice that with this mapping, every HTTP request are processed by the BridgeServlet. It cause the problem that it’s impossible to host some resources (Javascript, HTML, Servlet, JSP…) in the bridge WEB Application.

BridgeServlet – init-param

The BridgeServlet can be configured width XML init-param. The web.xml declare the following init-param (inside the servlet declaration) :

commandline

The commandline init-param is used to configure the Equinox OSGi container and it is declared like this :

<init-param>
  <param-name>commandline</param-name>
  <param-value>-console</param-value>			
</init-param>

The -console means that OSGi console must be launched when bridge is started. For more informations about the Equinox OSGi container configuration, please read The Eclipse runtime options.

enableFrameworkControls

The enableFrameworkControls init-param is setted like this :

<init-param>
	<param-name>enableFrameworkControls</param-name>
	<param-value>true</param-value>			
</init-param>

It means that /sp_* commands are available.

There are another init-param like extendedFrameworkExports (that I will explain in the [step6]) and frameworkLauncherClass (that I have never used).

sample.http2

In this section we will create a new bundle called sample.http2 which will register a servlet with path /helloworld2 like sample.http. The code of teh
sample.http2 will be (almost) the same than sample.http.

Create Bundle

Here we create the bundle sample.http with PDE (Plug-in Development Environment). To do that go at menu File/New/Other and Plug-in Development/Plug-in Project :

Click on Next button, and :

  • fill Project name field with sample.http2.
  • select the radio button an OSGI framework because we wish develop an OSGi bundle and select the combo standard.

Click on Next button, the wizard page which configure the OSGi bundle is displayed :

  • ID field is the Bundle identifier (Bundle-SymbolicName: sample.http2).
  • Version field is the Bundle version (Bundle-Version: 1.0.0.qualifier).
  • Name field is the Bundle name (Bundle-Name: Sample Http 2).
  • Execution Environment set the minimal version of the JRE in order to the bundle can be executed (Bundle-RequiredExecutionEnvironment: J2SE-1.5).
  • generate an activator, a Java class that controls the plug-in’s life cycle option must be selected to generate a Bundle Activator

Click on Finish button to generate the OSGi bundle project.

Your workspace looks like this :

The sample sample.http2 is generated. It contains :

  • A META-INF/MANIFEST.MF with contains OSGi bundles meta-data :
    Manifest-Version: 1.0
    Bundle-ManifestVersion: 2
    Bundle-Name: Sample Http 2
    Bundle-SymbolicName: sample.http2
    Bundle-Version: 1.0.0.qualifier
    Bundle-Activator: sample.http2.Activator
    Import-Package: org.osgi.framework;version="1.3.0"
    Bundle-RequiredExecutionEnvironment: J2SE-1.5
  • An Java class sample.http2.Activator with this content :
    package sample.http2;
    
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    
    public class Activator implements BundleActivator {
    
    	private static BundleContext context;
    
    	static BundleContext getContext() {
    		return context;
    	}
    
    	public void start(BundleContext bundleContext) throws Exception {
    		Activator.context = bundleContext;
    	}
    
    	public void stop(BundleContext bundleContext) throws Exception {
    		Activator.context = null;
    	}
    
    }

When the OSGi bundle sample.http2 will be :

  • started with OSGi console, the Activator#start(BundleContext bundleContext) will be called.
  • stopped with OSGi console, the Activator#stop(BundleContext bundleContext) will be called.

The Activator class implements org.osgi.framework.BundleActivator and is referenced in the META-INF/MANIFEST.MF like this :

...
Bundle-Activator: sample.http2.Activator

Create Servlet

Now we must modify the Activator to :

  • register a Servlet on Activator#start in the OSGi services registry.
  • unregister the Servlet on Activator#stop in the OSGi services registry.

HelloWorldServlet

Before managing OSGi Servlet with OSGi registry, we create a basic Servlet HelloWorldServlet in the sample.http2 project like this :

package sample.http2;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloWorldServlet extends HttpServlet {

	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html");
		resp.getWriter().write("<html><body>Hello World 2-- sample 2 servlet</body></html>"); //$NON-NLS-1$
	}

}

Import package javax.servlet

You will have a compilation problem with javax.servlet package. To fix the problem we must import package javax.servlet. To do that, open the META-INF/MANIFEST.MF and go at the Dependencies tab :

Click on Add… button of the list Imported packages list. The Package Selection dialog opens :

Type javax.servlet in the field and click on OK button :

Save the editor and go to the MANIFEST.MF tab. The MANIFEST.MF has changed by adding javax.servlet :

Import-Package: javax.servlet;version="2.5.0",
 org.osgi.framework;version="1.3.0"

You can notice that there is version= »2.5.0″ which means the bundle will works only with OSGi bundles which provides javax.servlet with Servlet 2.5. In our case our servlet is very simple, so we can remove this information :

Import-Package: javax.servlet,
 org.osgi.framework;version="1.3.0"

Import package javax.servlet.http

You will have the same compilation, problem with javax.servlet.http, import the package javax.servlet.http and remove the version. Here the MANIFEST.MF :

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Sample Http 2
Bundle-SymbolicName: sample.http2
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: sample.http2.Activator
Import-Package: javax.servlet,
 javax.servlet.http,
 org.osgi.framework;version="1.3.0"
Bundle-RequiredExecutionEnvironment: J2SE-1.5

Register the servlet as an OSGi service

Now we can register our HelloWorldServlet in the OSGi services registry. The OSGi services registry is used to publish/retrieve some services. Some bundles publish X services and some bundles retrieve and consume the X services. In our case we must retrieve from the OSGi service sregistry, the HTTP service org.osgi.service.http.HttpService which is an interface :

package org.osgi.service.http;

import java.util.Dictionary;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

/**
 * The Http Service allows other bundles in the OSGi environment to dynamically
 * register resources and servlets into the URI namespace of Http Service. A
 * bundle may later unregister its resources or servlets.
 * 
 */
public interface HttpService {

	public void registerServlet(String alias, Servlet servlet,
			Dictionary initparams, HttpContext context)
			throws ServletException, NamespaceException;

	public void registerResources(String alias, String name,
			HttpContext context) throws NamespaceException;

	public void unregister(String alias);

	public HttpContext createDefaultHttpContext();
}

Once we will have this service we can:

  • call HttpService#registerServlet on our Activator#start to register the servlet HelloWorldServlet.
  • call HttpService#unregister on our Activator#stop to unregister the servlet HelloWorldServlet.

HttpServiceTracker

To retrieve a service from the OSGi services registry, we can use the org.osgi.util.tracker.ServiceTracker which is used to track and retrieve a service from the OSGi services registry. Create the class sample.http2.HttpServiceTracker like this :

package sample.http2;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpService;
import org.osgi.util.tracker.ServiceTracker;

public class HttpServiceTracker extends ServiceTracker {

	public HttpServiceTracker(BundleContext context) {
		super(context, HttpService.class.getName(), null);
	}

	public Object addingService(ServiceReference reference) {
		HttpService httpService = (HttpService) context.getService(reference);
		try {			
			httpService.registerServlet("/helloworld2", new HelloWorldServlet(), null, null); //$NON-NLS-1$
		} catch (Exception e) {
			e.printStackTrace();
		}
		return httpService;
	}		
	
	public void removedService(ServiceReference reference, Object service) {
		HttpService httpService = (HttpService) service;
		httpService.unregister("/helloworld2"); //$NON-NLS-1$
		super.removedService(reference, service);
	}

}

The HttpServiceTracker constructor (which extends ServiceTracker), set the required BundleContext and the name of the service that we wish retrieve (in our case HttpService).

public HttpServiceTracker(BundleContext context) {
	super(context, HttpService.class.getName(), null);
}
Register servlet

When HttpServiceTracker will be opened by calling :

ServiceTracker#open();

it will track in the OSGi services registry the publish of the HttpService. When HttpService is published, the method

ServiceTracker#addingService(ServiceReference reference) ;

is called. So in this method, we can get the service HttpService and register our HelloWorldServlet width path /helloworld2 :

public Object addingService(ServiceReference reference) {
  HttpService httpService = (HttpService) context.getService(reference);
  try {			
    httpService.registerServlet("/helloworld2", new HelloWorldServlet(), null, null); //$NON-NLS-1$
  } catch (Exception e) {
    e.printStackTrace();
  }
  return httpService;
}
Unregister servlet

When HttpServiceTracker will be closed by calling :

ServiceTracker#close();

it will call the method :

ServiceTracker#removedService(ServiceReference reference, Object service);

So in this method, we can get the service HttpService and unregister our HelloWorldServlet width path /helloworld2 :

public void removedService(ServiceReference reference, Object service) {
  HttpService httpService = (HttpService) service;
  httpService.unregister("/helloworld2"); //$NON-NLS-1$
  super.removedService(reference, service);
}
Import package org.osgi.service.http + org.osgi.util.tracker

At this step you will have compilation problem. To fix that, import the packages org.osgi.service.http and org.osgi.util.tracker :

 org.osgi.service.http;version="1.2.1",
 org.osgi.util.tracker;version="1.4.0"

Keep the bundles version generated, there is a problem with this declaration. We will see the problem at Import Packages Problem section and how fix it.

Activator

Here we can modify the Activator to call our HttpServiceTracker. Modify the Activator class like this :

package sample.http2;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

public class Activator implements BundleActivator {

	private static BundleContext context;
	private ServiceTracker httpServiceTracker;

	static BundleContext getContext() {
		return context;
	}

	public void start(BundleContext bundleContext) throws Exception {
		Activator.context = bundleContext;
		
		httpServiceTracker = new HttpServiceTracker(context);
		httpServiceTracker.open();
	}

	public void stop(BundleContext bundleContext) throws Exception {
		Activator.context = null;
		
		if (httpServiceTracker != null) {
			httpServiceTracker.close();
		}
		httpServiceTracker = null;
	}

}

Install sample.http2

  • create and deploy the sample.http2_*.jar You will notice that the JAR generated use the datetime in the JAR name (ex : sample.http2_1.0.0.201009011521.jar). This behaviour is explained with the .qualifier in the MANIFEST.MF which declare :
    Bundle-Version: 1.0.0.qualifier

    Remove the .qualifier :

    Bundle-Version: 1.0.0

    Now if you create and deploy, you will have every time the sample.http2_1.0.0.jar.

  • modify the config.ini to add sample.http2@start
  • Go at http://localhost:8080/bridge/sp_redeploy to redeploy the JAR.
  • restart the Tomcat server

Import packages Problem

Restart your server and type in the console View

ss

The OSGi console display the Short Status of the bundles installed :


...
15 INSTALLED sample.http2

sample.http2 is INSTALLED but not RESOLVED, which means that there is a problem. Try to start the bundle (in my case with command start 15) and you will see the error :

osgi> start 15
org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Missing Constraint: Import-Package: org.osgi.service.http; version="1.2.1"
...

It means that org.osgi.service.http packages are not retrieved with version « 1.2.1 ». It’s possible to display available packages with OSGi console. To do that type :

packages org.osgi.service.http

You will see on the console :

osgi> packages org.osgi.service.http
org.osgi.service.http; version="1.2.0"
initial@reference:file:plugins/org.eclipse.equinox.http.servletbridge_1.0.0.200704022148.jar/ [3] imports
initial@reference:file:plugins/org.eclipse.equinox.http.registry_1.0.0.200704022148.jar/ [4] imports
initial@reference:file:plugins/sample.http_1.0.0.jar/ [5] imports
update@plugins/org.eclipse.equinox.http.servlet_1.0.0.200704022148.jar [9] imports
update@plugins/org.eclipse.osgi.services_3.1.100.200704022148.jar [12] imports

You will see that package exported is 1.2.0 and NOT 1.2.1. There is the same problem with org.osgi.util.tracker package. To fix the problem :

  • remove the versions of the MANIFEST.MF
  • OR change it your MANIFEST.MF with well version like this :
    Manifest-Version: 1.0
    Bundle-ManifestVersion: 2
    Bundle-Name: Sample Http 2
    Bundle-SymbolicName: sample.http2
    Bundle-Version: 1.0.0
    Bundle-Activator: sample.http2.Activator
    Import-Package: javax.servlet,
     javax.servlet.http,
     org.osgi.framework;version="1.3.0",
     org.osgi.service.http;version="1.2.0",
     org.osgi.util.tracker;version="1.3.3"
    Bundle-RequiredExecutionEnvironment: J2SE-1.5

To understand the problem, see the Plug-in dependencies of the sample.http2 :

You will notice that it use the JAR org.eclipse.osgi.services_3.2*.jar from the Eclipse IDE and not the org.eclipse.osgi_3.3.0.200704022148.jar from the WEB-INF/eclipse/plugins. We will in the next article [step3] how we can configure Eclipse to use bundles from the WEB-INF/eclipse/plugins and not from the Eclipse IDE.

Retry

If you go at http://localhost:8080/bridge/helloworld2 you will see :

You can stop to check that the servlet will not available.

OSGi Felix Webconsole

The Apache Felix Web Console is a simple tool to inspect and manage OSGi framework instances using your favourite Web Browser. In this section we will install to show you how it’s easy to install another bundles and more have I think it’s a cool feature to have OSGi Webconsole.

To do that :

Type in the console View

ss

The OSGi console display the Short Status of the bundles installed :

...
7 RESOLVED org.apache.felix.webconsole_3.1.2

Start the Felix Webconsole with the command :

start 7

Go at http://localhost:8080/bridge/system/console/ to display the OSGi console. This Webconsole is securized. A prompt dialog « OSGi Management Console » will display. Fill username/password like this :

Name Value
username admin
password admin

The OSGi Webconsole must be displayed :

Conclusion

In this article I have explained (a little) how bridge and sample.http works. I have introduced several problems with using « OSGi Equinox in a Servlet Container » :

  • When bundle (ex : sample.http2) is developped, it use JAR coming from the Eclipse IDE and not from the WEB-INF/plugins/eclipse folder. You can have some problems when bundle is deployed ( with import packages was a sample). We will see in the next article [step3] how we can fix this problem with Target Platform.
  • It may that /sp_redeploy doesn’t works (very rarely) with WTP Web Dynamic Project. In the next article [step3], I will explain more how WTP deploy the WEB application.
  • It’s impossible to use directly the OSGi bundle coming from the workspace. You must create a JAR and copy/paste to the /plugins folder. I will explain how fix this problem in [step4] with patch 323707.
  • It’s impossible to host some resources (HTML, Javascript, Servlet…) in the bridge WEB Application (when /* URL pattern is used). I will explain how fix this problem in [step4] with patch 323707.

In the [step3] article I will show you how we can fix those 2 first problems.

Publicité
Catégories :Equinox ServletBridge
  1. septembre 8, 2010 à 7:07

    great article. Keep up the good work 😉

  2. octobre 30, 2011 à 1:19

    Hi,

    How do you get OGSI console when you deploy your OSGI based web applications inside, let’s say Weblogic or Websphere ?

    Thanks & Regards,

    Setya

    • octobre 30, 2011 à 5:13

      Hi,

      I don’t know. Never tried that.

      Regards Angelo

  3. Jean
    février 21, 2013 à 8:00

    Hi, i’m following your article but i get the message: servlet delegate no registred.

    My bundles:
    0 ACTIVE org.eclipse.osgi_3.3.0.200704022148
    1 ACTIVE org.eclipse.equinox.common_3.3.0.200704022148
    4 ACTIVE org.eclipse.equinox.registry_3.3.0.v20070318
    5 ACTIVE org.glassfish.javax.servlet_3.0.0.SNAPSHOT
    6 ACTIVE org.eclipse.osgi.services_3.1.100.200704022148
    7 ACTIVE org.eclipse.equinox.http.registry_1.1.100.v20110502
    8 ACTIVE org.eclipse.equinox.http.servlet_1.1.200.v20110502
    9 ACTIVE org.eclipse.equinox.http.servletbridge_1.0.0.v20070523
    10 ACTIVE org.eclipse.equinox.servletbridge_1.0.0.v20070523
    11 ACTIVE sample.http_1.0.0

  1. septembre 2, 2010 à 10:24
  2. septembre 6, 2010 à 1:14
  3. septembre 8, 2010 à 1:48
  4. septembre 9, 2010 à 10:57
  5. septembre 29, 2010 à 11:54

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

%d blogueurs aiment cette page :