Chapter 8. Web Support

A major feature introduced in the 1.1.0 release is support for web applications which enables easy deployment of web artifacts to OSGi.

The biggest problems in running web applications in OSGi involve resource and class loading; there is no notion of bundle space or imported packages in a web application. Each web container has its own class loading hierarchy and classpath assumption which can conflict with the OSGi space. Spring-DM addresses these problems by bridging the web container and the OSGi space so loading is no longer a concern. Unique in its functionality, the web support in Spring-DM integrates directly with the web container so the WAR processing is literally handled by the server, giving full access to its configuration and capabilities(non-blocking vs blocking IO, thread pool, specification support (Servlet 2.3, 2.4, 2.5) and so on). The entire web.xml syntax is supported (without any parsing on Spring-DM behalf), as well as any custom configuration file particular to the target container. In short, everything that the target container supports is available to the OSGi WAR through Spring-DM.

[Tip]Tip
As a complement to this chapter, the Spring-DM distribution contains a number of web samples involving static resources, Servlets and JSPs running inside OSGi.
[Note]Note
For more information on web applications on Java platform, please see the Servlet home page.

8.1. Supported Web Containers

Currently, Spring-DM supports Apache Tomcat 5.5.x/6.0.x and Jetty 6.1.8+/6.2.x out of the box (other containers can be easily plugged in). The web support is JDK 1.4 compatible. Please check the chosen container requirements for more information on the required JVM. In general, Servlet 2.4/JSP 2.0 require JDK 1.4 while Servlet 2.5/JSP 2.1 require JDK 1.5.

8.2. Web support usage

Just like with non-WAR bundles, Spring-DM Web uses the extender pattern to detect and install WARs. However, one crucial difference from the standard Spring-DM Extender is that Spring-DM will only trigger the install and uninstall of the WAR - the actual web application creation and thread management is delegated to the web container in which the WAR is installed. That is, Spring-DM Web only dictates when a WAR is deployer to and undeployed from a web container; it is up to the web container to create and manage the equivalent web application.

To use Spring-DM Web, install:

  • spring-osgi-web.jar - Spring-DM web support
  • spring-osgi-web-extender.jar - Spring-DM web extender

bundles to detect started OSGi WAR bundles (bundles with .war extension) and to deploy them to one of the supported web containers. By default, Tomcat will be used but this can be changed to Jetty or to another custom server. When the war bundle is stopped, Spring-DM will also stop and uninstall the web application associated with it. Different from traditional web development, the Servlet classes need to be explicitly imported as the OSGi class path always takes priority (see below).

Since, when running a web application, it's the web container that does the bootstrapping and instantiation, there is no need to place the Spring .xml files under META-INF/spring or use the Spring-DM manifest entries. Simply bundle your files in the WAR and use your web framework (or Servlets/Listeners) to bootstrap the Spring container. See Section 8.7, “Spring-MVC Integration” for Spring-MVC integration and/or Spring reference documentation for more information. These being said, the Spring Extender is still required as it performs namespace handlers discovery - without it, it would not be possible to use Spring namespaces (like osgi:, aop: or even beans: for that matter).

8.3. WAR classpath in OSGi

The servlet specification defines a number of rules and locations which special meaning inside a WAR. This section will explain how these are handled in an OSGi environment.

8.3.1. Static Resources

When installing a WAR bundle, for static resources, Spring-DM will only consider what is available in the bundle space - this means what is available in the bundle jar and its attached fragments. Conforming to the Servlet spec, resources under WEB-INF folder will be available through the ServletContext API but not to remote clients connecting to the web application. In addition, any resource available in the classpath (imported packages, required bundles or dynamic imports) can be loaded and used by the application code but cannot be seen outside of it.

8.3.2. Servlets

The main difference from the traditional WAR deployment is that the Servlet packages need to be imported so they are available to the WAR bundle. To do that, add the following packages to the Import-Package entry:

Import-Package: javax.servlet,javax.servlet.http,javax.servlet.resources

