20

Why java.sql.Connection cannot be cast to oracle.jdbc.OracleConnection in code below?

My main goal is to pass to Oracle connection new user name and save it in 'SESSION' table in for example 'osuser' column because I want to trace in DB user changes and display it in the table.

@Repository
public class AuditLogDAOImpl implements AuditLogDAO {

    @PersistenceContext(unitName="myUnitName")
    EntityManager em;

    @Resource(name = "dataSource")
    DataSource dataSource;

    public void init() {

        try {
            Connection connection = DataSourceUtils.getConnection(dataSource);
            OracleConnection oracleConnection = (OracleConnection) connection; //Here I got cast exception!

            String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
            metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = "my_new_username";

            oracleConnection.setEndToEndMetrics(metrics, (short) 0);

            java.util.Properties props = new java.util.Properties();
            props.put("osuser", "newValue");

            oracleConnection.setClientInfo(props);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Here is error log:

10:42:29,251 INFO  [STDOUT] org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6@bcc8cb
10:42:51,701 ERROR [STDERR] java.lang.ClassCastException: $Proxy286 cannot be cast to oracle.jdbc.OracleConnection

Generally I have 2 problem in this case:

  • why cast from Connection to OracleConnection fails and
  • what is the best way to implement my intend (I mean set the new user name to v$session.osuser in Oracle DB?

I work with Oracle 11g, Hibernate (using entity manager), data source via jndi.

Please help, thanks!

EDIT:

After some improvement the problem with casting still exists.

Improvement:

Connection connection = DataSourceUtils.getConnection(dataSource);
connection = ((org.jboss.resource.adapter.jdbc.WrappedConnection)connection).getUnderlyingConnection();
OracleConnection oracleConnection = (OracleConnection) connection;

Error:

java.lang.ClassCastException: $Proxy287 cannot be cast to org.jboss.resource.adapter.jdbc.WrappedConnection
Roman
  • 1,121
  • 4
  • 23
  • 38
  • If you print out the exception message from the ClassCastException, it will tell you what the actual class of the connection object is. And that will help you (and us) understand why the cast is failing. – Stephen C Mar 18 '13 at 17:45
  • Find what object it is - usually System.out.println(connection) will show it. – igr Mar 18 '13 at 17:45
  • The object inside connection is org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6 – Roman Mar 19 '13 at 09:45

10 Answers10

44

The connection you are retrieving is probably a wrapped connection.

If you really need to get the underlying Oracle connection you should use:

if (connection.isWrapperFor(OracleConnection.class)){
   OracleConnection oracleConnection= connection.unwrap(OracleConnection.class);  
}else{
   // recover, not an oracle connection
}

The isWrapperFor and unwrap methods are available since Java 1.6, and should be meaningfully implemented by the A/S connection wrappers.

Carlo Pellegrini
  • 5,656
  • 40
  • 45
4

The connection pool usually has a wrapper around the real connection instance, that's why your cast fails.

What you are doing wouldn't work anyway, because the parameters in the properties instance are only checked when the connection is established. As you have a connection that is already active, it won't change anything.

You need tou use DBMS_APPLICATION_INFO.SET_CLIENT_INFO() in order to change this for an existing connection.

  • As I understand correctly to reach my goal I can do that in 2 ways: 1) by editing existing connection (via `DBMS_APPLICATION_INFO.SET_CLIENT_INFO()` or 2) by adding user name during connection establishment, right? Regarding to the second way where can I implement this approach in hibernate because I do not create a connection manually (I have configured EntityManagerFactory with hibernate bean injected)? – Roman Mar 19 '13 at 10:23
  • @Roman: sorry, I have no idea how to configure Hibernate. But if you use e.g. Tomcat's connection pool you can specify additionial properties. How exactly that needs to be done is documented in the manuals –  Mar 19 '13 at 13:27
3

This is just for people who come here via search on how to set metrics in OracleConnection, I spend great deal of time on this, so might help someone.

After you get your "connection" this should work:

DatabaseMetaData dmd = connection.getMetaData();
Connection metaDataConnection = null;

if(dmd != null)
{
    metaDataConnection = dmd.getConnection();
}

if(!(metaDataConnection instanceof OracleConnection))
{
    log.error("Connection is not instance of OracleConnection, returning");
    return; /* Not connection u want */
}

OracleConnection oraConnection = (OracleConnection)metaDataConnection;

String[] metrics = new String[END_TO_END_STATE_INDEX_MAX]; // Do the rest below...

It works for me for OracleConnection, but I face diff issue when setting metrics:

short zero = 0;
oraConnection.setEndToEndMetrics(metrics, zero);

After proxying connection via my method where I set metrics few times, I get:

java.sql.SQLRecoverableException: No more data to read from socket

But I think it has to do with some Spring wiring inits or connection pool.

Jenya G
  • 504
  • 3
  • 8
1

i had faced this issue when using spring to get connections. Typically , each layer adds a wrapper over the basic classes. i had just done connection.getClass().getName() to see the runtime type of the connection being retuned. It will be a Wrapper/proxy over which you can easily find the method to get the base OracleConnection type.

Akhilesh Singh
  • 2,548
  • 1
  • 13
  • 10
1

Try the following

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 is a specific implementation for Jboss called the JBossNativeJdbcExtractor

<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);
Sherin Syriac
  • 447
  • 6
  • 13
1

You can access the inner OracleObject inside a Wrapper, in this case the wrapper type is NewProxyConnection:

( I've used it in my project, it Worked ...no mistery, just use reflection)

Field[] fieldsConn= connection.getClass().getDeclaredFields();

Object innerConnObject = getFieldByName(fieldsConn,"inner").get(connection);


if(innerConnObject instanceof OracleConnection ){
  OracleConnection oracleConn = (OracleConnection)innerConnObject;
 //OracleConnection unwrap = ((OracleConnection)innerConnObject).unwrap();
  // now you have the OracleObject that the Wrapper 
}


//Method: Set properties of the ooject accessible.
 public static  Field getFieldByName(Field[] campos, String name) {
    Field f = null;
    for (Field campo : campos) {
        campo.setAccessible(true);
        if (campo.getName().equals(name)) {
            f = campo;
            break;
        }
    }
    return f;
 }
Ricuzzo
  • 11
  • 1
0

Not sure if my situation is related, but with my project, simply changing a database configuration setting actually causes unwrap to fail!

I'm using the Play framework with Scala; this works for me only when logSql=false:

db.withConnection { implicit c  =>
  val oracleConnection = c.unwrap(classOf[OracleConnection])
}

(this is just the Scala version of unwrapping an OracleConnection)

When I set logSql=true, I get:

com.sun.proxy.$Proxy17 cannot be cast to oracle.jdbc.OracleConnection java.lang.ClassCastException: com.sun.proxy.$Proxy17 cannot be cast to oracle.jdbc.OracleConnection

So something about the logSql configuration can actually cause unwrap to fail. No idea why.

With either configuration, my connection object is:

HikariProxyConnection@1880261898 wrapping oracle.jdbc.driver.T4CConnection@6b28f065

isWrapperFor(OracleConnection) is true in both cases

This happens with Hikari Connection Pool and Bone CP. Maybe it's a bug in Oracle JDBC?

Oracle JDBC Driver version according to MANIFEST.MF

Implementation-Version: 11.2.0.3.0
Repository-Id: JAVAVM_11.2.0.4.0_LINUX.X64_130711

Arlo
  • 1,331
  • 2
  • 15
  • 26
0

After trial and error. This way works:

        DelegatingConnection delConnection = new DelegatingConnection(dbcpConnection);
    oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();

But this way returned a null pointer for oraConnection:

DelegatingConnection delConnection = (DelegatingConnection) dbcpConnection;
    oraConnection = (oracle.jdbc.OracleConnection)delConnection.getInnermostDelegate();
frva
  • 76
  • 1
  • 5
-1

The following worked to get around AQ's TopicConnection.getTopicSession => JMS-112

//DEBUG: Native DataSource : weblogic.jdbc.common.internal.RmiDataSource
con = DataSource.getConnection();
debug("Generic SQL Connection: " + con.toString());               
//DEBUG: Generic Connection: weblogic.jdbc.wrapper.PoolConnection_oracle_jdbc_driver_T4CConnection
if (con != null && con.isWrapperFor(OracleConnection.class)) {
  WebLogicNativeJdbcExtractor wlne = new WebLogicNativeJdbcExtractor();//org.springframework to the rescue!!
  java.sql.Connection nativeCon = wlne.getNativeConnection(con);
  this.oraConnection = (OracleConnection) nativeCon;
  debug("Unwrapp SQL Connection: " + this.oraConnection.toString());
}

//DEBUG: Native Connection: oracle.jdbc.driver.T4CConnection è 

Now I could use this in the AQ-Factory w/o JMS-112

Rao
  • 20,781
  • 11
  • 57
  • 77
Dezman
  • 1
  • 1
-2

try casting like below

WrappedConnectionJDK6 wc = (WrappedConnectionJDK6) connection;
connection = wc.getUnderlyingConnection();
Bishan
  • 15,211
  • 52
  • 164
  • 258