There are situations when a call to httpService.registerServlet method is simply not enough. Writing servlets using only out.println() is also not the most pleasant pastime imaginable. Fortunately the equinox folks have recently finished a set of bundles that make it possible to create JSP UI's.

This article presents a way to include JSP pages in OSGI web applications. It draws heavily from information avaiable on following pages:

JSP Documentation

For your reference, the documentations that help you get started later:

Preparing the environment.

First we need a servlet container compatible with Servlet API v. 2.4.

  • javax.servlet
  • org.eclipse.equinox.http.jetty
  • org.eclipse.equinox.http.servlet
  • org.mortbay.jetty
  • org.apache.commons.logging
  • org.eclipse.equinox.http.registry

These bundles can be easily downloaded from appropriate CVS repositories by importing a Team Project Set from

http://www.eclipse.org/equinox/server/downloads/jettyhttp-anon.psf

It is worth noting that the build in equinox http service implementation org.eclipse.equinox.http is NOT compatible. It WILL cause conflicts with Jetty, since it is configured with the same system property (org.osgi.service.http.port), and will try to listen on the same port which will obviously fail. It is best to disable this bundle completely.

When the Jetty works, we may add bundles responsible for JSP support, these are:

  • javax.servlet.jsp [branch v2_0]
  • org.apache.commons.el [branch v1_0]
  • org.apache.jasper [branch v5_5_17]
  • org.eclipse.equinox.jsp.jasper

They can be easily downloaded with the following team project set:

http://www.eclipse.org/equinox/server/downloads/jspsupport-anon.psf

JSTL - standard tag libary

