JNDI without an app server? part 2

In our last episode, I was trying to find a way to do JNDI without an app server, so that we could run our integration tests locally — not in an app server environment — without having to change our production Spring bean files.

The mock JNDI blurb in the Spring 2.5 manual’s Testing chapter led me to the ExpectedLookupTemplate class.  I decided to see if ExpectedLookupTemplate could help us with integration testing.  What I found was…

We can do it…

We can indeed inject an ExpectedLookupTemplate into our JndiObjectFactoryBean and have it return the bean of our choice when that JNDI name is used.  First, the bean file showing the wiring:

The Spring Bean File

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
        <constructor-arg index="0" value="vm://localhost" />
    </bean>

    <bean id="queueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:activemq/QueueConnectionFactory" />
<property name="jndiTemplate">
            <bean class="org.springframework.mock.jndi.ExpectedLookupTemplate">
                <constructor-arg index="0" value="java:activemq/QueueConnectionFactory" />
                <constructor-arg index="1" ref="pooledJmsConnectionFactory" />
            </bean>
        </property>
    </bean>
</beans>

Notice that in our JndiObjectFactoryBean (line 12) we override the jndiTemplate property, specifying an ExpectedLookupTemplate (line 15) that returns our pooledConnectionFactory bean instance.  So we would replace the line:

<jee:jndi-lookup id="queueConnectionFactory" jndi-name="java:activemq/QueueConnectionFactory" />

with code like the above for integration testing.

…But does it buy us anything?

So we have a fake JNDI lookup working… but we could have gotten the same result without the JNDI shenanigans by dispensing with the JndiObjectFactoryBean bean and renaming our pooledConnectionFactory bean to just be the queueConnectionFactory directly (replacing lines 8-20 in the original beans file):

	<bean id="queueConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
		<constructor-arg index="0" value="vm://localhost" />
	</bean>

About the only thing the fake JNDI lookup buys us here is that in real life the queue name would be a variable, e.g. ${com.example.research.queue.name} — and by using the fake JNDI lookup we’d be making use of that variable name, and if it wasn’t in the properties file we’d see the error at integration test time.  It doesn’t seem like that’s a good enough reason though.  Without an outside-the-app-server JNDI server, it looks like we probably would replace our JNDI lookups with a bean for what would have been looked up.

Note: I’m not saying the ExpectedLookupTemplate doesn’t work as intended —  I think its purpose is really to make code that uses Spring’s JndiTemplate class directly, unit-testable  (as this post on the Spring forums also suggests).

Next: What about Bitronix Transaction Manager as a fake JNDI server and outside-the-app-server transaction manager?

Other Files for the Test

Other files needed to run this test are the pom.xml file and the unit test:

The POM file

<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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.research.jndi</groupId>
    <artifactId>fake-jndi</artifactId>
    <name>Fake JNDI example</name>
    <version>1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-mock</artifactId>
            <version>2.0.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>2.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>2.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-core</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

The Unit Test

package com.example.research.jndi;

import static org.junit.Assert.assertSame;

import org.apache.activemq.pool.PooledConnectionFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class FakeJndiTest {
    @Test
    public void TestFakeJndi() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/example/research/jndi/spring-beans.xml");
        Object bean = context.getBean("queueConnectionFactory");
        PooledConnectionFactory connectionFactory = (PooledConnectionFactory)context.getBean("pooledJmsConnectionFactory");
        assertSame(bean,connectionFactory);
    }
}


Advertisements

,

  1. #1 by Bobby on May 29, 2009 - 3:12 am

    Hi Daniel, I am trying to do something similar. If I can give you some context, basically I am moving my projects from ANT to Maven. Now in ANT I was able to have 2 datasources IE: 1 that gets shipped with the jar that does a JNDI lookup and 1 that defines a datasource to a local db. Now I am attempting for my test code(since they are all failing since there is no JNDI server with a datasource) to register a datasource in the test-app-ctx which will define the datasource and register it with spring JndiObjectFactoryBean like so:

    ${jdbc.url}

    ${jdbc.driver}

    ${jdbc.username}

    ${jdbc.password}

    org.springframework.mock.jndi.SimpleNamingContext

    However I keep receiving an NoInitialContext exception. Any thoughts how I can get the ds to load from the mock jndi entry?

    Many thanks

  2. #2 by danielmeyer on May 29, 2009 - 8:28 am

    Bobby,

    When I got a NoInitialContext exception it was because I was missing a jndi.properties file in the root of the classpath, pointing to the (in my case) BitronixInitialContextFactory. See the blowing up in test mode post for how I set up jndi.properties and other configuration settings.

    I’m surely not an expert – I hope my fiddlings around can be of some use to you.

  1. JNDI Tutorial With Spring JMS JNDI Example « Alan’s Script

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s