Today I’ve moved a bunch of my Grails apps over to JNDI Data Sources, and while it’s fresh in my mind, I thought I’d document a few of the tips and tricks involved.
First of all, why would you even want to use JNDI data sources? A few good reasons:
So what changes have I had to make? Just a few tiny ones to DataSource.groovy. Here’s the new one for groovyblogs:
dataSource {
pooled = false // JNDI, baby. All the way...
}
// environment specific settings
environments {
development {
dataSource {
// check out /web-app/WEB-INF/jetty-env.xml for the details
dbCreate = "update"
jndiName = "java:comp/env/jdbc/groovyblogs"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:hsqldb:mem:testDb"
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
}
}
production {
dataSource {
dbCreate = "update"
jndiName = "jdbc/groovyblogs"
}
}
}
First of all, I need to turn pooling off, since the container will now handle all of that for me. But how come the settings are different for DEV and PROD? The answer lies in how containers handle JNDI referencing.
In the DEV example above, I’m running Jetty to do all of my local PC dev work (ie. the standard container that ships with Grails). Embedded Jetty needs to have JNDI references specified using ENC naming (java:comp/env/jdbc/blah), which is resolves against a container specific file to find out the “real” underlying database settings. Enter our friend /web-app/WEB-INF/jetty-env.xml. For groovyblogs, that looks something like:
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
<New id="domainDb" class="org.mortbay.jetty.plus.naming.Resource">
<Arg>jdbc/groovyblogs</Arg>
<Arg>
<New class="org.postgresql.ds.PGSimpleDataSource">
<Set name="user">username</Set>
<Set name="password">password</Set>
<Set name="databaseName">groovyblogs</Set></New>
</Arg>
</New>
</Configure>
As you can see, because of the way the JNDI resource gets resolved in Jetty, I still need to bundle my postgresql driver in /lib. Alas, but I can at least still test things out locally. Notice that in my DataSource I specify things as java:comp/env/jdbc/groovyblogs, but in jetty-env.xml it needs to be jdbc/groovyblogs. You’ve been warned.
But what about production settings? In my case, I’m deploying to glassfish, so I just need to setup my jdbc connection pool and connection under the “Resources” section of the Glassfish Web Admin tool and I’m in business. Gives me a wealth of options for pool sizes, polling of connections and all that good gear – I can even “ping” the database from the admin tool to make sure it’s configured correctly. Most importantly for me, it gives me a single place for my database name, username and password. I can also do away with the ENC indirection, and just reference the JNDI name directly (jdbc/groovyblogs).
Enough JNDI for one day… Time to head off to jConsole and see what I might be able to monitor about the connection pool *while* the app is running…
Hello,
that’s interesting. But can I deploy a war file with this configuration in a tomcat server?
Gero
Thanks Glen,
for sharing your info, very smart solution, exactly fits my needs (GlassFish+PostgreSQL) and can easily be adopted to other deployment scenes (e.g. add serverName to jetty-env.xml in order to access remote servers).
Regards,
Reiner
Hi!
When i follow your instructions i get following exception when trying to run grails with jndi
java.lang.IllegalStateException: No Constructor:jdbc/data
test test data
on org.mortbay.jetty.webapp.WebAppContext@1747e0f{/wezi,D:grailsWorkspacewezi/web-app}
This is a pretty old post, but it’s the best description I’ve found about getting Grails to work with JNDI for use with Jetty. Unfortunately, I can’t get it to work. My JNDI configuration for the production environment works fine under Tomcat, but when I try to enable JNDI for the test environment (grails test-app), my functional tests blow up complaining with a “javax.naming.NameNotFoundException; remaining name ‘env/myDataSource’”.
I’ve created a jetty-env.xml file for hsqldb that looks like the following:
< ?xml version="1.0"?> < !DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
jdbc/myDataSource
org.hsqldb.jdbcDriver
jdbc:hsqldb:mem:devDB
sa
org.enhydra.jdbc.xapool
and I’ve changed my DataSource.groovy to include the following:
test { dataSource { // see web-app/WEB-INF/jetty-env.xml for JNDI settings dbCreate = "update" jndiName = "java:comp/env/myDataSource" pooled = false // JNDI provides pooling }As I’ve floundered around, I’ve also tried adding a section to the generated web.xml, and added enable.jndi=true to BuildConfig.groovy, but I still end up with the same error.
Any idea what I might be doing wrong?
@Tom that looks totally fine with me. Very weird.
As an aside, the Refactor boys have done a more current and detailed write up on this with Tomcat (might be useful since 1.2 has moved to Tomcat for the inprocess web server).
Tom I think you are missing the word”jdbc” in this line:
jndiName = “java:comp/env/myDataSource”
it should say:
jndiName = “java:comp/env/jdbc/myDataSource”
because you named your JNDI datasource like so: “jdbc/myDataSource”
Prefixing “jdbc/” to a logical name is done by convention and is not a requirement.
hello,
when i run app
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’: Cannot resolve reference to bean ‘
dataSource’ while setting bean property ‘dataSource’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean
with name ‘dataSource’: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name jdbc is not bound in this Cont
ext
Very Interesting. It totally work incorrectly for me. I got
java.lang.IllegalStateException: No Constructor: jdbc/data
testtestdata
on org.mortbay.jetty.webapp.WebAppContext@1747e0f{/wezi,D:grailsWorkspacewezi/web-app}
But other case work correctly In Java side.