Additionally, the servlet specification defines the classpath of a WAR, based on some predefined locations. To quickly recap, these are:

  • WEB-INF/classes - all resources under WEB-INF/classes
  • WEB-INF/lib/*.jar - all jars under WEB-INF/lib

In addition, each container implementation can provide common libraries that are appended to the war classpath. Since OSGi, with its class wiring, versioning, reloading, superseeds the WAR classpath, Spring-DM will ignore the WAR predefined locations and will always use the OSGi classpath instead. This means that classes imported by a WAR bundle can be used even if they are not present under WEB-INF/classes folder or inside a jar under WEB-INF/lib. This also means that any class under WEB-INF/classes will not be considered if it's not available in the WAR OSGi classpath.

One of the easiest ways to support the pre-defined WAR locations, is to add them to the bundle classpath, by adding them to the bundle manifest:

Bundle-Classpath: .,WEB-INF/classes,WEB-INF/lib/some.jar,WEB-INF/lib/lib.jar

Make sure the default Bundle-Classpath location (.) is present and there are no whitespaces between the commas.

[Note]Note
Since the OSGi API doesn't provide a hook for bundles to be pre-processed, Spring-DM cannot automate this process in a reliable way. However, we are working on finding a suitable solution. Note that there are tools (bnd) that can add these entries during packaging.

Before creating entries for embedded libraries, consider whether they can be installed as OSGi bundles - doing so allows them to be shared with other WARs if needed and since OSGi allows versioning, it is perfectly okay to have multiple versions of the same library inside the same VM.

8.3.3. Java Server Pages

For JSPs, Spring-DM integrates with Tomcat Jasper 2 Engine which means JSP 1.2, 2.0 and 2.1 are supported. OSGified versions for Jasper (from Tomcat 5.5.x and 6.0.x distribution respectively) are available in the Spring-DM OSGi repository. No imports on Jasper classes are required for the OSGi bundle.

8.3.3.1. Tag libraries

The JSP spec allows the creation of tag libraries to “define declarative, modular functionality that can be reused by any JSP page”. Also known as taglibs, these reusable components consist of Java classes (the tag implementation) and description files that indicate how the tags can be used. Spring-DM extends the JSP convention, of placing the taglibs either packed as a jar under WEB-INF/lib or unpacked under WEB-INF/classes, by detecting any taglib defined in the bundle classpath (imported packages or required bundles).

Spring-DM will automatically look for any taglib file (*.tld) available in the bundle classpath and will make them available to the Jasper engine. However, while the tag definitions are automatically discovered, the tag classes are not - again, the OSGi classpath takes priority. This means that in order to use a tag, the war bundle would have to import the tag corresponding classes since otherwise, they are not seen by the bundle and the tag cannot be used.

When dealing with libraries that export a lot of tags, one can use the Require-Bundle header instead of Import-Package for importing the tags:

Require-Bundle: org.springframework.osgi.jstl.osgi

Using the manifest entry above, all the classes (and thus tag implementations) exported by the JSP Standard Tag Library (or JSTL in short), are seen by the war bundle and thus can be used inside the JSPs.

[Warning]Warning
Before using Require-Bundle on a large scale, please read the OSGi specification (section 3.13) for an in-depth review of its implications.

No matter what mechanism you decide to use for the war classpath, due to the OSGi capabilities, it is possible to create libraries that are shared between multiple WARs while having full control over the used packages. Each bundle can import only the packages (and the versions) needed not an entire library jar - in fact, packages from different bundles/jars can be selectively used to obtain the desired behaviour - a very powerful capability which should make web application deployment easier.

8.4. Configuring the web extender

Just like the core extender, the web extender can be configured by using OSGi fragments. Following a similar pattern, the web extender looks for all XMLs under META-INF/spring/extender folder in its bundle space and assembles them into an application context that is used internally as its configuration. To override a default setting of the web extender, look up the appropriate bean name from the table below, define it in a suitable manner and then attach it as a fragment to the spring-osgi-web.jar, using:

Fragment-Host: org.springframework.bundle.osgi.web.extender

The following beans are currently recognized by the web extender:

Table 8.1. Web Extender Configuration Options

Bean NameTypeRoleDefault ClassDefault Behaviour
warDeployerWarDeployer [a] Installs OSGi bundles as web applications. The deployers takes care of locating the required web container and installing and uninstalling web applications.TomcatWarDeployer [b] Installs OSGi WARs to Tomcat 5.5.x/6.0.x. The servers needs to be installed, started and published as OSGi services as explained here.
contextPathStrategyContextPathStrategy[a]Determines the context path associated with an OSGi bundle/WAR. The returned path is used by the war deployer to install the war application.DefaultContextPathStrategy[c] Returns as context path of the war based on the Web-ContextPath manifest header or (if missing), the file name from the bundle location, falling back to the bundle name and bundle symbolic name respectively. If neither of these is present, the bundle object identity will be used.

[a] Part of org.springframework.osgi.web.deployer package

[b] Part of org.springframework.osgi.web.deployer.tomcat package

[c] Part of org.springframework.osgi.web.deployer.support package


Note that to properly support wars, whether they are using Servlet 2.5 or not, the Spring-DM web extender considers as WARs bundles that contains a .war extension.

8.4.1. Changing the war deployer

To change the Tomcat deployer to Jetty for example, one can create a configuration file META-INF/spring/extender/jetty-deployer.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="warDeployer"                                                               (1)
        class="org.springframework.osgi.web.deployer.jetty.JettyWarDeployer" />          (2)
</beans>
1

Pre-defined bean name used by the web extender

2

Bean implementing org.springframework.osgi.web.deployer.WarDeployer interface

Once the file is created, it should be bundled in an OSGi fragment attached to the Spring-DM Web Extender bundle by adding the Fragment-Host header:

Fragment-Host: org.springframework.bundle.osgi.web.extender

Now the fragment can be deployed alongside spring-osgi-web.jar bundle to plug in Jetty.

A pre-packed Jetty fragment is available in the Spring-DM maven repository under jetty.web.extender.fragment.osgi artifactId.

8.5. Customizing the standard deployers

By default, the out of the box deployers look up the needed services, at startup. As the services are considered mandatory, the deployers will wait for them and, in case they are not found, will throw an exception. In cases where the default timeout or service lookup filter is not be appropriate, one can customize the service used through a Spring-DM reference:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:osgi="http://www.springframework.org/schema/osgi"
   xmlns:p="http://www.springframework.org/schema/p"                                     (4)
   xsi:schemaLocation="http://www.springframework.org/schema/beans   
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/osgi
     http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <osgi:reference id="myTomcatServer"                                                  (1)
		interface="org.apache.catalina.Service" 
		filter="(environment=testing)"
		cardinality="0..1"/>
		
    <bean id="warDeployer"                                                               (2)
        class="org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer"
        p:service-ref="myTomcatServer"/>                                                 (3)

</beans>
1

User defined OSGi service lookup

2

Deployer definition (name is important)

3

Service property assignment (through p namespace)

4

Spring's p namespace declaration - see this blog entry for more information

Make sure to add the packages on which your configuration depends to the fragment manifest (since the web extender bundle imports only the packages it needs: Spring-DM web support's). For the example above, one must import Catalina Service's package. Since the Service interface signature depends on the Connector class from another package, its package needs to be imported as well - not doing so results in ClassNotFoundException/NoClassDefFoundErrors:

# Catalina packages
Import-Package: org.apache.catalina,org.apache.catalina.connector
# Spring-DM Web Extender
Fragment-Host: org.springframework.bundle.osgi.web.extender

8.6. OSGi-ready libraries and web development

Unfortunately, at the moment most libraries used for web development are not OSGi bundles, which means they cannot be used inside the OSGi space unless they are embedded in other bundles. To address this problem, the Spring-DM project has osgified most of the common libraries and made them available through a dedicated Maven repository (which can be found in the appendix). Please note that the current repository, for now, is provided as-is without any support. These being said, we hope you find it useful.

8.6.1. Deploying web containers as OSGi bundles

Spring-DM web support expects the web containers to be installed as OSGi services. Since neither the Tomcat nor the Jetty distribution do this, Spring-DM offers two simple yet useful OSGi Activators for both containers at the Spring-DM OSGi repository. Once installed, these will programmatically start the appropriate web container based on a default configuration (which can be customized and publish it as an OSGi service. While the activators are generic, they can be easily customized for advance usages or even replaced - it's up to each deployer to decide how the server instances are looked up.

[Note]Note
The activator binaries and sources can be found either in the Spring-DM repository (see below) or under the lib/ folder inside the Spring-DM (with-dependencies) distribution

All entries in the repository belong to the org.springframework.osgi group and have an .osgi termination to differentiate them from the original jars.

8.6.1.1. Tomcat 5.5.x/6.0.x

Apache Tomcat version 5.5.x and 6.0.x are available as OSGi artifacts in the repository under catalina.osgi artifactId. The jars require only commons-logging, JMX and Servlet/JSP libraries to be present.

In addition to the Catalina artifacts, the repository contains also a Tomcat activator (that works with both 5.5.x and 6.0.x versions) named catalina.osgi.start. The activator understands Tomcat XML configuration and contains a default, minimal setup that starts the server on localhost, port 8080. This behaviour can be customized by placing the desired configuration (which will override the default one) under conf/server.xml location (following the Tomcat folder layout) in a fragment attached to the Tomcat activator.

To attach fragments to the Tomcat activator, specify the following host name in the fragment manifest:

Fragment-Host: org.springframework.osgi.catalina.start.osgi

8.6.1.2. Jetty 6.1.8+/6.2.0

Since Jetty is OSGi-ready by default, the official distribution can be installed without any transformation/processing on the OSGi platform. However, since there is no activator, Spring-DM provides one, similar in functionality to the one available for Tomcat. The activator has jetty.start.osgi as artifact id. Just like the Tomcat case, a default configuration is included which starts a Jetty instance on localhost, port 8080. To change the defaults, place your Jetty configuration under etc/jetty.xml location.

To attach fragments to the Jetty activator, specify the following host name in the fragment manifest:

Fragment-Host: org.springframework.osgi.jetty.start.osgi

Just like the extender, each activator uses a default configuration if the user doesn't provide one. For the latter case, one should use fragments (as mentioned above) to provide a customized configuration and to avoid modifying the distribution jar.

8.6.2. Common libraries

The Servlet, Java Server Pages, Standard Taglib, Commons-EL and other web libraries are available as well in the Spring-DM repository. When browsing use an S3 compatible application .

8.7. Spring-MVC Integration

Since 1.1, Spring-DM integrates closely with Spring-MVC framework. This section details how Spring-MVC applications can be run into OSGi environments (it is assumed the reader is familiar with Spring-MVC concepts and APIs).

In order to be properly used inside an OSGi platform, a Spring-MVC application needs to adapt to its new environment. Spring-DM provides a dedicated, OSGi-aware, web application context (called OsgiBundleXmlWebApplicationContext) that offers the same functionality and behaviour to its Spring-MVC brethren, XmlWebApplicationContext. The application context is aware of the web application BundleContext and thus is able to load resources from the OSGi space, import and export OSGi services and support the BundleContextAware and component scanning across the bundles included in the classpath.

To use this application context instead of the default one, use the contextClass parameters supported by Spring's ContextLoaderListener and DispatcherServlet inside your web application WEB-INF/web.xml:

<context-param>
  <param-name>contextClass</param-name>                                                                         (1)
  <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>    (2)
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>                        (3)
</listener>

<servlet>
  <servlet-name>petclinic</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>                              (4)
  <load-on-startup>2</load-on-startup>
  <init-param>
    <param-name>contextClass</param-name>                                                                       (5)
    <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>  (2)
  </init-param>
</servlet>
1

Name of the context-param used by Spring's ContextLoaderListener to determine the root web application context type

2

Fully qualified name of the OSGi-aware web application context class

3

Spring configuration bootstrap listener

4

Spring MVC front controller

5

Name of the init-param used by Spring's DispatcherServlet to determine the web application context type

With this configuration, deployed Spring-MVC bundles will be able to look up the existing BundleContext and be aware of the OSGi environment.

[Note]Note
You still need to add the proper package imports to your Spring-MVC application - the WAR is still a bundle after all which means without the proper manifest entries, it will have an invalid class path and will not be able to work properly.