I was about to compare Message Driven Beans (MDB) with Spring JMS and started to setup a small application with an MDB and a servlet. Messages should be sent from the servlet to a ActiveMQ queue and the MDB should then receive them. The application was to be deployed on Glassfish v3 (3.1.2.2) I ran into a few problems which I will document for you to find in this blog post.

First I created my application using the built-in JMS broker shipped with Glassfish v3 (OpenMQ). This was quite straight forward and worked nicely.

Then I downloaded ActiveMq 5.7.0 and started a broker from the installation dir:

  1. bin/activemq start
  2. Tail log: tail -f data/activemq.log
  3. Load admin console: http://localhost:8161/admin/

To switch over to ActiveMQ as a JMS broker, I followed the steps described in this post by Geert Shuring. Versions are updated to current of today:

  1. Copy the following jars from ActiveMQ lib and lib/optional folders to the Glassfish lib folder: slf4j-api-1.6.6.jar, slf4j-log4j12-1.6.6.jar and log4j-1.2.17.jar.
  2. Download activemq-rar-5.7.0.rar from here and install as a Resource adapter using Glassfish admin console (http://localhost:4848/). (Application type is automatically detected by Glassfish)
  3. Create a new config, using Glassfish admin console, under Resources -> Resource adapter configs:
    • Resource adapter name = activemq-rar-5.7.0
    • Thread pool id = thread-pool-1
    • Additional properties untouched! (defaultUser / defaultPassword / tcp://localhost:61616 etc)
  4. Create a new Connector connection pool using admin console under Resources -> Connectors -> Connector Connection Pools with:
    • Pool name = amqpool
    • Resource adapter = activemq-rar-5.7.0
    • Connection definition = javax.jms.ConnectionFactory
  5. Create a new Connector Resource using admin console under Resources -> Connectors -> Connector Resources with:
    • JNDI name = amqres
    • Pool name = amqpool
  6. Create a new Admin Object Resource using asadmin tool. Don’t create the resource from the admin console! It caused me problems where the resource could not be found using JNDI. (This is most likely a bug in the admin console.)
    • Start asadmin from Glassfish bin folder
    • Execute command create-admin-object –restype javax.jms.Queue –raname activemq-rar-5.7.0 –property PhysicalName=incomingAmq incomingAmq
    • Check the new resource in the admin console
  7. Now the Glassfish configurations should be ready.

You can check the Connector Connection Pool being connected to ActiveMQ by clicking Ping button on the amqpool object.

After the configurations are ready, it’s time to write some code. These are the important files for the application:

SenderServlet.java:

package dag;

import javax.annotation.Resource;
import javax.jms.*;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SendServlet extends HttpServlet {
@Resource(mappedName = "amqres")
private ConnectionFactory connectionFactory;

@Resource(mappedName = "incomingAmq")
private Queue incoming;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  String messageText = request.getParameter("x");
  ServletOutputStream out = response.getOutputStream();
  out.println("<html><body>");
  out.println("<p>ConnectionFactory: "+connectionFactory+"</p>");
  out.println("<p>Queue: "+ incoming + "</p>");
  out.println("<p>Servlet sending '"+messageText+"' to queue.</p>");

  Connection connection;
  try {
    connection = connectionFactory.createConnection();
    Session session = connection.createSession(false,
    Session.AUTO_ACKNOWLEDGE);
    MessageProducer messageProducer = session.createProducer(incoming);
    TextMessage message = session.createTextMessage();
message.setText(messageText);
    messageProducer.send(message);
  } catch (JMSException e) {
    throw new RuntimeException(e);
  }
  out.println("Done sneding.");
  out.flush();
}
}

MessageReceiverBean.java

package dag;

import javax.ejb.*;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

@MessageDriven(
  activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "incomingAmq")
  }
)
public class MessageReceiverBean implements MessageListener {
  public MessageReceiverBean() {
  }

  @Override
  public void onMessage(Message message) {
    try {
      System.out.println("Received: "+((TextMessage)message).getText());
    } catch (JMSException e) {
      throw new RuntimeException(e);
    }
  }
}

