javaoracle-databaseopen-liberty

ClassCastException inserting data with Oracle XSU in OpenLiberty


We are migrating a legacy application from IBM WebSphere to OpenLiberty. Some functionalities use Oracle XSU to perform operations with the database.

I have managed to obtain the Oracle-specific connection by performing an unwrap operation over the java.sql.Connection.

The error is raised when trying to insert data through XSU:

oracle.xml.sql.OracleXMLSQLException:
com.ibm.ws.rsadapter.jdbc.v42.WSJdbc42PreparedStatement cannot be cast to oracle.jdbc.OraclePreparedStatement

My server.xml:

<library id="jdbcLib">
        <fileset dir="C:\Users\uC260112\.m2\repository\com\oracle\ojdbc6\11.2.0.1.0"/>
</library>

<dataSource id="gcmsXAWIPDS" jndiName="jdbc/gcmsXAWIPDS" type="javax.sql.XADataSource">
        <jdbcDriver javax.sql.XADataSource="oracle.jdbc.xa.client.OracleXADataSource" libraryRef="jdbcLib"/>
        <properties.oracle URL="jdbc:oracle:thin:@c588cwegcmd03.int.xxxx.com:1521/xxxxx.int.xxxxx.com" password="xxxxxx" user="xxxxxx"/>
        <connectionManager maxPoolSize="100" minPoolSize="1"/>
</dataSource>

<enterpriseApplication id="xxxxx" location="xxxxx-1.0.ear" name="xxxxx">
        <classloader commonLibraryRef="jdbcLib"/>
</enterpriseApplication>

Obtain and unwrap the connection:

conId = myXAPool.obtainConnection(null, null);
Connection connection = myXAPool.getConnection(conId);

OracleConnection oracleConnection = connection.unwrap(OracleConnection.class); 

Try to insert using XSU:

OracleXMLSave sav = null;

    try {
        sav = getNewOracleXMLSave(oracleConnection, tableName);
        sav.setRowTag(rowDelimiter);
        sav.setDateFormat("dd/MM/yyyy HH:mm:ss");

        sav.insertXML(xsuXml); //Exception here
    } catch (Exception ex) {

Does anyone have an idea how to solve it?


Solution

  • Assuming the problem is that Oracle's XSU library is not unwrapping the PreparedStatements (thanks @njr), I have found a solution that works.

    Build a proxy over the oracleConnection, and pass it to the XSU library instead of the actual oracleConnection:

    OracleConnection oracleConnection = connection.unwrap(OracleConnection.class); 
    
    OracleConnection proxy = (OracleConnection) Proxy.newProxyInstance(
                            OracleConnection.class.getClassLoader(),
                            new Class<?>[]{OracleConnection.class},
                            new OracleConnectionInvocationHandler(oracleConnection));
    
    OracleXMLSave sav = null;
    
            try {
                sav = getNewOracleXMLSave(proxy, tableName);
                sav.setRowTag(rowDelimiter);
                sav.setDateFormat("dd/MM/yyyy HH:mm:ss");
    
                sav.insertXML(xsuXml);
    

    That way, I can intercept the calls that the XSU library makes to the prepareStatement method, and do the unwrap before returning the object to the library. This is the proxy handler:

    public class OracleConnectionInvocationHandler implements InvocationHandler {
        private final OracleConnection target;
        private SPELogger systemLog = null;
    
        public OracleConnectionInvocationHandler(OracleConnection target) {
            systemLog = AppLogger.getInstance();
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("prepareStatement".equals(method.getName())) {
    
                PreparedStatement preparedStatement = (PreparedStatement) method.invoke(target, args);
    
                systemLog.logInfo(this, "Making PreparedStatement unwrap through OracleConnectionInvocationHandler");
    
                return preparedStatement.unwrap(oracle.jdbc.OraclePreparedStatement.class);
    
            }
    
            return method.invoke(target, args);
        }
    }
    

    In the meantime, I'm still in contact with Oracle to see if there is an XSU library that handles this properly. But the solution with the proxy works perfectly.