Accueil > Eclipse RAP > My first steps with Eclipse RAP [step4]

My first steps with Eclipse RAP [step4]


In [step3], we have explained how to manage dependencies between bundles with Require-Bundle and Import-Package. OSGi services registry was introduced and used to publish/consume a business service UserService which provides list of Users. At this step we have explained OSGi bundle and OSGi container.

RAP Application (like RCP Application) needs OSGi Container, but it also needs Servlet Container, because RAP Application is a WEB Application. When http://127.0.0.1:8080/rap/ URL is called, the RAP Servlet org.eclipse.rap.ui.internal.servlet.RequestHandler is called. This Servlet is a dispatcher which does the RAP treatments according to the URL request.

In this article I will explain how OSGi Container and Servlet Container can work together. OSGi provides the org.osgi.service.http.HttpService which is a service where you can register/unregister Servlet.

In this article we will create a new bundle org.akrogen.dynaresume.httposgiapplication which register in the OSGi services registry the servlet HelloWorldServlet with the path /helloworld. This servlet will be accessible with the URL http://localhost/helloworld :

Here a schema about the architecture OSGi container and Servlet container used in this article :

Download

You can download rap_step4.zip which contains the following explained projects :

  • org.akrogen.dynaresume.httposgiapplication which is the generated OSGi Bundle explained in this article.

org.akrogen.dynaresume.httposgiapplication

Here we will create a new bundle org.akrogen.dynaresume.httposgiapplication which will register/unregister a Servlet HellowWorldServlet in the OSGi registry services. At the end of this section, your workspace looks like this :

Create Bundle org.akrogen.dynaresume.httposgiapplication with the org.akrogen.dynaresume.httposgiapplication.internal.Activator. This Activator will be used to register our servlet in the OSGi services registry.

org.akrogen.dynaresume.httposgiapplication/META-INF/MANIFEST.MF looks like this :

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Http OSGi Application
Bundle-SymbolicName: org.akrogen.dynaresume.httposgiapplication
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: org.akrogen.dynaresume.httposgiapplication.internal.Activator
Import-Package: org.osgi.framework;version="1.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6

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

Create org.akrogen.dynaresume.httposgiapplication.internal.HelloWorldServlet Servlet class like this :

package org.akrogen.dynaresume.httposgiapplication.internal;

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</body></html>"); //$NON-NLS-1$
	}
}

internal package is used to forbid the access of this class to other bundles (because this package is not exported).

Import package javax.servlet.*

You will have compilation problems with javax.servlet and javax.servlet.http packages. To fix this problem, Import-Package javax.servlet and javax.servlet.http. Your MANIFEST.MF must import those 2 packages :

Import-Package: javax.servlet;version="2.5.0",
 javax.servlet.http;version="2.5.0"

At this step, your Project compiles without problem.

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 registry, 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 have this service we can:

  • call HttpService#registerServlet in the Activator#start to register the servlet HelloWorldServlet.
  • call HttpService#unregister in the 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. In [step3] we have used BundleContext#getServiceReference to retrieve a service, but the problem with this way is that the service you want to retrieve could not be published when BundleContext#getServiceReference is called. ServiceTracker resolves this problem by using OSGi events listener.

Create the class org.akrogen.dynaresume.httposgiapplication.internal.HttpServiceTracker like this :

package org.akrogen.dynaresume.httposgiapplication.internal;

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(
					"/helloworld", 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("/helloworld"); //$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 want to retrieve (in our case HttpService).

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

When HttpServiceTracker opens 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 with path /helloworld :

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

When HttpServiceTracker closes 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 with path /helloworld :

public void removedService(ServiceReference reference, Object service) {
  HttpService httpService = (HttpService) service;
  httpService.unregister("/helloworld"); //$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-Package 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"

Activator

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

package org.akrogen.dynaresume.httposgiapplication.internal;

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;
	}

}

Testing

