Thursday, April 1, 2010

DataSource in Jetty through JNDI

Prerequesite: Please ensure JNDI is configured properly and you can pass parameters to Jetty via JNDI.

First, you need to add the used libraries to Jettys classpath. In my case, I wanted commons-dbcp (alternative: c3p0) over an H2 database, so I added the following to my pom.xml


      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>7.0.1.v20091125</version>
        <dependencies>
          <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
          </dependency>
          <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.2.131</version>
          </dependency>
        </dependencies>
      </plugin>

On Jetty standalone, I copied the libraries to the lib/ext directory and added ext to the options in start.ini. Please note that commons-dbcp also requires commons-pool.

Next, add the configuration of your DataSource to jetty-env.xml:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure id="thisfile" class="org.eclipse.jetty.webapp.WebAppContext">
  <New class="org.eclipse.jetty.plus.jndi.Resource">
    <Arg><Ref id="thisfile"/></Arg>
    <Arg>jdbc/nameOfMyDatabase</Arg>
    <Arg>
      <New class="org.apache.commons.dbcp.BasicDataSource">
        <Set name="url">jdbc:h2:/path/to/your/DataBase</Set>
      </New>
    </Arg>
  </New>
</Configure>

To actually have access to the datasource, you also need modify web.xml to declare that you want a resource unter this name:


  <resource-ref>
    <res-ref-name>jdbc/nameOfMyDatabase</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

The database is now available as DataSource at java:/comp/env/jdbc/nameOfMyDatabase and can be used directly in tools such as hibernate by setting hibernate.connection.datasource to this value.

Background: Hibernate did not properly release the database connection when the WebApp shut down. This resulted in Exceptions when trying to undeploy and deploy the webapp without killing Jetty, as the embedded database existed both in the old and the new context. By moving the connection out of the WebApp, and into Jetty's responsibility, the embedded database driver now only exists in Jetty's context. Adding the connection pool is just decoration.

Passing parameters to Jetty 7 via JNDI

If you have the jetty-maven-plugin properly configured in the pom.xml, then everything is already set up.

For Jetty Standalone, please see my other post about configuring JNDI in jetty standalone.

Passing parameters through JNDI proved to be very simple. In src/main/webapp/WEB-INF create a file jetty-env.xml, with the following contents:


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure id="thisfile" class="org.eclipse.jetty.webapp.WebAppContext">
  <New class="org.eclipse.jetty.plus.jndi.EnvEntry">
    <Arg><Ref id="thisfile"/></Arg>
    <Arg>paramname</Arg>
    <Arg type="java.lang.String">A sample parameter value</Arg>
    <Arg type="boolean">false</Arg>
  </New>
</Configure>

You can then load your parameter through the standard naming context:


import javax.naming.Context;
import javax.naming.InitialContext;
...
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
String param = (String) envCtx.lookup("paramname");

Of course, you can add other parameters, rename paramname to something that makes more sense, or use other data types. Please note: Jetty supports more datatypes than required by the J2EE standard.

Configuring Jetty 7 standalone for JNDI

Configuring Jetty 7 for JNDI can be a bit tricky. If you miss any of these points, you may end up passing JNDI parameters, but they will not be available in your webapp.

  1. Ensure the plus libraries are loaded. You can to this by adding the option plus in Jetty's start.ini
  2. Ensure jetty is configured to use the plus configuration classes. You can do this by adding the following inside the context, or the jetty-env.xml:
      <Set name="ConfigurationClasses">
        <Array id="plusConfig" type="java.lang.String">
          <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
          <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
          <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
          <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
          <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
          <Item>org.eclipse.jetty.plus.webapp.Configuration</Item>
          <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
          <Item>org.eclipse.jetty.webapp.TagLibConfiguration</Item>
        </Array>
      </Set>
    

    The documentation suggests adding etc/jetty-plus.xml to the startup, which would make this configuration available as plusConfig to refer to. This did not work on my machine.

You can now continue with Passing parameters to Jetty 7 via JNDI.