I am working on a project that requires a database password to be resolved at run time. I am effectively doing this locally by specifying a DataSourceFactory
within the Tomcat configuration. This process prevents any credential being stored in a plain text config file and gives us the custom control to resolve passwords at runtime. I have great success with this locally, but when I move my solution to a real/non-embedded Tomcat instance on AWS, I see this error:
Could not invoke the static newInstance method on the named factory class "org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory".
To start, there are a few fishy details around this error message. Number one (as you'll see below), my factory class is being ignored in the tomcat config file despite the other credentials being valid (e.g. username, password, etc).
The code for my DataSourceFactory
needs to reference a class to resolve a value.
Here is the xml configuration that I have for tomcat:
<?xml version='1.0' encoding='UTF-8'?>
<Context useHttpOnly="false">
<Resource auth="Container" name="mail/Session" type="javax.mail.Session"/>
<Resource name="jdbc/jndi"
auth="Container"
scope="Shareable"
type="javax.sql.DataSource"
useLocalSessionState="true"
cacheServerConfiguration="true"
useServerPrepStmts="true"
cachePrepStmts="true"
cacheCallableStmts="true"
elideSetAutoCommits="true"
alwaysSendSetIsolation="false"
enableQueryTimeouts="false"
prepStmtCacheSize="250"
prepStmtCacheSqlLimit="2048"
maxActive="100"
maxIdle="20"
maxWait="10000"
removeAbandoned="true"
driverClassName="com.mysql.jdbc.Driver"
url="${myURL}"
username="${myUsername}"
password="${myPasswordToResolve}"
factory="my.class.CustomDatabaseConfigurationFactory"
testOnBorrow="true"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="20000"
poolPreparedStatements="true"
maxOpenPreparedStatements="50"
validationQuery="select 1"
validationInterval="60000"
defaultTransactionIsolation="READ_COMMITTED"
/>
<Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
<Loader loaderClass="org.apache.catalina.loader.ParallelWebappClassLoader" />
<Resource auth="Container"
name="jms/JMSConnectionFactory"
type="org.apache.activemq.pool.PooledConnectionFactory"
description="JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory"
brokerURL="my.URL"
maxConnections="100"
maximumActiveSessionPerConnection="50"
expiryTimeout="10000"
/>
</Context>
I will not add the code of my my.class.CustomDatabaseConfigurationFactory
because I know it works locally. For some reason, when I launch this using a remote configuration and setup, Tomcat completely ignores the custom factory I have put in place for the JNDI resource. Is there anything that I have to add to force Tomcat to look for my custom factory and not ignore it? I have almost the same configuration working on an embedded tomcat instance locally. On top of that, I don't see any clues in the logs that are related to a misconfigured file.
Here is the complete stack trace:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [spring/dataaccess/openjpa/openjpa.xml]: Invocation of ini\
t method failed; nested exception is <openjpa-2.4.0-ep2.1-runknown fatal user error> org.apache.openjpa.persistence.ArgumentException: Could not invoke the static newInstance method on the named fact\
ory class "org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory".
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1514)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1119)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:924)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:410)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5118)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5634)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:679)
at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1966)
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:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java)
at java.lang.Thread.run(Thread.java:748)
Realistically, this problem should never occur when proper passwords are being resolved by a custom data factory.
A few things for anyone else who is having this issue:
On top of that, I don't see any clues in the logs that are related to a misconfigured file.
This was a dead giveaway that something was not being loaded or loggers were not being set up properly. After I realized that log4j wasn't configured properly, I was able to see that my factory was in fact being loaded. Later on, after seeing the results, I was able to see that the passwords being set for the database were incorrect.
Although the log error was too high level and without enough detailed information, this issue was fixable with basic debugging methods and can be avoided with attention to logging.