At this step, the bundle org.akrogen.dynaresume.httposgiapplication registers in the OSGi services registry the servlet HelloWorldServlet with the path /helloworld. Now it’s time to test our bundle. To test this bundle we need OSGi container and Servlet container. There are 2 ways to do that :

  • Embedding an HTTP server in Equinox is the way used by default when RAP Application is started with RAP Tooling. When the launch RAP Application is started, OSGi container is started and Servlet container (Jetty server) which is an OSGi bundle is started : here OSGi container starts the server (Servlet container).
  • Equinox in a Servlet Container is the way that you must use when your Servlet Container (Tomcat, Jetty) is not an OSGi bundle : here the server (Servlet container) starts OSGi container (with ServletBridge servlet which must be declared in the web.xml).

For more information, please go to Server-Side Equinox.

Embedding an HTTP server in Equinox

Here a basic schema which show you how to work Embedding an HTTP server in Equinox with our org.akrogen.dynaresume.httposgiapplication bundle which provides HelloWorldServlet with /helloworld path :

The HTTP server (Jetty) is an OSGi bundle which is started by the OSGi container.

OSGi launch

Now we can create an OSGi launch to play with our servlet. Like we have seen, OSGi launch is used to select bundles that we want to install in the OSGi container. In our case we need the required bundles :

  • javax.servlet bundle which provides Servlet interfaces.
  • org.eclipse.osgi.services which provides OSGi interface. This bundle contains also the OSGi container Equinox.

Those bundles are required to compile our bundle. Afterward we need Server OSGi bundles :

  • org.mortbay.jetty.server OSGi Jetty Server.
  • org.mortbay.jetty.util utilities class where org.mortbay.jetty.server is based on.
  • org.eclipse.equinox.http.jetty bundles used to configure Jetty.
  • org.eclipse.equinox.http.servlet bundle which implement HttpService interface.

To test, we must create a launch. If you click on Launch the framework, it will show you existing launch :

Here we want to create new OSGi Launch. To do that, go to the Run->Run Configurations… menu

Select OSGi Framework menu item, click on your mouse and select New :

This action creates a new OSGi Launch. OSGI bundles comes from Workspace and Target Platform are selected :

Fill in the Name field with Http OSGi Application and unselect all OSGi bundles. Select just our bundle org.akrogen.dynaresume.httposgiapplication :

If you click on Validate button, you will see this error dialogue :

To fix that, click on Add Required Bundles… button :

This action will select javax.servlet and org.eclipse.osgi.services bundles. Now we must select Jetty bundles (Servlet Container). To do that select org.mortbay.jetty.server and click on Add Required Bundle… button :

This action will select org.mortbay.jetty.server and org.mortbay.jetty.util bundles. Now we must select HttpService implementation with Jetty. To do that select org.eclipse.equinox.http.jetty and click on Add Required Bundle… button :

This action will select org.eclipse.equinox.http.jetty and org.eclipse.equinox.http.servlet bundles.

Our OSGi launch is finished. Now we can play with our servlet.

Testing

Click on Run button to start the Http OSGi Application launch :

This action starts the OSGi container with the selected bundles of the launch. OSGi container starts the Servlet Container (Jetty) and our servlet is registered in the OSGi services registry.

To check that, open your favorite browser and go to the http://localhost/helloworld URL :

Servlet display Hello World.

Type in the OSGi console ss to see installed bundles :

osgi> ss

Framework is launched.

id State Bundle
0 ACTIVE org.eclipse.osgi_3.6.2.R36x_v20110210
1 ACTIVE org.akrogen.dynaresume.httposgiapplication_1.0.0.qualifier
2 ACTIVE javax.servlet_2.5.0.v200910301333
3 ACTIVE org.eclipse.osgi.services_3.2.100.v20100503
4 ACTIVE org.mortbay.jetty.util_6.1.23.v201004211559
5 ACTIVE org.eclipse.equinox.http.jetty_2.0.0.v20100503
6 ACTIVE org.mortbay.jetty.server_6.1.23.v201004211559
7 ACTIVE org.eclipse.equinox.http.servlet_1.1.0.v20100503

In my case, org.akrogen.dynaresume.httposgiapplication bundle has the ID 1.