In Nepomuk, we can also use the JSTL. Taglibs DO work when packaged as osgi bundles. The JSTL bundle can be downloaded from the sourceforge project call Eclipse Server Side Examples (http://sourceforge.net/project/showfiles.php?group_id=180967), more specifically from http://prdownloads.sourceforge.net/sse-examples/org.eclipse.equinox.jsp.jstl-proj.zip?download

It is included in the external folder of the Eclipse target platform.

To make it work, you need to (weird as it is), copy the TLD files from the taglib jar to your web/WEB-INF/tld directory, read the crucial tip.

NOTE: we have both JSTL 1.0 and 1.1.2 at the moment, you have to be careful to select the right versions in your manifest.mf

Writing the application

An OSGI bundle is NOT a war archive. The differences can learned by observing the examples from the above mentioned Server Side Examples project (http://sourceforge.net/project/showfiles.php?group_id=180967) these are:

For those in a hurry: here are the most important differences and issues you need to remember

  1. The osgi framework is not tomcat. It doesn't automatically recognize jsp pages and doesn't compile them. To achieve this we need to use the equinox.jsp.jasper bundle. It provides two classes that we need.:

1.1. The first one is the JspServlet?. When it receives a request for a jsp page, it loads it, compiles it (with jasper), executes the generated servlet and returns the html to the client.

1.2. In order for it to be useful we need to wrap it in a ContextPathServletAdaptor?. This piece of magic enables us to register a servlet with a context path that contains a mask (i.e. /*.jsp).

All in all a code snippet responsible for all of this might look like this:

 HttpService httpService = (HttpService) context.getService(reference);
 try {
    HttpContext commonContext = httpService.createDefaultHttpContext();
    httpService.registerResources("/jsp-examples", "/web", commonContext); 
    Servlet JspServlet jspServlet = new JspServlet(context.getBundle(), "/web");
    Servlet adaptedJspServlet = new ContextPathServletAdaptor(jspServlet, "/jsp-examples", "/web");  
    httpService.registerServlet("/jsp-examples/*.jsp", adaptedJspServlet, null, commonContext);
 } catch (Exception e) {
    e.printStackTrace();
 }
  1. The manifest has to contain all libraries and class folders. The http service won't be intelligent enough to detect everything it should in WEB-INF/lib and WEB-INF/classes. An example taken from the struts-examples application.

 Import-Package: javax.servlet;version="2.4.0",
  javax.servlet.http;version="2.4.0",
  javax.servlet.jsp;version="2.0.0",
  javax.servlet.jsp.el;version="2.0.0",
  javax.servlet.jsp.jstl.core,
  javax.servlet.jsp.jstl.fmt,
  javax.servlet.jsp.jstl.sql,
  javax.servlet.jsp.jstl.tlv,
  javax.servlet.jsp.resources;version="2.0.0",
  javax.servlet.jsp.tagext;version="2.0.0",
  javax.servlet.resources;version="2.4.0",
  org.apache.commons.logging,
  org.eclipse.equinox.jsp.jasper,
  org.osgi.framework;version="1.3.0",
  org.osgi.service.http;version="1.2.0",
  org.osgi.util.tracker;version="1.3.1"
 Bundle-ClassPath: .,
  web/WEB-INF/classes/,
  web/lib/antlr.jar,
  web/lib/commons-beanutils.jar,
  web/lib/commons-digester.jar,
  web/lib/commons-fileupload.jar,
  web/lib/commons-validator.jar,
  web/lib/jakarta-oro.jar,
  web/lib/struts.jar
  1. By default our application is situated at the 'root' of our web server, the paths, under which our resources will be visible are those passed to the registerServlet and registerResources methods. Choose them wisely, conflicts will occur if servlets from two different applications will try to occupy the same path.

  1. Remember that you can pass initialization parameters to the servlet. There is no web.xml, so you should do it manually. This example has been taken from the struts-examples application
 Dictionary initparams = new Hashtable();
 initparams.put("servlet-name", "action"); //Note: requires servlet-name support in Http Service Implementation
 initparams.put("config", "/WEB-INF/struts-config.xml");
 initparams.put("config/exercise", "/WEB-INF/exercise/struts-config.xml");
 initparams.put("config/upload", "/WEB-INF/upload/struts-config.xml");
 initparams.put("config/validator", "/WEB-INF/validator/struts-config.xml");
 initparams.put("config/dispatch", "/WEB-INF/dispatch/struts-config.xml");
 initparams.put("debug", "2");
 initparams.put("detail", "2");
 Servlet adaptedActionServlet = new ContextPathServletAdaptor(new ActionServlet(), "/struts-examples", "/web");				
  1. Taglibs seem to work, but you need to remember to place the tld's in the META-INF directory of the taglib bundle. The same applies to tld's you'd like to include in your own applications, not as separate bundles (in that case tld's should be in the META-INF, of the taglib jar, Don't forget to include them in the Bundle-ClassPath in the manifest). See the struts-examples for details.

Using the Nepomuk JSP layout

We have a website that makes your JSP pages or servlets look better. You have to add a piece of HTML before and after your own content, we have written some utility methods that should handle this.

Additional to above dependencies, you need to add this to your manifest-bundle dependencies:

  • org.semanticdesktop.nepomuk.server.app

Then, you can use the ServletUtil.java class

Here is a minimal JSP that uses the ServletUtil? to make the HTTP look better:

<%@page import="org.semanticdesktop.nepomuk.util.ServletUtil"%>
<%
ServletUtil.includeBefore(request, response, getServletContext());
%>
<h2>Hello world</h2>
<p>Nepomuk rocks</p>
<%
// out.flush() is important!!
out.flush();
ServletUtil.includeAfter(request, response, getServletContext());
%>

If you want to access nepomuk services, registered at the OSGI registry, you can use the ServletUtil.registerBundleContext(servlet, context) method. After creating your servlet, you register the OSGI bundlecontext within the sevlet's config. Then, from within the servlet, you can later get the BundleContext by calling ServletUtil.getBundleContext(getServletContext());. With the bundle-context, you can then later access the middleware or other services.

For an example how to register the bundle-context:

Example of using it:

A good example how to make simple JSP interfaces and servlets is this project, which is quite small:

Using JSP and Servlets in NEPOMUK effectively

During development, a few simple patterns emerged that are common in JSP development. Feel free to copy or extend.

  • Requests are addressed to a servlet, then the servlet processes the input and forwards the rendering process to a JSP page using a RequestDispatcher.
  • Variables are passed from servlet to JSP using request.setAttribute("myvar")
  • Variables are read in JSP either using one of:
    <!-- print the value of myvar using JSTL -->
    <c:out value="${myvar}" />
    <!-- print the value of myvar easily, just use the dollar-sign and curly brackets anywhere in JSP -->
    ${myvar}
    <% 
    // use findAttribute, this searches more possible places
    Object myvar = pageContext.findAttribute("myvar");
    %>
    

Problems and Answers

Well, in practice ...

Your JSP applications works in Developement but not in deployment

Did you check, in the build.properties (part of the MANIFEST.MF) the box to include your web-folder in the build? If not, your will get a

HTTP ERROR: 404
/index.jsp
RequestURI=/org.semanticdesktop.services.localmetadataalignment/index
Powered by Jetty://

exporting osgi plugins with web-resources

It results in somethign like this:

source.. = src/
output.. = bin/
bin.includes = META-INF/,\
               .,\
               lib/secondstring.jar,\
               lib/h2.jar,\
               lib/smile.jar,\
               web/
src.includes = META-INF/,\
               src/,\
               web/

Your non-jsp pages, javascript/images/etc are all 404

Make sure you use:

httpContext = httpService.createDefaultHttpContext();

NOT

httpContext = ServletUtil.getHttpContextOfServer();

Attachments