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=***
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