1
votes

I've hit a blocker while migrating a legacy Java application over to Spring Boot. Specifically, the application uses Apache CXF for SOAP web services that have operations for getting data from an Oracle database. The issue I'm seeing is that as soon as a JNDI lookup is attempted by the web service, then the following exception is thrown:

Name [java:comp/env/jdbc/myDataSource] is not bound in this Context. Unable to find [java:comp].

NamingException: javax.naming.NameNotFoundException: Name [java:comp/env/jdbc/myDataSource] is not bound in this Context. Unable to find [java:comp].

For reference, I checked in an example of the issue here: https://github.com/pmercer/spring-boot-sample-tomcat-jndi-cxf-ws

So, if anyone could point me to a working example and/or offer advice for resolving the issue, then it would be much appreciated.

Adding more details ...

Here's how the lookup is performed in the existing application, which is deployed as a .war file on an external Tomcat server ...

/**
 * Returns a database connection for the specified datasource
 * @param db - ex. "jdbc/DatasourceXyz"
 * @return db Connection
 * @throws Exception
 */
public static Connection getDbConnection(String db) throws Exception {
    java.sql.Connection conn = null;

    try {
        LOG.debug("getting db connection for " + db + " ...");

        javax.naming.Context initCtx = new javax.naming.InitialContext();
        javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env");
        javax.sql.DataSource ds = (javax.sql.DataSource) envCtx.lookup(db);
        conn = ds.getConnection();

        initCtx.close();
        LOG.debug("got db connection for " + db);
        LOG.debug("url = " + conn.getMetaData().getURL());

    } catch (Exception e) {
        String message = String.format("Exception thrown while creating a JDBC connection for %s: %s", db,
                e.getMessage());
        throw new Exception(message);
    }

    return conn;
}

And the database properties are stored in the Tomcat server's context.xml file ...

<Context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- environment-specific jdbc data sources: -->
<Resource name="jdbc/DatasourceXyz" auth="Container" type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver" url="jdbc:oracle:thin:@hostname:1521:database" username="someUser" password="***" maxTotal="20" maxIdle="10" maxWaitMillis="60000"/>
<!-- set other jdbc data sources below: -->

Also, because the existing getDbConnection method invocations are widespread and because the application's database CRUD code is extensive, the goal is to leave that code as-is. So basically I just need to be able to create the database connection, like so ...

public static Connection getDbConnection(String db) throws Exception {
    java.sql.Connection conn = null;

    try {
        LOG.debug("getting db connection for " + db + " ...");

        // Get the SampleUtilService from the Bridge
        SampleUtilService sampleUtilService = SpringContextBridge.services().getSampleUtilService();

        // Get DataSource from the JndiObjectFactoryBean
        javax.sql.DataSource ds = sampleUtilService.getDataSourcefromFactoryBean(db);

        conn = ds.getConnection();

        LOG.debug("got db connection for " + db);
        LOG.debug("url = " + conn.getMetaData().getURL());

    } catch (Exception e) {
        String message = String.format("Exception thrown while creating a JDBC connection for %s: %s", db,
                e.getMessage());
        throw new Exception(message);
    }

    return conn;
}

So, with the help of the The SpringContextBridge functionality and configuring JNDI using an embedded Tomcat server, as credited in my sample project's README, basically that's my plan. Also, since I'm fairly new to Spring Boot, I'm not sure if that's the best approach or not.

Also, after doing some additional testing after my initial post, it appears the issue I was seeing could simply be caused by setting the JndiObjectFactoryBean.setLookupOnStartup property to false, ie.,

    @Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
    JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
    bean.setJndiName("java:comp/env/jdbc/myDataSource");
    bean.setProxyInterface(DataSource.class);
    bean.setLookupOnStartup(false);
    bean.afterPropertiesSet();
    return (DataSource)bean.getObject();
}

When I set the bean's setLookupOnStartup property to true, the application was able to successfully get the database connection. However, I still see the issue when the application attempts to do the lookup via JNDI, ie.,

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

So, if additional testing using the bean proves successful, then I'll probably just go with doing the lookups using the bean. Also, I'm open for suggestions if there are better ways to do it.

Also, it is my understanding the datasource configuration should be stored in the app's application.properties file ...

jdbc.xyz.datasource.name=jdbc/DatasourceXyz
jdbc.xyz.datasource.driver=oracle.jdbc.OracleDriver
jdbc.xyz.datasource.url=jdbc:oracle:thin:@hostname:1521:database
jdbc.xyz.datasource.username=someUser
jdbc.xyz.datasource.password=***
1
Where is your jndi setup at?ndrone
Why would you need JNDI with an embedded tomcat?!M. Deinum
I added more details to my post, so let me know if that helps answer your question, as well as the question before from @ndrone.Patrick Mercer
Yes you set it up to connect to java:comp/env/jdbc/myDataSource but where does that definition or configuration live? In WebSphere it would live in the console... where does it live in embedded tomcat?ndrone
In a Spring Boot app using an embedded Tomcat server, my understanding is the datasource configuration is stored in the app's application.properties file.Patrick Mercer

1 Answers

0
votes

I'm not sure why the direct JNDI lookups are failing when performed within the context of an Apache CXF SOAP web service call. However, lookups done using the JndiObjectFactoryBean bean are working, so that's what I'm going to run with.