Reputation: 711
I have a tomcat application server and my DB connection is defined in context.xml and fetched the data source as a JNDI.
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
<Resource name="datasource/test" auth="Container"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
factory="org.apache.naming.factory.BeanFactory"
user="abc"
password="abc123"
jdbcUrl="jdbc:mysql://localhost:3306/jacplus"
driverClass="com.mysql.jdbc.Driver"
minPoolSize="2"
initialPoolSize="30"
maxPoolSize="50"
idleConnectionTestPeriod="600"
acquireRetryAttempts="30"/>
</Context>
rather than hardcoding the username and password here in context.xml.I want to store db credentials in aws secret manager and create the data source with retrieved DB credentials from aws secret manager.
to do this I created the following custom ComboPooledDataSource class.
import com.mchange.v2.c3p0.AbstractComboPooledDataSource;
import javax.naming.Referenceable;
import java.io.Serializable;
public final class CustomComboPoolDataSource extends AbstractComboPooledDataSource implements Serializable, Referenceable {
}
import com.mchange.v2.c3p0.PoolConfig;
import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import java.sql.SQLException;
import java.util.Properties;
import javax.naming.Context;
import javax.sql.DataSource;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
public class SecureTomcatDataSourceImpl extends DataSourceFactory {
public SecureTomcatDataSourceImpl() {
}
@Override
public DataSource createDataSource(Properties properties, Context context, boolean XA) throws SQLException {
String userName = getFromAWSSecretManager("username");
String password = getFromAWSSecretManager("password");
PoolConfiguration poolProperties = SecureTomcatDataSourceImpl.parsePoolProperties(properties);
PoolConfig poolConfig = new PoolConfig(properties);
CustomComboPoolDataSource customDataSource = new CustomComboPoolDataSource();
customDataSource.setProperties(properties);
customDataSource.setUser(userName );
customDataSource.setPassword(password);
// The rest of the code is copied from Tomcat's DataSourceFactory.
if (poolProperties.getDataSourceJNDI() != null && poolProperties.getDataSource() == null) {
performJNDILookup(context, poolProperties);
}
return customDataSource;
}
}
after that, i created a jar file from above implementation and put it in tomcat /lib folder.
and I did the following modifications in Context.xml file in tomcat/conf folder.
<Resource name="datasource/test" auth="Container"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
**factory="com.aws.rds.SecureTomcatDataSourceImpl"
user=""
password=""**
jdbcUrl="jdbc:mysql://localhost:3306/jacplus"
driverClass="com.mysql.jdbc.Driver"
minPoolSize="2"
initialPoolSize="30"
maxPoolSize="50"
idleConnectionTestPeriod="600"
acquireRetryAttempts="30"/>
but when i start tomcat i am getting following exception.
thod failed; nested exception is org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException: Failed to look up JNDI DataSource with name 'java:comp/env/datasource/test'; nested exception is javax.naming.NameNotFoundException: JNDI object with [java:comp/env/datasource/test] not found: JNDI implementation returned null at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1745) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1083) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:853) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:400) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:291) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:103) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4770) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5236) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:744) at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:980) at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1851) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
Caused by: javax.naming.NameNotFoundException: JNDI object with [java:comp/env/datasource/test] not found: JNDI implementation returned null at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:158) at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178) at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:96) at org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup.getDataSource(JndiDataSourceLookup.java:45)
I verified my DB credentials and DB configs are correct.
Upvotes: 0
Views: 682
Reputation: 16045
You are extending Tomcat JDBC's ObjectFactory
to create your datasource, which returns null
whenever the type attribute is not javax.sql.DataSource
, javax.sql.XADataSource
or org.apache.tomcat.jdbc.pool.DataSource
and logs the problem at a warning level (cf. source code).
If you set type="javax.sql.DataSource"
it should work, but your solution has a dependency on both Tomcat JDBC and C3P0.
I would rather retrieve username and password and call setUser
and setPassword
in the default constructor of CustomComboPoolDataSource
and use the generic BeanFactory
to configure it in Tomcat.
Upvotes: 0