Injecting JNDI datasources for JUnit Tests outside of a container

I was working on some webservices that we're moving into libraries the other day, and needed to run a full set of tests using junit, but outside of a container. I didn't want to create and deploy an entire test harness, I just wanted to run the junit tests in the nb ide. But I couldn't get the test to inject the datasource resource so it was usable. I finally got it working, but I'll spare you all the pain I went through to get here. Here's how I did it in my test class:
    @BeforeClass
    public static void setUpClass() throws Exception {
        // rcarver - setup the jndi context and the datasource
        try {
            // Create initial context
            System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
                "org.apache.naming.java.javaURLContextFactory");
            System.setProperty(Context.URL_PKG_PREFIXES, 
                "org.apache.naming");            
            InitialContext ic = new InitialContext();

            ic.createSubcontext("java:");
            ic.createSubcontext("java:/comp");
            ic.createSubcontext("java:/comp/env");
            ic.createSubcontext("java:/comp/env/jdbc");
           
            // Construct DataSource
            OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
            ds.setURL("jdbc:oracle:thin:@host:port:db");
            ds.setUser("MY_USER_NAME");
            ds.setPassword("MY_USER_PASSWORD");
            
            ic.bind("java:/comp/env/jdbc/nameofmyjdbcresource", ds);
        } catch (NamingException ex) {
            Logger.getLogger(MyDAOTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
As you can see, in this case I used the OracleConnectionPoolDataSource, you should use the datasource for your db connection. Don't forget to link in the appropriate db jar (in my case it was ojdbc14.jar) The code that is used to retrieve the datasource (which works for test and production) looks like this:
        Context initContext = new InitialContext();
        Context webContext = (Context)initContext.lookup("java:/comp/env");

        DataSource ds = (DataSource) webContext.lookup("jdbc/nameofmyjdbcresource");
viola!
Comments:

Thank you so much for posting this! After searching for several days I finally found exactly what I was looking for. I just had to change two lines since I'm using SQL Server.

SQLServerConnectionPoolDataSource ds = new SQLServerConnectionPoolDataSource();
ds.setURL("jdbc:sqlserver://host:1433;databaseName=db");

Greate job!

Posted by Matt on March 20, 2008 at 09:30 AM MDT #

I needed to add the bootstrap.jar from my tomcat/bin directory and then it works well.

Thanks.

Posted by Hein on April 21, 2008 at 11:58 PM MDT #

Wow! You deserve a medal! This was an extremely helpful post that I will be sharing with my co-workers. Thanks!

Posted by Jeff on May 28, 2008 at 04:04 AM MDT #

Hey,

Thanks for these good tips!

I've done that
I have 3 different environnements: weblogic in production, tomcat for local testing, and i also would like to test some code in the webapp using JUnit.

I manage to get the DataSource, but, when I try to get the Connection, I have an strange SQLException:
I/O Exception : The Network Adapter could not establish the connection

I can't see why? Anyone with an idea?
Thanks a lot.

Posted by Johan Chouquet on July 10, 2008 at 12:18 AM MDT #

Randy,
Thank you for the out-of-container SE JNDI code above.
It works perfectly, I have referenced your blog on one of our tutorials on running JPA/EJB 3.0 on Tomcat 6 - when developers want to reuse the JNDI datasource.
thank you
/michael

Posted by Michael OBrien on August 14, 2008 at 01:37 AM MDT #

I too needed this, but for Spring implementation, I took some of yours and expanded it to deal with my Spring need - not sure where to post it, but this seems as good as any :)

Test class

@BeforeClass

public static void setUpClass() throws Exception

{

try

{

// Create initial context

System.setProperty( Context.INITIAL_CONTEXT_FACTORY,

"org.apache.xbean.spring.jndi.SpringInitialContextFactory" );

//-- Loads initial context - and data found in jndi.xml

new InitialContext();

}

catch( NamingException ex )

{

ex.printStackTrace();

//Logger.getLogger( AbstractTest.class.getName() ).log( Priority.SEVERE, null, ex );

}

jndi.xml:

<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:p="http://www.springframework.org/schema/p"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<bean

id="jndi"

class="org.apache.xbean.spring.jndi.DefaultContext">

<property name="entries">

<map>

<entry key="jdbc/MyDataSource">

<bean

class="org.springframework.jdbc.datasource.DriverManagerDataSource"

destroy-method="close"

singleton="false">

<property

name="driverClassName"

value="com.ibm.db2.jcc.DB2Driver" />

<property

name="url"

value="jdbc:db2://xxx.xxx.xxx.xxx:50000/DB2B" />

<property

name="username"

value="joebob" />

<property

name="password"

value="wilson" />

<property name="connectionProperties">

<map>

<entry

key="currentSchema"

value="schemajoe" />

<entry

key="retrieveMessagesFromServerOnGetMessage"

value="true" />

</map>

</property>

</bean>

</entry>

</map>

</property>

</bean>

</beans>

The class listed above for org.apache.xbean.spring.jndi.SpringInitialContextFactory

comes from http://geronimo.apache.org/xbean/

The only reason I chose this route is because of the schema piece - and I wanted to stay in Spring.

Posted by Sean on December 11, 2008 at 08:25 AM MST #

Hi Randy,

a big THANK YOU from me as well. You saved my day.

regards,
se

Posted by se on May 09, 2009 at 08:35 AM MDT #

Very helpful. Thank you Randy for posting this!

Cheers,
Freddy

Posted by Freddy Daoud on May 21, 2009 at 05:27 AM MDT #

Thanks a lot, this helped a lot.

We just Maven. First I got a classnotfound error. I needed to add extra dependencies need.
Below are the dependencies. For unit tests, of course you can use the test scope. I couldn't use the xml of the original pom.xml file, because HTML syntax is not allowed in comments.

artifactId: naming-factory
version: 5.5.15
groupId: tomcat

artifactId: naming-resources
version: 5.5.15
groupId: tomcat

Posted by Gerbrand van Dieijen on May 25, 2009 at 01:35 AM MDT #

you can also use RefFSContextFactory

something as simple as

System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");

//can also set Context.PROVIDER_URL for exact directory location

InitialContext ctx = new InitialContext();

ctx.bind("java:MyDataSource", myds);

You need fscontext.jar and providerutil.jar from Sun for it (can get it here http://java.sun.com/products/jndi/downloads/index.html#DOWNLOAD12 , select File System Provider).

This seems to be less server-specific solution (but need to use correct paths for win and unix environments). Personally I would prefer Spring helper implementations if only I could use it on my project.

Posted by Phil on January 24, 2010 at 10:25 PM MST #

Thanks - this helped me quickly poke some databases from Clojure: http://www.martindengler.com/#2010-01-29

Posted by Martin on January 29, 2010 at 01:05 AM MST #

Another alternative for Spring 3.x:

@PostConstruct
public void setUpDataSource() throws Exception {
try {

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
DriverAdapterCPDS cpds = new DriverAdapterCPDS();
cpds.setDriver("org.h2.Driver");
cpds.setUrl("jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE");
cpds.setUser("sa");
cpds.setPassword(null);

SharedPoolDataSource dataSource = new SharedPoolDataSource();
dataSource.setConnectionPoolDataSource(cpds);
dataSource.setMaxActive(10);
dataSource.setMaxWait(50);
builder.bind("java:/comp/env/jdbc/testDB", dataSource);
builder.activate();
} catch (NamingException ex) {
log.error("Error configuring JNDI DataSource", ex);
}
}

This uses org.springframework.mock.jndi.SimpleNamingContextBuilder.

Thanks Randy for the initial post!

Posted by Areg on July 04, 2010 at 11:10 AM MDT #

Thank you very much, this information was very helpful :D

Posted by Isra on August 02, 2010 at 12:45 AM MDT #

How can i do this for mysql?

Posted by Sekhar on September 20, 2010 at 09:24 PM MDT #

Hi Randy,

thank you so very much for this information. It was extremely helpful for getting the testing environment up and running.

For PostgreSQL 8 two lines to be exchanged:
...
// Construct DataSource
PGConnectionPoolDataSource ds = new PGConnectionPoolDataSource();
ds.setServerName("jdbc:postgresql://xxx.xxx.xxx.xxx/db_name");
...

Double thumbs up!

BTW, needed to add juli-6.0.xy.jar to tomcat lib folder, to satisfy logging requirements of InitialContext.

Posted by Froestel on October 01, 2010 at 12:51 AM MDT #

For PostgreSQL 9 works this.

PGPoolingDataSource ds = new PGPoolingDataSource();

ds.setServerName("127.0.0.1:5432/cistic?useUnicode=true&amp;characterEncoding=UTF-8&amp;characterSetResults=UTF-8");

Posted by swosh on December 14, 2010 at 10:45 AM MST #

For jUnit testing i had to have all classes used in setUpClass() in library test. In my case it was catalina.jar, tomcat-dbcp.jar a jdbc driver.jar for postgre.
Before i got this exception.
SEVERE: null javax.naming.NoInitialContextException: Cannot instantiate class: org.apache.namContextFactorying.java.javaURLContextFactory [Root exception is java.lang.ClassNotFoundException: org.apache.namContextFactorying.java.javaURLContextFactory]

Posted by swoshik on December 14, 2010 at 12:37 PM MST #

This was an extremely helpful post except that i had to google for the jar that contains javaURLContextFactory and add it ..Thanks

Posted by Himateja on January 04, 2011 at 02:38 PM MST #

This code is the total package!

Posted by Tim 0Wen on January 27, 2011 at 10:47 PM MST #

This is great. The only thing I had to do to make this work for my jUnit test was to find/add Naming-common to my Libraries. You have saved me and my team a ton of rework. Thanks again.

Posted by Hendry Betts on February 06, 2011 at 11:21 PM MST #

Many thanks ;) this saves me bunch of time :)

Posted by deeprot on February 17, 2011 at 02:23 AM MST #

Very handy.
Thank you so much..

Posted by Caner on February 24, 2011 at 07:24 PM MST #

It works fine when i run all tests directly from my eclipse IDE but it doesn't work in a bachtest (ant).
I have this error:
noclassdeffounderror ....NamingContext...
Any help?
thanks in advance.
LUC

Posted by Luc on March 16, 2011 at 01:14 AM MDT #

Here's another approach with BasicDataSourceFactory, which mirrors whats in the context.xml:

Properties properties = new Properties();
properties.setProperty("url", "jdbc:mysql://127.0.0.1/myschema");
properties.setProperty("maxActive", "10");
properties.setProperty("maxIdle", "8");
properties.setProperty("minIdle", "10");
properties.setProperty("maxWait", "10");
properties.setProperty("testOnBorrow", "true");
properties.setProperty("username", "username");
properties.setProperty("password", "password");
properties.setProperty("validationQuery", "SELECT 1");
properties.setProperty("removeAbandoned", "true");
properties.setProperty("removeAbandonedTimeout", "1");
properties.setProperty("logAbandoned", "true");
DataSource ds = BasicDataSourceFactory.createDataSource(properties);

"ds" is then used for the bind() call.

Posted by Tilman Hausherr on March 19, 2011 at 12:23 AM MDT #

This post was a lifesaver.Great job!
Thanks..

Posted by Kaplica on April 28, 2011 at 01:32 AM MDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Randy Carver

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today