28
votes

I am using Apache Commons DBCP (commons-dbcp.jar) Connection pool.

Once I obtained a connection from the pool it is wrapped in the class org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.

My requirement is to pass an array of Strings to pl/sql stored procedure in Oracle.

Here is what I am doing in the following code snippet:

Connection dbConn = ConnectionManager.ds.getConnection();
//The above statement returns me an connection wrapped in the class
//org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.

org.apache.commons.dbcp.DelegatingConnection del = new org.apache.commons.dbcp.DelegatingConnection(dbConn.getConnection());
con = del.getInnermostDelegate();

cs = con.prepareCall("call SP_NAME(?,?,?,?)");
oracle.sql.ArrayDescriptor arDesc= oracle.sql.ArrayDescriptor.createDescriptor("ARRAY_NAME", (OracleConnection) con);

CallableStatement c_stmt = conn.prepareCall("begin update_message_ids_ota
(:x); end;" );
c_stmt.setArray( 1, array_to_pass );
c_stmt.execute();

On executing the above code, I get the following exception:

java.lang.ClassCastException: org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection at oracle.sql.ArrayDescriptor.createDescriptor

I tried to find out solution over this going throughout almost of the sites and forums, but couldn't get the satisfied answer or solution on the same.

11
Why do you wrap dbConn only to unwrap it again? What happens if you call getInnermostDelegate directly on dbConn?Thilo

11 Answers

25
votes

By default, DBCP does not allow access to the "real" underlying database connection instance, so you cannot get to the Oracle class.

When configuring the pool, you can set

accessToUnderlyingConnectionAllowed = true

and then it works.

Default is false, it is a potential dangerous operation and misbehaving programs can do harmful things. (closing the underlying or continue using it when the guarded connection is already closed) Be careful and only use when you need direct access to driver specific extensions

NOTE: Do not close the underlying connection, only the original one.

18
votes

If you are using a Java 6 compliant JDBC Connection, you can use code like the following:

OracleConnection oracleConnection = null;
try {
    if (connection.isWrapperFor(OracleConnection.class)) {
        oracleConnection = connection.unwrap(OracleConnection.class);
    }
} catch (SQLException ex) {
    // do something
}
return oracleConnection;

From this point on, use the oracleConnection instead of the original connection.

See http://docs.oracle.com/javase/6/docs/api/java/sql/Wrapper.html

6
votes

Seen this post i can get the OracleConnection with this code:

DataSource ds1 = // get the org.apache.commons.dbcp.PoolingDataSource
org.apache.tomcat.dbcp.dbcp.DelegatingConnection del = new org.apache.tomcat.dbcp.dbcp.DelegatingConnection(cds1.getConnection());
OracleConnection con = (OracleConnection) del.getInnermostDelegate();

remember the commons-dbcp-1.4.jar neet to be in the class path

5
votes

Hmmm,I have meet the same solution like you.I think there a two position need you mention it. 1.Config Connection pool set accessToUnderlyingConnectionAllowed = "true" ; 2.The nightmare concerned to open source project. The terrable conceration. In this case,that is

org.apache.commons.dbcp.DelegatingConnection 

is not equal to

org.apache.tomcat.dbcp.dbcp.DelegatingConnection

while in default apache common-dbcp.jar,you will never find the follow Class.But just the class is the key. So,we must find the Class in somewhere. I final find the package tomcat-dbcp.jar . You can get it from http://www.docjar.com/ After

import org.apache.tomcat.dbcp.dbcp.DelegatingConnection

,you can force cast you dbConn and get the Underlying Connection

oracle.jdbc.driver.OracleConnection delConn = 

(oracle.jdbc.driver.OracleConnection) 

((org.apache.tomcat.dbcp.dbcp.DelegatingConnection)c_stmt.getConnection()).getDelegate();

Then we can use delConn to get the ArrayDescriptor Remember one thing,in there,we do not need the

org.apache.commons.dbcp.DelegatingConnection Class

It's a so strange thing,but real work to the case.

2
votes

I'm positing this here to make sure anyone else looking for advice knows about the ultimate solution to this:

If you're forced to use the non bundled version of the persistence manager (because an old repository still uses that structure which is incompatible with the bundled layout), here what you can do, the solution is quite simple:

Download the sources for Jackrabbit Core (you can get them from the Jackrabbit website) Open the OraclePersistenceManager class and find the following line of code:

Object blob = createTemporary.invoke(null,
                new Object[]{con, Boolean.FALSE, durationSessionConstant});

(Around line 377 - can also check the StackTrace for reference)

ConnectionFactory contains a static method that allows to unwrap a connection which is exactly what you need:

Object blob = createTemporary.invoke(null,
                new Object[]{org.apache.jackrabbit.core.util.db.ConnectionFactory
                        .unwrap(con), Boolean.FALSE, durationSessionConstant});

You will need Maven 2+ in order to compile the sources, I did that and had no dependency problems, note that I compiled version 2.2.10 of Jackrabbit.

I also made sure to log a bug against Jackrabbit 2.2.11 (current release which still has the issue): https://issues.apache.org/jira/browse/JCR-3262

Hope this helps!

1
votes

We use arrays in our calls to oracle stored procs and use oracle proprietary api to build arrays. This little check fixed the issue for us when using the functionality from stand alone apps using commons-dbcp.

    if (conn instanceof org.apache.commons.dbcp.DelegatingConnection)
    {
        log.debug("detected apache commons dbcp datasource");
        conn = ((org.apache.commons.dbcp.DelegatingConnection) conn).getInnermostDelegate();
    }

You will need commons-dbcp in the classpath/dependecies though.

    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
        <scope>provided</scope>
    </dependency>
1
votes

I had encountered the same issue. We were using spring and it has a class called NativeJdbcExtractor. It has many implementations and the following one works for TomCat. There are specific implementations for Websphere,Weblogic app servers.

<bean id="jdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"></bean>

In your DAO you can inject the bean and use the following method

protected NativeJdbcExtractor jdbcExtractor;
Connection conn=jdbcExtractor.getNativeConnection(oracleConnection);
0
votes

in your context definition add below tags to your existing xml definition.

factory="oracle.jdbc.pool.OracleDataSourceFactory
scope="Shareable"
type="oracle.jdbc.pool.OracleDataSource"

.

0
votes

For anyone else looking, getDelegate() and getInnermostDelegate() both return NULL in my code. However, from the debugger I found the OracleConnection as below. We use Spring JdbcTemplate throughout the application, which has the data source injected. We're on spring-jdbc-4.1.5.RELEASE.jar and ojdbc6.jar.

Connection conn = getJdbcTemplate().getDataSource().getConnection();

OracleConnection oracleConnection = ( OracleConnection ) conn.getMetaData().getConnection();
0
votes

I am working with tomcat 8.5.8 and was facing this issue.
The below solution worked like charm.


The Code:

Delegating Connection delegate_conn = new Delegating Connection(connection)
conn = delegate_conn.getInnermostDelegate();
oracle.sql.ArrayDescriptor desc = oracle.sql.ArrayDescriptor.createDescriptor("TABLE_VIEW", conn);

The Solution:

Adding dependency for tomcat-dbcp 8.5.8 and add the same jar in lib folder of tomcat.
Seems tomcat has different jars for different versions starting from 7.0 (reference: https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-dbcp).

Hope it helps someone.

0
votes

I'm using java7 & ojdbc7.jar & Tomcat 8.

I had the same issue in tomcat while converting ((OracleConnection)connection).createARRAY

After searching for lot of solutions and forums, finally the worked solution for me is,

Connection connection = datasource.getConnection();
CallableStatement cs = connection.prepareCall("{ CALL PKG.PROCEDURE(?)}");

if(cs.getConnection().isWrapperFor(OracleConnection.class)) {
  OracleConnection orConn = cs.getConnection().unwrap(OracleConnection.class);
  orConn.createARRAY ..// is working perfectly.

}

If you did connection.isWrapperFor(OracleConnection.class) you will get false. you need to use cs.getConnection.

Might help someone.