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 | |
---|---|
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 | |
---|---|
For more information on web applications on Java platform, please see the Servlet home page. |
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.
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 deployed 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 and to deploy them to one of the supported web containers. Note that the web extender consider a war a bundle
that has trailing .war
in its location or contains a WEB-INF
entry.
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 Servlet
s/Listener
s)
to bootstrap the Spring container. See Section 9.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).
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.
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.
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 | |
---|---|
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.
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.
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 | |
---|---|
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.
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.osgi.web.extender
The following beans are currently recognized by the web extender:
Table 9.1. Web Extender Configuration Options
Bean Name | Type | Role | Default Class | Default Behaviour |
---|---|---|---|---|
warDeployer | WarDeployer
[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. |
contextPathStrategy | ContextPathStrategy [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 [b] Part of [c] Part of |
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.
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" class="org.springframework.osgi.web.deployer.jetty.JettyWarDeployer" /> </beans>
Pre-defined bean name used by the web extender | |
Bean implementing |
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.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 (make sure to use version 1.0.1+).
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" 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" interface="org.apache.catalina.Service" filter="(environment=testing)" cardinality="0..1"/> <bean id="warDeployer" class="org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer" p:service-ref="myTomcatServer"/> </beans>
User defined OSGi service lookup | |
Deployer definition (name is important) | |
Service property assignment (through | |
Spring's |
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/NoClassDefFoundError
s:
# Catalina packages Import-Package: org.apache.catalina,org.apache.catalina.connector # Spring DM Web Extender Fragment-Host: org.springframework.osgi.web.extender
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.
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 | |
---|---|
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.
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
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. Similar to the Tomcat case, a default configuration bundle (named jetty.etc.osgi
) is provided for starting a Jetty instance on localhost
, port 8080
. To change the defaults, place your Jetty configuration under etc/jetty.xml
location (either by updating the provided bundle by using a custom one).
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 which can be overridden by the user. For the latter case, one should use fragments (as mentioned above) to provide a customized configuration and to avoid modifying the distribution jar.
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 .
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> <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>petclinic</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value> </init-param> </servlet>
Name of the | |
Fully qualified name of the OSGi-aware web application context class | |
Spring configuration bootstrap listener | |
Spring MVC front controller | |
Name of the |
With this configuration, deployed Spring-MVC bundles will be able to look up the existing BundleContext
and
be aware of the OSGi environment.
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. |