glassfish-ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
 <enterprise-beans>
 <ejb>
 <ejb-name>MessageReceiverBean</ejb-name>
 <mdb-resource-adapter>
 <resource-adapter-mid>activemq-rar-5.7.0</resource-adapter-mid>
 </mdb-resource-adapter>
 </ejb>
 </enterprise-beans>
</glassfish-ejb-jar>

(ejb-jar.xml should not be needed)

Create an ear package and deploy to Glassfish. Go to http://localhost:8080/web/servlet?x=theOatmeal to test the application. What is given in request parameter x should be sent to the JMS queue ‘incomingAmq’ and the MDB should then receive this message from ‘incomingAmq’ and print it on stdout. The physical queue ‘incomingAmq’ will be automatically created by ActiveMQ. Take a look at the AMQ admin console: http://localhost:8161/admin/queues.jsp

Possible problems that may occur:

  • When invoking the SenderServlet you get an error in the log like “javax.naming.NameNotFoundException: incomingAmq not found”, where “incomingAmq” is the resource mappedName in the SernderServlet. This may be caused by creating the Admin Resource Object “incomingAmq” from the admin console. Create it using asadmin as described above! Stacktrace may look like:
javax.naming.NameNotFoundException: incomingAmq not found
	at com.sun.enterprise.naming.impl.TransientContext.doLookup(TransientContext.java:248)
	at com.sun.enterprise.naming.impl.TransientContext.lookup(TransientContext.java:215)
	at com.sun.enterprise.naming.impl.SerialContextProviderImpl.lookup(SerialContextProviderImpl.java:77)
	at com.sun.enterprise.naming.impl.LocalSerialContextProviderImpl.lookup(LocalSerialContextProviderImpl.java:119)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:505)
	at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:455)
	at javax.naming.InitialContext.lookup(InitialContext.java:392)
        ...
  • If you get the following NullPointerException when invoking the servlet, you may be missing the PhysicalName property on the Admin Resource object ‘incomingAmq’. It should be included in the object if created using the asadmin command above. Otherwise you can add the property using the admin console.
java.lang.NullPointerException
	at org.apache.activemq.command.ActiveMQDestination.hashCode(ActiveMQDestination.java:329)
	at java.util.HashMap.get(HashMap.java:300)
	at org.apache.activemq.openwire.OpenWireFormat.getMarshallCacheIndex(OpenWireFormat.java:517)
	at org.apache.activemq.openwire.v9.BaseDataStreamMarshaller.tightMarshalCachedObject1(BaseDataStreamMarshaller.java:158)
	at org.apache.activemq.openwire.v9.ProducerInfoMarshaller.tightMarshal1(ProducerInfoMarshaller.java:98)
	at org.apache.activemq.openwire.OpenWireFormat.marshal(OpenWireFormat.java:234)
	at org.apache.activemq.transport.tcp.TcpTransport.oneway(TcpTransport.java:183)
        ...

There is a section in the WebSphere Admin console called WebSphere variables (located under Environment), where you can … manage WebSphere variables (WAS variables). But how can one use these WAS variables within a JavaEE application? There is a proprietary way to load them which I will not mention here.

A better way in my opinion is to bridge the WAS variable to an environment variable. Then it can simply be accessed through System.getProperty(“varName”); It’s also easy to access the variable on Tomcat without touching the code.

To bridge a WAS variable:

  1. Log on to your WAS console.
  2. Add a WAS variable “myapp.myVar” under Environment -> WebSphere variables
  3. Go to (something like) Servers -> Server Types -> Websphere application servers -> YOUR_SERVER -> Java and process management -> Process definition -> Java virtual machine -> Custmo properties
  4. Define a new variable myapp.myVar = ${myapp.myVar}
  5. Save and restart.

The value of myapp.myVar can now be obtained: System.getProperty(“myapp.myVar”)

Setting up a Java EE project with IntelliJ is a trivial task. Getting a multi-module setup to work properly with quick deployments and maven builds is not at all a trivial task.

