JNDI Hell

“Java Naming and Directory Interface” is an API to access directories using a hierarchical model. Most Java developer never use it but for JavaEE application that extensively use it.
 

Theory

For an application to use a server managed resource (for sample a DataSource), the later has to be exposed to application in JNDI. To avoid hard-coding the resource name, JavaEE defines resource references. In application descriptor, a let you declare a local name for a resource your application depends on. 
 
From a JavaEE component (webapp, or EJB for full profile JavaEE), you can access the local JNDI context as java:comp/env.
  • “java” as root specifier, as you can use the same JNDI API to access an LDAP directory
  • “comp” as JavaEE “component”
  • “env” as environment

 

Java EE 7 introduced new JNDI scopes, but most of us will only use java:comp/env.
 
During deployment, the application server has to provide a mechanism to bind this reference to an actual managed resource. So you can bind a managed DataSource “jdbc/monitoredOracleDb” to component resource reference “jdbc/mydb”.

Practice

The bad news is that this binding is not standardized, and we end with a container-specific descriptor: WEB-INF/jboss-web.xml, WEB-INF/weblogic-web.xml, etc. Even worst, such a descriptor has to be included in the WAR, so you have to know all details for your production environment and hard-code them in this extra descriptor. You can’t deploy the same artifact on staging and production until they use the same internal names for resources, etc.
 
Then starts JNDI hell. For some obvious reason, when you ask Tomcat JNDI for “jdbc/foo” it automatically generates a new DataSource, without any configuration, and you end up with a mysterious “Cannot create JDBC driver of class ” for connect URL ‘null’”. 
 
JBoss has it’s own way to understand the spec: your application has to use “jdbc/mydb” as JNDI name. If you use “java:comp/env/jdbc/mydb” as standard tells you it won’t find it.
 
So, most of us end by defining internal resources with the exact same name used in webapp to avoid such issues. This makes completely useless…

SDK binding

.. until you use CloudBees SDK. 
 
SDK let you bind a database to your application and declare an alias for it :
 
bees app:bind -db myaccount/mydatabase -a myaccount/myapp -as db
 
The magic part is the latest parameter “as”. Application will then get a DataSource exposed in JNDI context as “jdbc/db”, you even don’t need a in most application servers.
 
This approach just make JNDI work for Java EE managed resources as it was supposed to.
 
 
note: this picture from Antonio Goncalves, Java EE expert group member, is absolutely not related to this blog post and was included without his agreement :P
 
Nicolas De Loof
CloudBees
 
 
Blog Categories: 

Add new comment