Reputation: 173
I am using a JNDI datasource that is configured in tomcat server. I want to avoid storing the password as clear text and also i have an existing encryption logic available in the application used which i want to use to encrypt the database password.
<Resource name="jdbc/testdb" auth="Container"
factory="com.zaxxer.hikari.HikariJNDIFactory"
type="javax.sql.DataSource"
minimumIdle="5"
maximumPoolSize="50"
connectionTimeout="300000"
driverClassName="org.mariadb.jdbc.Driver"
jdbcUrl="jdbc:mysql://localhost:3307/testdb"
dataSource.implicitCachingEnabled="true"
connectionTestQuery="Select 1" />
Considering this use case and the possible solutions available online i decided to use org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter for providing the username and password for the database using the code
<bean id="dataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/testdb" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter">
<property name="targetDataSource" ref="dataSource1"/>
<property name="username" value="${dataSource.username}"/>
<property name="password" value="#{passwordDecryptor.decryptedString}"/>
</bean>
This approach works for me for making connections to MSSQL database but quite strangely fails on MariaDB with the error "Access denied for user ''@'localhost' (Using Password :NO)". I wonder if this issue has got anything to do with the HikariCP connection pool, as the same works with C3P0 implementation without any issues.
Also I would like to know if this is the right approach and please suggest if this can improved for getting better performance.
Upvotes: 1
Views: 2038
Reputation: 11114
Ok, I've taking a peek around, give this a shot:
<Resource name="jdbc/testdb" auth="Container"
factory="org.apache.naming.factory.BeanFactory"
type="com.zaxxer.hikari.HikariDataSource"
minimumIdle="5"
maximumPoolSize="50"
connectionTimeout="300000"
driverClassName="org.mariadb.jdbc.Driver"
jdbcUrl="jdbc:mysql://localhost:3307/testdb"
connectionTestQuery="Select 1" />
What is different? We're not using the com.zaxxer.hikari.HikariJNDIFactory
. Why? Because the HikariJNDIFactory
uses the HikariDataSource(HikariConfig config)
constructor, which immediately instantiates the underlying datasource, after which the configuration can no longer be changed.
Using the BeanFactory
, we call the default constructor, which does not instantiate the underlying datasource until the first call to getConnection()
, which means we are free to set the username/password after the JNDI lookup.
On the Spring side:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="dataSource"
jndi-name="jdbc/testdb"
cache="true"
lookup-on-startup="true"
expose-access-context="true">
<property name="dataSourceProperties">
<props>
<prop key="user">${dataSource.username}</prop>
<prop key="password">${passwordDecryptor.decryptedString}</prop>
</props>
</property>
</jee:jndi-lookup>
I believe this should work, or something very close to it. I am going to make an addition to HikariCP too honor the passing of the JNDI environment so that <jee:environment>
can be used inside of the <jee:jndi-lookup>
tag for a cleaner solution.
Upvotes: 2