No entanto, ao utilizar algumas operações específicas da implementação do JDBC da Oracle, me deparei com a seguinte exceção:
java.lang.ClassCastException: org.jboss.resource.adapter.jdbc.WrappedConnection
Pelo que pude entender isto ocorre porque estas operações específicas esperam um objeto conexão do tipo oracle.jdbc.driver.OracleConnection. No entanto, a conexão devolvida pelo Datasource é deste tipo, uma vez que estou usando um banco de dados Oracle (pois é leitor, não iria tentar usar o JDBC da Oracle com outro banco). O que o JBoss faz é encapsular este objeto dentro do objeto WrappedConnection. Desta forma, é possível obter o objeto OracleConnection e utilizar as funcionalidades desejadas do JDBC da Oracle. Para tanto, escrevi o seguinte código:
public class ConnectionUtilities {
/**
*
*/
private static final String WRAPPED_CONNECTION_NAME =
"org.jboss.resource.adapter.jdbc.WrappedConnection";
/**
*
*/
private static final String GET_UNDERLYING_CONNECTION_METHOD =
"getUnderlyingConnection";
/**
* Se a aplicação estiver rodando dentro do JBoss retorna a conexão encapsulada dentro
* da conexão obtida pelo JBoss. Caso contrário, retorna a própria conexão dada
*
* @param conn A conexão com o banco de dados
*
* @return
*/
public static Connection getUnderlyingConnection(Connection conn) {
// Variaveis auxiliares
Connection underlyingConn = conn;
ClassLoader cl = null;
Class wrappedConnectionClass = null;
Method getUnderlyingConnectionMethod = null;
try {
cl = Thread.currentThread().getContextClassLoader();
wrappedConnectionClass = cl.loadClass( WRAPPED_CONNECTION_NAME );
getUnderlyingConnectionMethod = wrappedConnectionClass.getMethod(
GET_UNDERLYING_CONNECTION_METHOD, (Class[]) null );
if( wrappedConnectionClass.isAssignableFrom( conn.getClass() ) ) {
underlyingConn =
(Connection) getUnderlyingConnectionMethod.invoke( conn,
(Object[]) null );
}
} catch (Exception e) {
}
return underlyingConn;
}
}
Note-se que se este código for utilizado fora do ambiente JBoss ele retorna a própria conexão dada como parâmetro. Desta forma consigo utilizar o mesmo código no ambiente de testes que configurei no Eclipse, e no ambiente de produção com o JBoss.
Um comentário:
Eu conversei agora pouco com voce sobre esse codigo e fiquei de escrever minhas observacoes aqui. Entao aih vai.
Eu achei desnecessario todo esse codigo pra fazer tao pouco. O seguinte codigo faria a mesma coisa (nao testei o codigo, escrevi direto aqui).
public class ConnectionUtilities {
public static Connection getUnderlyingConnection(Connection conn) {
if (conn instanceof WrappedConnection) {
return ((WrappedConnection) conn).getUnderlyingConnection();
}
return conn;
}
Esse codigo teria as seguintes vantagens:
1) eh mais eficiente, pois tudo pode ser verificado estaticamente (ok, tem o cast, mas eh bem mais eficiente do que usar reflexao)
2) eh mais facil de entender, qualquer programador entende o que estah acontecendo. Pede pra um estagiario entender o seu codigo ;-)
3) nao gera checked excecoes (sem contar que no seu codigo voce nao trata a excecao que voce gerou)
4) nao eh bem uma vantagem, mas o seu codigo usa muitas variaveis locais. Isso eh ruim, pois dificulta entendimento e refactoring automatico.
Agora, se eh pra fazer um negocio mais independente, e pra brincar com reflexao, voce poderia fazer o utilities configuravel, algo como:
public class ConnectionUtilities {
Map<Class, String> map; // inicia isso aqui de algum jeito
public static Connection getUnderlyingConnection(Connection conn) {
String methodName = map.get(conn.getClass());
if (methodName == null) return conn;
try {
return (Connection) conn.getMethod(methodName, null).invoke(conn, null);
} ... // trata excecoes direito aqui
}
Abracos,
Bart
Postar um comentário