NanoContainer Servlet

Overview

The NanoContainer Servlet component brings the power of PicoContainer's DependencyInjection to the Java Servlet/web application world. It is the backbone of NanoWar NanoWeb, NanoWar WebWork or any other Java Servlet based web application framework or application that wants to integrate with PicoContainer. It's based on an embedded Container.

(NanoContainer Servlet is not a javax.servlet.Servlet class. It is a reusable component intended to be used within a servlet environment.)

How it works

The NanoContainer Servlet component creates PicoContainer instances at the application, session and request level, using the following hierarchy:

As illustrated in the diagram, a new PicoContainer will be instantiated every time the following happens:

  • The web application starts. A new PicoContainer will be created and stored in the ServletContext.
  • A new session is created. A new PicoContainer (using the application level one as parent) will be created and stored in the HttpSession.
  • A new request is created. A new PicoContainer (using the session level one as parent) will be created and stored in the ServletRequest.
    The lifespan of the request level containers will be really short! However, instantiating a PicoContainer is a very light operation.

PicoContainer Configuration with NanoContainer

It is the web application deployer's responsability to provide the configuration of the various PicoContainer instances used by the web application. There are several ways to do this:

NanoContainer Composition Script referenced from web.xml

The simplest (and recommended) way to configure the containers is by referencing a Container Script from web.xml as follows:

<context-param>
  <param-name>nanocontainer.groovy</param-name>
  <param-value>/WEB-INF/nanocontainer.groovy</param-value>
</context-param>

The script could look like this:

pico = new org.picocontainer.defaults.DefaultPicoContainer(parent)
if(assemblyScope instanceof javax.servlet.ServletContext) {
  pico.registerComponentImplementation(com.yourdomain.SomethingRequiredGlobally)
} else if(assemblyScope instanceof javax.servlet.http.HttpSession) {
  pico.registerComponentImplementation(com.yourdomain.SomethingRequiredInSessions)
} else if(assemblyScope instanceof javax.servlet.ServletRequest) {
  pico.registerComponentImplementation(com.yourdomain.SomethingRequiredInRequests)
}

The value of the param-name element must start with nanocontainer and the extension must be one of .groovy, .bsh, .js or .py. The value of the param-value element must be the path to the script from the top of the webapp. You can use any path as long as it starts with a '/' and ends with the same as the param-name.

NanoContainer Composition Script inlined in web.xml

NanoContainer composition scripts can also be embedded inside web.xml as follows:

<context-param>
  <param-name>nanocontainer.groovy</param-name>
  <param-value><![CDATA[
    pico = new org.picocontainer.defaults.DefaultPicoContainer(parent)
    if(assemblyScope instanceof javax.servlet.ServletContext) {
      pico.registerComponentImplementation(com.yourdomain.SomethingRequiredGlobally)
    } else if(assemblyScope instanceof javax.servlet.http.HttpSession) {
      pico.registerComponentImplementation(com.yourdomain.SomethingRequiredInSessions)
    } else if(assemblyScope instanceof javax.servlet.ServletRequest) {
      pico.registerComponentImplementation(com.yourdomain.SomethingRequiredInRequests)
    }
  ]]</param-value>
</context-param>

The value of the param-name element must start with nanocontainer and the extension must be one of .groovy, .bsh, .js or .py. The value of the param-value element must be a CDATA-embedded script in the corresponding scripting language.

Custom Java class specified in web.xml

If you don't want to embed a NanoContainer script in web.xml you can do it all in Java:

<context-param>
  <param-name>org.nanocontainer.integrationkit.ContainerComposer</param-name>
  <param-value>com.yourdomain.YourContainerComposer</param-value>
</context-param>

The param-name value must be "org.nanocontainer.integrationkit.ContainerComposer" and the param-value the name of a concrete class that implements org.nanocontainer.integrationkit.ContainerComposer. Then, your container composer might look like the following:

public class YourContainerComposer implements ContainerComposer {
  public void composeContainer(MutablePicoContainer pico, Object assemblyScope) {
    if(assemblyScope instanceof javax.servlet.ServletContext) {
      pico.registerComponentImplementation(com.yourdomain.SomethingRequiredGlobally)
    } else if(assemblyScope instanceof javax.servlet.http.HttpSession) {
      pico.registerComponentImplementation(com.yourdomain.SomethingRequiredInSessions)
    } else if(assemblyScope instanceof javax.servlet.ServletRequest) {
      pico.registerComponentImplementation(com.yourdomain.SomethingRequiredInRequests)
    }
  }
}

Nanocontainer composition via XStream

You can also use XStream to compose your containers.

<context-param>
  <param-name>org.nanocontainer.integrationkit.ContainerComposer</param-name>
  <param-value>org.nanocontainer.servlet.XStreamContainerComposer</param-value>
</context-param>

This container will set up container hierarchy from tree xml files, which shall be placed in
WEB-INF/classes - nano-application.xml , nano-session.xml and nano-request.xml. See
examples of xstream based container configuration in test case and from nanocontainer subproject.

Activating NanoContainer in the Web application

In order to activate NanoContainer in the Web application, the following lines also have to be added to web.xml:

<listener>
  <listener-class>org.nanocontainer.servlet.ServletContainerListener</listener-class>
</listener>

This class will receive various events from the Servlet container and do all the magic

Integration with Velocity templating engine

Velocity offers a very fast and lightweight templating engine for web applications as an alternative to JSP. Sometimes it would be nice to have direct access to the container hierarchy from within Velocity templates. This is really easy. Just add a single macro to your global Velocity macro definitions:

## obtain pico component by key
#macro(nano_component $component $key) 
    #set($component = $req.getAttribute('nanocontainer.request').getComponentInstance($key))
#end

Then

#nano_component($fooBar 'foo_bar')

will retrieve the component stored with the key "foo_bar" and place it into the Velocity context. This also works nice with NanoWar WebWork.

In order for this to work properly, you must ensure the components you want to access are registered with java.lang.String keys.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Feb 26, 2004

    Aslak Hellesøy says:

    We should add an example here that illustrates how First class session objects c...

    We should add an example here that illustrates how First class session objects can be used here. The session containers can contain custom (app-specific) Session objects:

    class MyAppSession {
    Basket getBasket()

    Unknown macro: {...}


    User getUser()

    }