Michael R. Head ([info]suppressingfire) wrote,
@ 2009-05-25 00:51:00
Previous Entry  Add to memories!  Tell a Friend  Next Entry
Entry tags:eclipse, java

Accessing the java:comp/env JNDI lookup context outside of a J2EE container
I was recently modifying some custom JDBC code that is meant to work inside of Tomcat where a JNDI naming context has been setup with a reference for a javax.sql.DataSource. In other words, somewhere in the data layer, we have code like this:


Context initContext = new InitialContext();
DataSource dataSource = (DataSource) initContext.lookup("java:comp/env/jdbc/db1");

And somewhere in the container configuration (in this case Tomcat's context.xml), we have something like this (obviously the IP address, username and password have been made up):

<Resource name="jdbc/db1" auth="Container" type="javax.sql.DataSource" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://sql.example.com:1433" username="dbuser" password="dbpass" maxActive="20" maxIdle="10" maxWait="-1" />


The modification was to create a main() method that can use the same code from the command line. One option is just to “fix” the above code and remove to dependence on the on JNDI. While that is probably the most straightforward approach, I found it undesirable in this case to modify the existing code.

It took me some time to figure out how to mimic what Tomcat does with the above Resource tag. It's not terribly complicated, but it wasn't obvious to me, and I ran past a large number of “Name java: is not bound in this Context” when trying to bind the name "java:comp/env/jdbc/db1" and javax.naming.NoInitialContextExceptions before I understood how the javax.naming package is meant to work.

After some trial and error and reading through some of the Tomcat sources, I was able to put together some working code. The first trick is to create the data source, which will differ slightly depending on the specific database and JDBC driver used:

private static DataSource createDataSource() {
SQLServerDataSource ds = new SQLServerDataSource();
ds.setURL("jdbc:sqlserver://sql.example.com:1433");
ds.setUser("dbuser");
ds.setPassword("dbpass");
return ds;
}

And the following sets that data source into the context so it can be found by the unmodified JDBC code above.

private static void setupNamingContext(DataSource ds) throws NamingException {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, javaURLContextFactory.class.getName());
Context ctx = new InitialContext();
Context javaCtx = ctx.createSubcontext("java");
javaCtx.createSubcontext("comp").createSubcontext("env").createSubcontext("jdbc").bind("db1", ds);
ctx.bind("java:", javaCtx);
}

And then in main, I call these directly:

public static void main(String[] args) throws NamingException {
setupNamingContext(createDataSource());
...
}


So far, I've just run the code from within an Eclipse project that was already configured as a Dynamic Web Project configured to run with Tomcat, so all the main jars from Tomcat are in the classpath as well as the jar for the JDBC driver. I did specifically need to add tomcat-juli.jar to the build path of the project to avoid an exception when first accessing the NamingContext:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory


In conclusion, the above few lines of Java should allow the use of code written with the expectation that it will run within a servlet container, with a JNDI naming context with resources in the “java:comp/env” context, to be called from a standalone main() method without the entire servlet engine up and running.



(6 comments) - (Post a new comment)

Run JNDI as stanalone
(Anonymous)
2009-11-19 12:14 pm UTC (link)
Hi Michael,

I was gone through with your article. And i'm interested to knwo how can we do this
" just to “fix” the above code and remove to dependence on the on JNDI".

Thanks

(Reply to this) (Thread)

Re: Run JNDI as stanalone
(Anonymous)
2009-11-19 12:46 pm UTC (link)
Added to the above i tried this solution passing the datasource to the setupNamingContext.
I'm using myeclipse IDE to run this.
Still the same problem persists.
What am i missing? any jars are required?
Any help is appreciated.

(Reply to this) (Parent)(Thread)

Re: Run JNDI as stanalone
[info]suppressingfire
2009-11-19 04:20 pm UTC (link)
You'll probably need most of the tomcat jars in your class path. Note well this sentence:

So far, I've just run the code from within an Eclipse project that was already configured as a Dynamic Web Project configured to run with Tomcat, so all the main jars from Tomcat are in the classpath as well as the jar for the JDBC driver.

(Reply to this) (Parent)

Re: Run JNDI as stanalone
[info]suppressingfire
2009-11-19 04:16 pm UTC (link)
by "fix" the code, I mean to remove all users to JNDI classes such as Context/InitialContext. In that case, you might do the usual thing of storing the database connection information (URL, Driver class name, username, password) in a .properties file and directly connect using DriverManager.getConnection() per usual resources.

(Reply to this) (Parent)(Thread)

Re: Run JNDI as stanalone
(Anonymous)
2009-11-20 05:35 am UTC (link)
OK. Thanks for the clarification Michael

(Reply to this) (Parent)

Run JNDI as stanalone
(Anonymous)
2009-11-19 12:14 pm UTC (link)
Hi Michael,

I was gone through with your article. And i'm interested to know how can we do this
" just to “fix” the above code and remove to dependence on the on JNDI".

Thanks

(Reply to this)


(6 comments) - (Post a new comment)

Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…