My requirements:

  • A multi-module project with at least a web module, an ejb module and a common module with shared code to be used by ejb/web
  • Maven building ear file.
  • Easy deployment to local app server (Glassfish and Websphere Application Server)
  • Quick compiles and hot swapping of recompiled code into running app.
  • EJB 3.0 (not 3.1)

Download attached  example project and have a look for what it’s worth.

This is how I created my project using IntelliJ IDEA 10 Ultimate edition:

1. Create modules:

  • Create EJB module named ‘ejb’ and add EJB facet. Don’t “Fix” javaee.jar yet!
  • Create Web module named ‘web’ with Web facet. Don’t create any artifact yet. Move and rename web folder within web module! Give it name ‘webapp’ and place it under src/main.
  • Create EAR module named ‘ear’ with JavaEEApplication facet. Don’t create any artifact yet.
  • Create common module named ‘domain’ (or whatever it’s going to contain)

2. Edit root pom.xml and module pom files.

Root pom.xml should look something like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>dag</groupId>
    <artifactId>mavenear</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <modules>
        <module>domain</module>
        <module>ejb</module>
        <module>web</module>
        <module>ear</module>
    </modules>

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>http://download.java.net/maven/2/</url>
            <layout>default</layout>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>javaee</groupId>
            <artifactId>javaee-api</artifactId>
            <version>5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

Domain module pom.xml should look like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>dag.mavenear2</groupId>
    <artifactId>domain</artifactId>
    <packaging>jar</packaging>

</project>

EJB pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>dag.mavenear2</groupId>
    <artifactId>ejb</artifactId>
    <packaging>ejb</packaging>

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>http://download.java.net/maven/2/</url>
            <layout>default</layout>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>dag.mavenear2</groupId>
            <artifactId>domain</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ejb-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <ejbVersion>3.0</ejbVersion>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Web pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear2</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>dag.mavenear2</groupId>
    <artifactId>web</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>domain</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>dag.mavenear2</groupId>
            <artifactId>ejb</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>web</finalName>
    </build>
</project>

EAR pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mavenear</artifactId>
        <groupId>dag</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>dag.mavenear2</groupId>
    <artifactId>ear</artifactId>
    <packaging>ear</packaging>
    <dependencies>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>ejb</artifactId>
            <version>1.0</version>
            <type>ejb</type>
        </dependency>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>web</artifactId>
            <version>1.0</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>dag.mavenear</groupId>
            <artifactId>domain</artifactId>
            <version>1.0</version>
            <type>jar</type>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-ear-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <modules>
                        <webModule>
                            <groupId>dag.mavenear</groupId>
                            <artifactId>web</artifactId>
                        </webModule>
                        <ejbModule>
                            <groupId>dag.mavenear</groupId>
                            <artifactId>ejb</artifactId>
                        </ejbModule>
                        <jarModule>
                            <groupId>dag.mavenear</groupId>
                            <artifactId>domain</artifactId>
                        </jarModule>
                    </modules>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3. Now write some code:

Common module: Create a class as follows:

public class Common {
    public String sayHey() {
        return "Common says hey!";
    }
}

For EJB module: Let IntelliJ create the EJB stub by adding a stateless session bean to main src. Name it ‘Dag’ and add the following method to the bean:

@Stateless(name = "DagEJB")
public class DagBean implements Dag {
    public DagBean() {
    }

    @Override
    public String sayHey() {
        Common common = new Common();
        return "EJB says hey. Via EJB: " + common.sayHey();
    }

}

Extract an interface named ‘Dag’ with the single method sayHey().

For Web module:

Let IntelliJ IDEA create a servlet stub and name it ‘DagServlet’. IntelliJ edits web.xml for you but you need to add a servlet mapping. This can be done using a IntelliJ feature or add the following to web.xml:

<servlet-mapping>
    <servlet-name>DagServlet</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
</servlet-mapping>

Add the following code to the servlet:

@EJB(name = "DagEJB")
private Dag ejb;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ServletOutputStream out = response.getOutputStream();
    out.println("<html><body>");
    out.println("<p>Servlet says hey.</p>");
    Common common = new Common();
    out.println("<p>" + common.sayHey() + "</p>");
    out.println("<p>" + ejb.sayHey() + "</p>");
    out.println("</body></html>");
    out.flush();
}