Type stop 1 to stop org.akrogen.dynaresume.httposgiapplication, if you go to the http://localhost/helloworld URL, you will see Jetty error :

Our servlet was unregistered from the OSGi service registry, so it is not available.

Type start 1 to start org.akrogen.dynaresume.httposgiapplication :

If you go to the http://localhost/helloworld URL, you will see that servlet is available again.

You can notice that it is possible to start/stop a servlet without stopping OSGi container and Servlet Container. This feature is very interesting :

  • for development mode : start one time the OSGi container and Servlet Container and use start/stop from OSGi console to retest your code of your servlet. No need to restart your server (which can be very long in a big project).
  • for production mode : you can install a new version of the bundle which could fix problems of the servlet (or another service) without stopping the HTTP Server.

Equinox in a Servlet Container

Here is a basic schema which shows you how to work Equinox in a Servlet Container with our org.akrogen.dynaresume.httposgiapplication bundle which provides HelloWorldServlet with /helloworld path :

The BridgetServlet must be declared in the web.xml. When bridge WEB Application is started with HTTP Server (Tomcat, Jetty…), the BridgeServlet is created and BridgeServlet#init() is called. The sheme shows you several steps :

  • (1) init() : BridgeServlet#init() is called by HTTP server when the server starts 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 starts all the OSGi bundles coming from the /plugins folder.
    • (1.1) the org.akrogen.dynaresume.httposgiapplication starts and registers a HelloWorldServlet in the OSGi services registry. More exactly, it uses HttpService coming from the OSGi services registry to register the servlet. The registration is done with an another servlet called ProxyServlet. When org.akrogen.dynaresume.httposgiapplication 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 searchs a Servlet registered with /helloworld path from the OSGi registry services. More exactly it uses ProxyServlet.
    • (2.3) it returns the Servlet founded. In our case HelloWorldServlet from the org.akrogen.dynaresume.httposgiapplication 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 the server stops the bridge WEB application. In this method, the Equinox OSGi container is stopped by calling org.eclipse.core.runtime.adaptor.EclipseStarter#shutdown().

I don’t explain how to manage this mode in this article but if you want more information about that, please read my articles called OSGi Equinox in a Servlet Container.

Libra

I have never tested, but if you want to use OSGi with WTP, please go to Libra :

Libra is an open source project under the Eclipse Web Tools Platform Container Project. It provides standard tools for OSGi Enterprise application development and in particular tools that integrate the existing WTP and PDE tooling so that OSGi Enterprise applications can be developed with both tooling at the same time. Libra also will enable users to work with tools for better experience in the Server-Side Equinox scenario.

Conclusion

In this article we have seen how to manage OSGi container and Servlet container together with HttpService. At this step we have enough knowledge about OSGi to understand generated RAP Application and RAP Architecture. In the next article [step5] we will explain the generated code RAP Hello World Application that we have generated in the [step1].

Publicité
Catégories :Eclipse RAP
  1. Yippikai
    avril 21, 2012 à 2:05

    Hi,

    Thanks for this great tutorial.

    When I was clickin on « Run button to start the Http OSGi Application », I was having bind error message with Jetty. I am not sure why, but i believe I need to set an http port on my configuration for Jetty to start properly.
    From RAP it’s quite easy to do as it’s defined on the Run Configuration settings, but with OSGi, it can also be achieved using
    This can be done by adding in the VM argument of the Run Configuration:
    -Dorg.eclipse.equinox.http.jetty.http.port=8080

    Cheers.

    • avril 21, 2012 à 3:21

      Hi,

      I have never start the Jetty without the RAP Run configuration.
      Thank’s for your comment.

      Regards Angelo

  2. Amod
    avril 24, 2012 à 6:13

    Yup perfect and Thankx to Yippikai
    Need to start Jetty before from the VM Argument tab.
    Saved time.
    -Amod

  1. mai 20, 2011 à 8:47
  2. mai 21, 2011 à 10:37
  3. mai 24, 2011 à 9:44

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 :