I have a problem setting up my configuration for JNDI with Spring. I checked the other posts but could not get my problem solved. I am using Tomcat 6 as my container. From my understanding I need to set up a resource on the server. So in my server.xml
file I have this:
<GlobalNamingResources>
<Resource auth="Container" driverClassName="org.postgresql.Driver"
maxActive="100" maxIdle="5" maxWait="10000"
minEvictableIdleTimeMillis="60000" name="jdbc/myTomcatPool"
password="password" testOnBorrow="true" testWhileIdle="true"
timeBetweenEvictionRunsMillis="10000" type="javax.sql.DataSource"
url="jdbc:postgresql://localhost:5432/postgis" username="postgres"
validationQuery="SELECT 1"/>
</GlobalNamingResources>
I have the following in my spring-context.xml
(which is on the classpath):
<jee:jndi-lookup id="geoCodeData" jndi-name="java:comp/env/jdbc/myTomcatPool" />
<bean id="geoCodeService" class="com.sample.SampleImpl">
<property name="dataSource" ref="geoCodeData"/>
</bean>
I then have this in file META-INF/context.xml
:
<Context path="/myApp" reloadable="true" cacheMaxSize="51200"
cacheObjectMaxSize="2560">
<ResourceLink global="jdbc/myTomcatPool" name="jdbc/myTomcatPool"
type="javax.sql.DataSource"/>
</Context>
My server starts up free of errors.
When I try to run the following test (that worked before I added the JNDI code):
public class Test {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("spring-context.xml");
}
}
I get the following error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'geoCodeData': Invocation of init method failed;
nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
Is my configuration wrong or is the way I am trying to run the test incorrect?
When you run your testcases, you will want to use JDBC instead of JNDI lookup. The simple reason is because you usually don't run your testcases from the application server. Thus, JNDI lookup will fail.
What I do on my end is to place data source in a separate file. I have one file for production that uses JNDI:-
project-datasource.xml
<jee:jndi-lookup id="geoCodeData" jndi-name="java:comp/env/jdbc/myTomcatPool"></jee:jndi-lookup>
... another another file for unit test that uses JDBC:-
project-datasource-test.xml
// use the same bean name "geoCodeData"
<bean id="geoCodeData" class="...">
<property name="driverClassName" value="..." />
<property name="url" value="..." />
<property name="username" value="..." />
<property name="password" value="..." />
</bean>
The web app will use project-datasource.xml whereas the unit test will use project-datasource-test.xml.
After several hours of tearing out my (already sparse) hair I have managed to get my tomcat server to pool database connections to Oracle and use the Spring framework.
I though I would reply to this question even though there seems to be an answer, just in case it helped anybody else.
What I wanted:
A connection pool administered by Tomcat (rather than per servlet) and the config for the DB connection in the Tomcat server config (again, rather than the servlet config files).
We have several instances of Tomcat and each one connects to a specific Oracle DB, but developers are producing servlets that could be required to run on any one of them, hence I don't want the DB connection details in the WAR file they produce, but to let them look it up and be provided with that server's data source, via JNDI.
In the Tomcat server conf/context.xml
I added the following code:
<Resource name="jdbc/banner"
auth="Container"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@DBan8DB1.example.ac.uk:1522:bde8"
username="dbsro"
password="verysecret"
initialSize="5"
maxActive="20"
maxIdle="10"
removeAbandoned="true"
global="jdbc/banner"
maxWait="-1"/>
Obviously I have the ojdbc, pool and dbcp JAR files present in Tomcat's lib
directory on the server.
An important point to note here is that the type
is of "javax.sql.DataSource"
rather than "org.apache.commons.dbcp.BasicDataSource"
which I originally thought it should be.
Now in the Web Application's WEB-INF/web.xml
I added the following:
<resource-ref>
<description>Oracle Banner Datasource</description>
<res-ref-name>jdbc/banner</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
and in my Web Application's servlet-context.xml
file (where ever you keep yours might vary) I had this. This is not the whole file but the XML name space is important here, for the jee parts:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/banner" resource-ref="true" />
Again note the fully qualified reference jndi-name="java:comp/env/jdbc/banner"
which seemed to be required. Why is isn't needed in the resource-ref section of the web.xml
file, I have no idea.
If anyone has any thoughts on this I would be pleased to read them.
By the way, this URL helped: Tomcat6 JNDI data source how-to
After all that, the connection worked. So I mopped the blood, sweat and tears from my work station and enjoyed a delicious cup of fresh coffee.