Wednesday, February 25, 2009

Create and Set a DataSource in Your DAO Using Maven2 and JUnit

Here's a somewhat quick hack if you need to functional test a DAO using JUnit (or anything) in the case that a class assumes a DataSource will be injected into it (which is perhaps done elsewhere). You should probably be using Spring to do this (it's much cleaner), but in this case, the jar was not otherwise Spring dependent, so I chose this method just as a quick hack.

First see Randy Carver's code at:
* http://blogs.sun.com/randystuph/entry/injecting_jndi_datasources_for_junit

Using the example Randy Carver provides there, here is some possible code to use. You only want to create one InitialContext, otherwise you get an error, so first off I have a single class to create and provide the InitialContext. By the time I got this far, I was sure that something much better was already written and that I should give it up and just use Spring to inject it, but this was amusing to write, if nothing else:

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
 * @author Gary S. Weaver
 */
public class InitialContextFactory {

    private static InitialContext initialContext;

    // should really do this with Spring, but since no Spring used in this library jar yet, we're using the following
    // http://blogs.sun.com/randystuph/entry/injecting_jndi_datasources_for_junit
    static {
        try {
            System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
            System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
            initialContext = new InitialContext();
            initialContext.createSubcontext("java:");
            initialContext.createSubcontext("java:/comp");
            initialContext.createSubcontext("java:/comp/env");
            initialContext.createSubcontext("java:/comp/env/jdbc");
        }
        catch (NamingException ne) {
            System.err.println("Problem with the jndi name.");
            ne.printStackTrace();
        }
    }

    public static InitialContext getInitialContext() {
        return initialContext;
    }
}
Example test 1:
import junit.framework.TestCase;
import oracle.jdbc.pool.OracleConnectionPoolDataSource;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @author Gary S. Weaver
 */
public class DoSomethingDaoImplTest extends TestCase {

    static {
        try {
            OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
            ds.setURL("connection string goes here");
            ds.setUser("username goes here");
            ds.setPassword("password goes here");
            InitialContextFactory.getInitialContext().bind("java:/comp/env/jdbc/mydatasourcename1", ds);
        }
        catch (NamingException ne) {
            System.err.println("Problem with the jndi name.");
            ne.printStackTrace();
        }
        catch (SQLException se) {
            System.err.println("Problem instantiating OracleConnectionPoolDataSource.");
            se.printStackTrace();
        }
    }

    public void testDoSomething1() throws Exception {
        Context initContext = new InitialContext();
        Context webContext = (Context) initContext.lookup("java:/comp/env");
        DataSource dataSource = (DataSource) webContext.lookup("jdbc/mydatasourcename1");

        MyDAOImpl1 dao = new MyDAOImpl1();
        dao.setDataSource(dataSource);
        ...
        dao.doSomething1("blah");
        ...
    }

    public void testDoSomething2() throws Exception {
        Context initContext = new InitialContext();
        Context webContext = (Context) initContext.lookup("java:/comp/env");
        DataSource dataSource = (DataSource) webContext.lookup("jdbc/mydatasourcename1");

        MyDAOImpl1 dao = new MyDAOImpl1();
        dao.setDataSource(dataSource);
        ...
        dao.doSomething2("blah");
        ...
    }
}
Example test 2:
import junit.framework.TestCase;
import oracle.jdbc.pool.OracleConnectionPoolDataSource;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * @author Gary S. Weaver
 */
public class DoSomethingElseDaoImplTest extends TestCase {

    static {
        try {
            OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
            ds.setURL("connection string goes here");
            ds.setUser("username goes here");
            ds.setPassword("password goes here");
            InitialContextFactory.getInitialContext().bind("java:/comp/env/jdbc/mydatasourcename2", ds);
        }
        catch (NamingException ne) {
            System.err.println("Problem with the jndi name.");
            ne.printStackTrace();
        }
        catch (SQLException se) {
            System.err.println("Problem instantiating OracleConnectionPoolDataSource.");
            se.printStackTrace();
        }
    }

    public void testDoSomethingElse1() throws Exception {
        Context initContext = new InitialContext();
        Context webContext = (Context) initContext.lookup("java:/comp/env");
        DataSource dataSource = (DataSource) webContext.lookup("jdbc/mydatasourcename2");

        MyDAOImpl1 dao = new MyDAOImpl1();
        dao.setDataSource(dataSource);
        ...
        dao.doSomethingElse1("blah");
        ...
    }

    public void testDoSomethingElse2() throws Exception {
        Context initContext = new InitialContext();
        Context webContext = (Context) initContext.lookup("java:/comp/env");
        DataSource dataSource = (DataSource) webContext.lookup("jdbc/mydatasourcename2");

        MyDAOImpl1 dao = new MyDAOImpl1();
        dao.setDataSource(dataSource);
        ...
        dao.doSomethingElse2("blah");
        ...
    }
}
The next thing you'll need is ojdbc14.jar so that it can find OracleConnectionPoolDataSource. Although that driver jar is not in the main Maven2 repo, you can download and install it locally or in your local repo, or I also found this which you could add to your pom.xml (but it may not always be available).
...
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>10.2.0.4.0</version>
        </dependency>
...
        <!-- they host the ojdbc14.jar, so it doesn't have to be installed locally -->
        <repository>
            <id>mesir-repo</id>
            <url>http://mesir.googlecode.com/svn/trunk/mavenrepo</url>
        </repository>
...
This will compile already, but you'll see this error if you run the test:
javax.naming.NoInitialContextException: Cannot instantiate class: org.apache.naming.java.javaURLContextFactory [Root exception is java.lang.ClassNotFoundException: org.apache.naming.java.javaURLContextFactory]
 at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:657)
 at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:247)
 at javax.naming.InitialContext.init(InitialContext.java:223)
 at javax.naming.InitialContext.<init>(InitialContext.java:175)
 at ...(your unit test)...
 at junit.framework.TestCase.runBare(TestCase.java:125)
 at junit.framework.TestResult$1.protect(TestResult.java:106)
 at junit.framework.TestResult.runProtected(TestResult.java:124)
 at junit.framework.TestResult.run(TestResult.java:109)
 at junit.framework.TestCase.run(TestCase.java:118)
 at junit.framework.TestSuite.runTest(TestSuite.java:208)
 at junit.framework.TestSuite.run(TestSuite.java:203)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:585)
 at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:213)
 at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
 at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
 at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:585)
 at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)
 at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)
Caused by: java.lang.ClassNotFoundException: org.apache.naming.java.javaURLContextFactory
 at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:316)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:280)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
 at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:242)
 at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:42)
 at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:654)
 ... 25 more
Caused by: java.lang.ClassNotFoundException: org.apache.naming.java.javaURLContextFactory
 at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:316)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:280)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
 at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:242)
 at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:42)
 at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:654)
 ... 25 more
You need to add the following dependency to your pom.xml. This will pull in org.apache.naming.java.javaURLContextFactory:
...
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>catalina</artifactId>
            <version>6.0.18</version>
        </dependency>
...
That's it! Hope this helps you out.

2 comments:

David said...

Thanks man, it works!!

Rags said...

Thanks man