Friday, September 11, 2009

Spring JNDI Bind Utility for Unit Tests


Spring does provide JndiTemplate class for JNDI lookup through spring configuration. But there is not such convenience class for binding Objects to jndi server through configuration.
Through there are some classes like SimpleNamingContextBuilder that can be used programmatically but not via configuration (at least as it is).

Here I am presenting a utility class that be used to register jndi objects via Spring configuration.
For demo purpose commons-dbcp classes are used as resources to be bound to the jndi server.

The JNDI server used is file based RefFSContextFactory from sun. Any other JNDI Server would also suffice

Prerequisites
spring-core-2.5.6.jar
spring-beans-2.5.6.jar
spring-context-2.5.6.jar
providerutil.jar (referenced from fscontext)
The following jars are required for the demo sample only

I am presenting two version of the JNDI Binder utility class; one extends JndiTemplate and the other one extends InitialContext.


Binder Utility based on Spring's JndiTemplate class: JndiBinderTemplate
package in.spring.jndi.util;

import java.util.Iterator;
import java.util.Map;
import javax.naming.NamingException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jndi.JndiTemplate;

/**
* A Spring specific utility class to register objects to a jndi server through spring configuration
* Sample usage with file based jndi server
*
* Instead of using interfaces InitializingBean and DisposableBean, @PostConstruct and @PreDestroy
* annotations or init-method can be used
* @see org.springframework.jndi.JndiTemplate
* @author Vijesh
*
*/
public class JndiBinderTemplate extends JndiTemplate implements InitializingBean, DisposableBean  {

private Map jndiObjects;

public Map getJndiObjects() {
return jndiObjects;
}

public void setJndiObjects(Map jndiObjects) {
this.jndiObjects = jndiObjects;
// if life cycle interfaces are not available
// call loadJndi(); here
}

@Override
public void afterPropertiesSet() throws Exception {
loadJndi(); 
}


public void loadJndi() {
System.out.println(" start binding Objects to JNDI server ");

if(jndiObjects != null && !jndiObjects.isEmpty()) {

Iterator itr = jndiObjects.entrySet().iterator();

while (itr.hasNext()) {
Map.Entry entry = (Map.Entry) itr.next();

try {
rebind((String) entry.getKey(),entry.getValue());
} catch (NamingException e) {
System.out.println(" Binding "+entry.getKey()+" is failed with error: "+e.getMessage());
}  
}

System.out.println(jndiObjects.size()+" Objects bound to JNDI server ..");
}
}


@Override
public void destroy()  throws Exception {
clearJndi();
}


public void clearJndi() {
System.out.println(" unbind the Objects from the JNDI server  ");

if(jndiObjects != null && !jndiObjects.isEmpty()) {
Iterator itr = jndiObjects.keySet().iterator();

while (itr.hasNext()) {
String jndi = null;
try {
jndi = (String) itr.next();
unbind(jndi);
} catch (NamingException e) {
System.out.println(" Error in unbinding "+jndi);
e.printStackTrace();
}
}
}
}
}

The Spring configuration for the above class with some sample resources used to bind to the JNDI server is:



 


com.sun.jndi.fscontext.RefFSContextFactory
file:/




















Now the look up from the jndi consumer classes using will happen as expected. For example the following code would work perfectly fine.

/**
* Jndi lookup using Standard mechanism
*/
public void testJNDIBinder() {

System.out.println("\n in testJNDIBinder ");

System.setProperty("java.naming.factory.initial", "com.sun.jndi.fscontext.RefFSContextFactory");
System.setProperty("java.naming.provider.url", "file:/");

try {
Context cxt = new InitialContext();

Object obj1 = cxt.lookup("jndi/sDS");
System.out.println(" obj1 "+obj1);
Object obj2 = cxt.lookup("jndi/pDS");
System.out.println(" obj2 "+obj2);

System.out.println(" Look up test is successful.... ");

} catch (NamingException e) {
System.out.println(" Look up test failed.... ");
e.printStackTrace();
}
}


Binder Utility without Spring dependency: JndiBinderContext

The second class is very much similar to the one shown above except for it extends the class InitialContext

package in.spring.jndi.util;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
* A standard utility class to register objects to a jndi server through spring configuration
* Sample usage with file based jndi server
* @see javax.naming.InitialContext
* @author Vijesh
*/
public class JndiBinderContext extends InitialContext {

public JndiBinderContext() throws NamingException {
super(); 
}

public JndiBinderContext(Hashtable env) throws NamingException {
super(env);
}

// Rest is almost same as JndiBinderTemplate class, refer the source code for details



Another main difference is in passing the environment properties. In this case they are passed as constructor arguments (it is so in InitialContext)


    




com.sun.jndi.fscontext.RefFSContextFactory
file:/


















Please note that both the classes should be loaded lazily.

It's really unfortunate that Spring didn't provide a similar utility class. Why didn't it occurred to them when they created JndiTemplate class for lookup purpose?

Download the source (eclipse project)
The dependent libraries listed in the prerequisites should be downloaded separately and added to the project.

Even though I have used particular version of each dependent libraries, it doesn't mean that the utility will work only with that. As you can see, it is a simple class and should work with the earlier releases of the dependent libraries.

3 comments:

jtanzy said...

Gracefully written.
Tahnk you for posting it.

timtish said...

Good idea.
Thanks!

nathan said...

Good way of thinking.
Is there a way to use this tech to bind String objects?
As the binding object has to implements Referenceable, I am not suer how to bind String object using the RefFSContextFactory.
I try to run UT for the code that reference to some JNDI properties (all String objects) and like to find a way to do the UT without the application server running. Can I use the RefFSContextFactory as the JNDI server for that?