4. Build with maven

Now it should be able to build an ear file using Maven, assuming Maven is setup correctly, Run the 'package' goal in the root pom.xml and check the created ear-1.0.ear in folder .../ear/target
Deploy the ear file on Glassfish and go to http://localhost:8080/web/servlet

5. Deploy from IntelliJ

Now that we know the app works as wanted it’s time to setup deployment from within IntelliJ IDEA.

Chances are good that IntelliJ at this point has created a number of artifacts for you. Setup a Glassfish configuration and select artifact ‘ear:ear exploded’ for deployment.

Edit application.xml in ear module and set web context root to /web:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd"
             version="5">

    <module>
        <ejb>ejb-1.0.jar</ejb>
    </module>
    <module>
        <web>
            <web-uri>web.war</web-uri>
            <context-root>/web</context-root>
        </web>
    </module>
</application>

Perhaps you will need to remove artifact output folder …/ear/target/ear-1.0 if maven has built to this folder before.

Start the Glassfish server in debug mode and go to http://localhost:8080/web/servlet You will most likely end up with a ClassNotFoundException. The problem is missing MANIFEST.MF files in the web and ejb module. These can be added from the artifact view in IntelliJ:

1. Navigate to artifact “ear:ear exploded” and select ejb-1.0.jar. There should be a “Create Manifest…”-button below. Click it and select the ejb module root.

2. Insert “domain-1.0.jar” to the Class-Path field.

3. Repeat for the web:war artifact. The META-INF should be located in the webapp folder next to WEB-INF.

Potholes along the road:

  • At some point I had a web.xml without servlet API version (2.5). This lead to Glassfish not being able to detect my annotated EJB:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
...
</web-app>

Resources:

Maven EAR plugin: http://agoncal.wordpress.com/2009/10/23/because-i-always-forget-how-to-use-maven-ear-plugin/

Classloader and EAR structure: http://www.technicalfacilitation.com/get.php?link=classloading

I ran into a stupid problem yesterday when I was trying to setup Spring + Hibernate with HSQLDB. Everything looked fine except it didn’t work… The error I got was the following:

org.springframework.jdbc.BadSqlGrammarException: Hibernate operation: could not insert: [com.company.domain.Payment]; bad SQL grammar [insert into Payment (paymentId, customerId, orderId, paymentReferenceNumber, sum, validTransactionDate) values (null, ?, ?, ?, ?, ?)]; nested exception is java.sql.SQLException: Table not found in statement [insert into Payment (paymentId, customerId, orderId, paymentReferenceNumber, sum, validTransactionDate) values (null, ?, ?, ?, ?, ?)]
The error might be obvious to the reader, but it was not for me… A column can’t be named ‘sum’ –  it’s a reserved word in SQL.
Hibernate has the ability to autocreate tables for you but it said eaxactly nothing about failing to do so.

I’m on a new project and have the opportunity to make things right from the beginning. I’ve been trying to include version number and build number into the packaged WAR file. As a bonus I’ve made the version available through a web page like:

Version: MyApp-1.0 (build number 7)

My project is a web service built with maven. TeamCity (5.1.1) is used for building. I use IntelliJ IDEA version 9 as my IDE. With version number I refer to the version in my pom.xml, for instance:

    <version>1.0</version>

Build number refers to the automatically incremented build number in TeamCity.

These are the steps I took:

1. In the TeamCity Build configuration, make sure Build number format includes {0}.

2. Create/edit your project file src/main/resources/application.properties:

    version=${pom.name}-${pom.version} (build number ${build.number})

pom.name and pom.version are Maven attributes defined in the beginning of your pom.xml. build.number is an environment variable set by TeamCity at build time.

3. Edit pom.xml:

<properties>
<!--
When building on TeamCity this property is set as an environment variable by TeamCity and then maven replaces the placeholder in application.properties.
When compiling locally we have to set this property here or Spring will fail to resolve the placeholder in application.properties. This works in IntelliJ (only?) with its feature to filter maven resources.
A cleaner solution is to set up an envrionment variable in the IDE with this name, but this requires new developers to configure the IDE.
-->
  <build.number>Unknown/local build</build.number>
