Unfortunately Java EE specs does not specify any standard way of JNDI naming conventions, hence most of the application servers have their own way of JNDI naming. On specifying a Datasource's JNDI name as 'jdbc/myDatasource', Tomcat (6) binds that as 'java:/comp/env/jdbc/myDatasource' while JBoss (5) binds as 'java:/jdbc/myDatasource'. So if one wants to deploy the application on multiple application servers then at least JNDI names has to be changed. I was wondering if Spring Framework has a solution for this, so tried to do lookup using <jee:jndi-lookup ...> as shown below-
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatasource" resource-ref="true"/>it works well with Tomcat but fails on JBoss. Reason, the JNDI prefix is hard coded in Spring to 'java:comp/env/'.
package org.springframework.jndi;
public abstract class JndiLocatorSupport extends JndiAccessor {
/** JNDI prefix used in a J2EE container */
public static final String CONTAINER_PREFIX = "java:comp/env/";
. . .
}So it is obvious that it will fail on JBoss. To overcome this limitation, I came up with following solution-public final class ServiceLocator {
private static final Map<String, Object> services = new ConcurrentHashMap<String, Object>();
private static ServiceLocator instance;
private static Context context;
static {
try {
Context initContext = new InitialContext();
if (ServerDetector.isJBoss()) {
context = (Context) initContext.lookup("java:");
} else if (ServerDetector.isTomcat()) {
context = (Context) initContext.lookup("java:/comp/env");
} else {
context = initContext;
// or add more 'else if' blocks according to servers to be supported
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public DataSource getDataSource(String name) throws Exception {
if (name == null || name.length() <= 0)
throw new IllegalArgumentException("name");
if (services.containsKey(name))
return (DataSource) services.get(name);
DataSource ds = (DataSource) context.lookup(name);
services.put(name, ds);
return ds;
}
}Here ServerDetector is a good utility class I found in Liferay code base. Now ServiceLocator takes care of application server specific JNDI prefixes and avoids hassles of changing configuration or code to deploy it on a specific server. In case of Spring instead of using <jee:jndi-lookup ...> one can do the lookup using above-mentioned ServiceLocator as shown below-
<bean id="serviceLocator" class="com.vinodsingh.ServiceLocator" factory-method="getInstance" /> <bean id="dataSource" factory-bean="serviceLocator" factory-method="getDataSource"> <constructor-arg value="jdbc/myDatasource" /> </bean>Now entire code (including configuration files) becomes truly portable, at least for JNDI lookups :-)