2017-07-21 3 views
0

Dans un environnement Tomcat 8.5.15 utilisant une base de données Oracle 11, je souhaite implémenter une source de données qui gère les mots de passe cryptés dans le context.xml. Par exemple, au lieu d'avoir:Pourquoi est-ce que j'obtiens une exception CannotAcquireResourceException en essayant de me connecter, quand j'étends AbstractComboPooledDataSource?

 <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" 
        /> 

Je voudrais avoir quelque chose comme ce qui suit, où seuls les password et type ont changé:

<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" 
       /> 

Le principal changement est ma mise en œuvre du EncryptedC3p0WrappingDataSource . ComboPooledDataSource de C3p0 est final, donc je ne peux pas l'étendre. Au lieu de cela, je prolonge sa superclasse, AbstractComboPooledDataSource, et implémente des méthodes supplémentaires. Cette classe contient un ComboPooledDataSource, qui est le wrappedDataSource, et est utilisé pour le travail réel par délégation.

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); 
    } 
} 

Quand je lance notre application sous Tomcat avec la première configuration (ComboPooledDataSource), il fonctionne très bien. Lorsque je tente la deuxième configuration (EncryptedC3p0WrappingDataSource), je reçois l'exception suivante:

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 

J'ai regardé ce longuement dans le débogueur. La partie de cryptage et de décryptage semble se passer correctement. Mes EncryptedC3p0WrappingDataSource.getConnection() résultats de la méthode dans un appel à la méthode ComboPooledDataSource.getConnection() (la méthode AbstractPoolBackedDataSource.getConnection() héritée, alors pourquoi vais-je recevoir l'exception

MISE À JOUR:

Si je modifie ma méthode get setPassword utiliser aussi 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(); } 
    } 

je reçois une autre exception:

Caused by: java.sql.SQLException: [email protected] 
[wrapping: [email protected]] 
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 

MISE à JOUR 2:

J'ai posté une question étroitement liée, et je l'espère plus simple, here.

Répondre

0

L'erreur résultait d'un problème de chargement de classe, dans lequel les classes Oracle étaient chargées depuis plusieurs fichiers jar (%CATALINA_HOME%\lib\ojdbc7-12.1.0.2.0.jar et %CATALINA_HOME%\webapps\my-webapp-1.0.0\WEB-INF\lib\ojdbc7-12.1.0.2.0.jar) par différents chargeurs de classe. Quand j'ai supprimé %CATALINA_HOME%\webapps\my-webapp-1.0.0\WEB-INF\lib\ojdbc7-12.1.0.2.0.jar, mon problème est parti.

Ces sources (1, 2, 3) en traitent plus en détail.