</properties>
...
<resources>
  <resource>
    src/main/resources
    <filtering>true</filtering>
  </resource>
</resources>

This will make Maven replace the variables in application.properties with maven properties and environemnt variable available at build time.

4. Now the packaged application.properties will contain current build number from TeamCity and maven artifact name and version from your pom.xml !

As a bonus I made the version available via a jsp called status.jsp. Getting the version property from application.properties into a jsp can be made in several ways of course. Since my project don’t contain any web stuff (only web services) I created the jsp in a hacky way:

<html><body>
  <p>Version: <%= BusinessServiceImpl.instance.version %></p>
</body></html>

And BusinessServiceImpl:

@Autowired
public String version;
public static StaffDiscountServiceImpl instance;

public StaffDiscountServiceImpl(} {
  instance = this;
}

The version attribute is autowired and defined in Spring’s applicationContext.xml:

<context:property-placeholder location="classpath:/application.properties "/>
<bean id="version" >
  <constructor-arg value="${version}" />
</bean>

There’s certainly better ways of doing this jsp stuff (comments, please!) but this was simple and it works.

The last couple of days I’ve been looking into ways of configuring a JEE application. A common problem is how to configure your application for different environments (local dev machine, test machine, integration tests, production). This can be done in different ways including:

  • Fetching configuration from the database.
  • Running different post compile scripts for the various environments.
  • Shipping different properties files along the main package (WAR or EAR)

The database url/datasource is often retrieved from the web server or app server using JNDI.

A nice approach is to store not only datasources, but also other properties on the app server. On Websphere Application Server (WAS) version 6 and 7 (at least) there are different types of resources that can be configured: jdbc datasources, jms, urls and a few more.

I had the need to configure a remote webservice endpoint as a resource and the URL type on WAS felt natural but it also had to work nicely on Tomcat during development. Setting up a URL resource on WAS is straight forward: give it a name, a JNDI name and the actual URL.  Accessing this JNDI resource can be done in different ways, but I’ll show the way which also works on Tomcat:

1. Edit your web.xml and add something like:

<resource-ref>
 <res-ref-name>
   url/SomeService
 </res-ref-name>
 <res-type>
   java.net.URL
 </res-type>
 <res-auth>
   Container
 </res-auth>
</resource-ref>

2. Instruct Tomcat to setup an object accessible over JNDI. Edit your app’s META-INF/context.xml or $CATALINA_HOME/conf/server.xml if you want to setup a system wide resource:

<Resource name="url/SomeService" auth="Container"
 type="java.net.URL"
 factory="com.mycompany.common.URLFactory"
 url="http://www.someservice.com/service" />

3. Implement a URLFactory used by Tomcat to create URL objects on demand:

import java.net.URL;
import java.util.Hashtable;
import javax.naming.*;
import javax.naming.spi.ObjectFactory;

public class URLFactory implements ObjectFactory {
 public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception {
 Reference ref = (Reference) obj;
 String urlString =  (String) ref.get("url").getContent();
 return new URL(urlString);
 }
}

4. Write some application code to fetch your Resource:

InitialContext context = new InitialContext();
URL url = (URL) context.lookup("java:comp/env/url/SomeService");

Using this approach it’s possible to build one WAR or EAR and install it wherever you want. The different environments hold their own configuration within the app server. Since Tomcat is only used for local development, the resources could be stored as application specific resources in META-INF/context.xml, but it’s also possible to keep resources as a per-environment basis using Tomcat’s conf/server.xml

It’s also possible to write your own class as a resource (instead of using DataSource, URL and so on). This is quite easy on Tomcat, see Apache Tomcat 6.0 – JNDI Resources HOW-TO, but takes some pain to setup on WAS. Here’s an old article on how to get it done on WAS 5, but it works similarly on newer releases:

Resources:
Apache Tomcat 6.0 – JNDI Resources HOW-TO: