当我扩展 AbstractComboPooledDataSource 时,为什么在尝试连接时会出现 CannotAcquireResourceException?
Why do I get a CannotAcquireResourceException while trying to connect, when I extend AbstractComboPooledDataSource?
在使用 Oracle 11 数据库的 Tomcat 8.5.15 环境中,我想在 context.xml
中实现一个处理加密密码的数据源。例如,而不是:
<Resource
auth="Container"
description="MyDataSource"
driverClass="oracle.jdbc.OracleDriver"
maxPoolSize="100"
minPoolSize="10"
acquireIncrement="1"
name="jdbc/MyDataSource"
user="me"
password="mypassword"
factory="org.apache.naming.factory.BeanFactory"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
jdbcUrl="jdbc:oracle:thin:@mydb:1521:dev12c"
/>
我想要类似下面的内容,其中只有 password
和 type
发生了变化:
<Resource
auth="Container"
description="MyDataSource"
driverClass="oracle.jdbc.OracleDriver"
maxPoolSize="100"
minPoolSize="10"
acquireIncrement="1"
name="jdbc/MyDataSource"
user="me"
password="D364FEC1CBC1DAEB91A1D8997D4A2482B"
factory="org.apache.naming.factory.BeanFactory"
type="com.mycompany.EncryptedC3p0WrappingDataSource"
jdbcUrl="jdbc:oracle:thin:@mydb:1521:dev12c"
/>
主要变化是我对 EncryptedC3p0WrappingDataSource
的实施。 C3p0 的 ComboPooledDataSource 是最终的,所以我不能扩展它。相反,我扩展了它的 superclass、AbstractComboPooledDataSource
,并实现了一些额外的方法。此 class 包含一个 ComboPooledDataSource
,即 wrappedDataSource
,并通过委托用于实际工作。
public class EncryptedC3p0WrappingDataSource
extends AbstractComboPooledDataSource
implements PooledDataSource, Serializable, Referenceable
{
/** The actual C3P0 data source that will be used to connect to the database. */
private ComboPooledDataSource wrappedDataSource = new ComboPooledDataSource();
// TODO Should this be retrieved from a pool? How?
/** The object that does the encryting/decrypting. */
private Encryptor encryptor;
/**Construct the data source, with the necessary Encryptor. */
public EncryptedC3p0WrappingDataSource() {
try {
encryptor = new Encryptor();
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | UnsupportedEncodingException e) {
log.fatal("Error instantiating decryption class.", e);
throw new RuntimeException(e);
}
}
/**
* Set the in-memory password of the wrapped data source to the decrypted password.
* @param encryptedPassword the encrypted password, as read from a file.
*/
public void setPassword(String encryptedPassword) {
try {
String decryptedPassword
= encryptor.decrypt(encryptedPassword, Encryptor.AES_ALGORITHM);
log.info("***************** Successfully decrypted "
+ encryptedPassword + " to " + decryptedPassword);
wrappedDataSource.setPassword(decryptedPassword);
} catch (Exception e) { e.printStackTrace(); }
}
public void setDriverClass(String driverClass) throws PropertyVetoException {
wrappedDataSource.setDriverClass(driverClass);
}
public void setJdbcUrl(String jdbcUrl) {
wrappedDataSource.setJdbcUrl(jdbcUrl);
}
public void setDescription(String description) {
wrappedDataSource.setDescription(description);
}
public void setMaxPoolSize(int maxPoolSize) {
wrappedDataSource.setMaxPoolSize(maxPoolSize);
}
public void setMinPoolSize(int minPoolSize) {
wrappedDataSource.setMinPoolSize(minPoolSize);
}
public void setAcquireIncrement(int acquireIncrement) {
wrappedDataSource.setAcquireIncrement(acquireIncrement);
}
public Connection getConnection() throws SQLException {
return wrappedDataSource.getConnection();
}
public Connection getConnection(String name, String password) throws SQLException {
return wrappedDataSource.getConnection(name, password);
}
}
当我 运行 我们的应用程序在 Tomcat 下使用第一个配置 (ComboPooledDataSource
) 时,它 运行 没问题。当我尝试第二种配置 (EncryptedC3p0WrappingDataSource
) 时,出现以下异常:
2017-07-21 07:57:29,962 FATAL [XXX.DataSourceFactory] Connections could not be acquired from the underlying database!
java.sql.SQLException: Connections could not be acquired from the underlying database!
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:690)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at com.mycompany.EncryptedC3p0WrappingDataSource.getConnection(EncryptedC3p0WrappingDataSource.java:116)
...
Caused by: com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1463)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:639)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:549)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:756)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:683)
... 69 more
我在调试器中对此进行了广泛的研究。加密和解密部分似乎正确发生。我的 EncryptedC3p0WrappingDataSource.getConnection()
方法导致调用 ComboPooledDataSource.getConnection()
方法(继承的 AbstractPoolBackedDataSource.getConnection()
方法,那么为什么我会收到异常?
更新:
如果我修改我的 get setPassword
方法以也使用 setOverrideDefaultPassword
:
public void setPassword(String encryptedPassword) {
try {
String decryptedPassword
= encryptor.decrypt(encryptedPassword, Encryptor.AES_ALGORITHM);
log.info("***************** Successfully decrypted "
+ encryptedPassword + " to " + decryptedPassword);
wrappedDataSource.setPassword(decryptedPassword);
wrappedDataSource.setOverrideDefaultPassword(decryptedPassword);
} catch (Exception e) { e.printStackTrace(); }
}
我得到一个不同的异常:
Caused by: java.sql.SQLException: com.mchange.v2.c3p0.impl.NewProxyConnection@7e30531e
[wrapping: oracle.jdbc.driver.T4CConnection@51dba714]
is not a wrapper for or implementation of oracle.jdbc.OracleConnection
at com.mchange.v2.c3p0.impl.NewProxyConnection.unwrap(NewProxyConnection.java:1744)
at org.jaffa.security.JDBCSecurityPlugin.executeStoredProcedure(JDBCSecurityPlugin.java:117)
... 67 more
更新 2:
我发布了一个密切相关且希望更简单的问题 。
在使用 Oracle 11 数据库的 Tomcat 8.5.15 环境中,我想在 context.xml
中实现一个处理加密密码的数据源。例如,而不是:
<Resource
auth="Container"
description="MyDataSource"
driverClass="oracle.jdbc.OracleDriver"
maxPoolSize="100"
minPoolSize="10"
acquireIncrement="1"
name="jdbc/MyDataSource"
user="me"
password="mypassword"
factory="org.apache.naming.factory.BeanFactory"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
jdbcUrl="jdbc:oracle:thin:@mydb:1521:dev12c"
/>
我想要类似下面的内容,其中只有 password
和 type
发生了变化:
<Resource
auth="Container"
description="MyDataSource"
driverClass="oracle.jdbc.OracleDriver"
maxPoolSize="100"
minPoolSize="10"
acquireIncrement="1"
name="jdbc/MyDataSource"
user="me"
password="D364FEC1CBC1DAEB91A1D8997D4A2482B"
factory="org.apache.naming.factory.BeanFactory"
type="com.mycompany.EncryptedC3p0WrappingDataSource"
jdbcUrl="jdbc:oracle:thin:@mydb:1521:dev12c"
/>
主要变化是我对 EncryptedC3p0WrappingDataSource
的实施。 C3p0 的 ComboPooledDataSource 是最终的,所以我不能扩展它。相反,我扩展了它的 superclass、AbstractComboPooledDataSource
,并实现了一些额外的方法。此 class 包含一个 ComboPooledDataSource
,即 wrappedDataSource
,并通过委托用于实际工作。
public class EncryptedC3p0WrappingDataSource
extends AbstractComboPooledDataSource
implements PooledDataSource, Serializable, Referenceable
{
/** The actual C3P0 data source that will be used to connect to the database. */
private ComboPooledDataSource wrappedDataSource = new ComboPooledDataSource();
// TODO Should this be retrieved from a pool? How?
/** The object that does the encryting/decrypting. */
private Encryptor encryptor;
/**Construct the data source, with the necessary Encryptor. */
public EncryptedC3p0WrappingDataSource() {
try {
encryptor = new Encryptor();
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | UnsupportedEncodingException e) {
log.fatal("Error instantiating decryption class.", e);
throw new RuntimeException(e);
}
}
/**
* Set the in-memory password of the wrapped data source to the decrypted password.
* @param encryptedPassword the encrypted password, as read from a file.
*/
public void setPassword(String encryptedPassword) {
try {
String decryptedPassword
= encryptor.decrypt(encryptedPassword, Encryptor.AES_ALGORITHM);
log.info("***************** Successfully decrypted "
+ encryptedPassword + " to " + decryptedPassword);
wrappedDataSource.setPassword(decryptedPassword);
} catch (Exception e) { e.printStackTrace(); }
}
public void setDriverClass(String driverClass) throws PropertyVetoException {
wrappedDataSource.setDriverClass(driverClass);
}
public void setJdbcUrl(String jdbcUrl) {
wrappedDataSource.setJdbcUrl(jdbcUrl);
}
public void setDescription(String description) {
wrappedDataSource.setDescription(description);
}
public void setMaxPoolSize(int maxPoolSize) {
wrappedDataSource.setMaxPoolSize(maxPoolSize);
}
public void setMinPoolSize(int minPoolSize) {
wrappedDataSource.setMinPoolSize(minPoolSize);
}
public void setAcquireIncrement(int acquireIncrement) {
wrappedDataSource.setAcquireIncrement(acquireIncrement);
}
public Connection getConnection() throws SQLException {
return wrappedDataSource.getConnection();
}
public Connection getConnection(String name, String password) throws SQLException {
return wrappedDataSource.getConnection(name, password);
}
}
当我 运行 我们的应用程序在 Tomcat 下使用第一个配置 (ComboPooledDataSource
) 时,它 运行 没问题。当我尝试第二种配置 (EncryptedC3p0WrappingDataSource
) 时,出现以下异常:
2017-07-21 07:57:29,962 FATAL [XXX.DataSourceFactory] Connections could not be acquired from the underlying database!
java.sql.SQLException: Connections could not be acquired from the underlying database!
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:690)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at com.mycompany.EncryptedC3p0WrappingDataSource.getConnection(EncryptedC3p0WrappingDataSource.java:116)
...
Caused by: com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1463)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:639)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:549)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:756)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:683)
... 69 more
我在调试器中对此进行了广泛的研究。加密和解密部分似乎正确发生。我的 EncryptedC3p0WrappingDataSource.getConnection()
方法导致调用 ComboPooledDataSource.getConnection()
方法(继承的 AbstractPoolBackedDataSource.getConnection()
方法,那么为什么我会收到异常?
更新:
如果我修改我的 get setPassword
方法以也使用 setOverrideDefaultPassword
:
public void setPassword(String encryptedPassword) {
try {
String decryptedPassword
= encryptor.decrypt(encryptedPassword, Encryptor.AES_ALGORITHM);
log.info("***************** Successfully decrypted "
+ encryptedPassword + " to " + decryptedPassword);
wrappedDataSource.setPassword(decryptedPassword);
wrappedDataSource.setOverrideDefaultPassword(decryptedPassword);
} catch (Exception e) { e.printStackTrace(); }
}
我得到一个不同的异常:
Caused by: java.sql.SQLException: com.mchange.v2.c3p0.impl.NewProxyConnection@7e30531e
[wrapping: oracle.jdbc.driver.T4CConnection@51dba714]
is not a wrapper for or implementation of oracle.jdbc.OracleConnection
at com.mchange.v2.c3p0.impl.NewProxyConnection.unwrap(NewProxyConnection.java:1744)
at org.jaffa.security.JDBCSecurityPlugin.executeStoredProcedure(JDBCSecurityPlugin.java:117)
... 67 more
更新 2:
我发布了一个密切相关且希望更简单的问题