Coverage Report - org.apache.ojb.broker.platforms.PlatformOracle9iImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
PlatformOracle9iImpl
N/A
N/A
7.833
 
 1  
 package org.apache.ojb.broker.platforms;
 2  
 
 3  
 /* Copyright 2003-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
 19  
 import org.apache.ojb.broker.metadata.ConnectionPoolDescriptor;
 20  
 import org.apache.ojb.broker.util.ClassHelper;
 21  
 import org.apache.ojb.broker.util.logging.Logger;
 22  
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 23  
 
 24  
 import java.io.ByteArrayInputStream;
 25  
 import java.lang.reflect.Field;
 26  
 import java.lang.reflect.Method;
 27  
 import java.lang.reflect.Modifier;
 28  
 import java.sql.Connection;
 29  
 import java.sql.DatabaseMetaData;
 30  
 import java.sql.PreparedStatement;
 31  
 import java.sql.SQLException;
 32  
 import java.sql.Statement;
 33  
 import java.sql.Types;
 34  
 import java.util.Collections;
 35  
 import java.util.Map;
 36  
 import java.util.WeakHashMap;
 37  
 
 38  
 /**
 39  
  * This class is a concrete implementation of <code>Platform</code>. Provides
 40  
  * an implementation that works around some issues with Oracle in general and
 41  
  * Oracle 9i's Thin driver in particular.
 42  
  *
 43  
  * NOTE: When using BEA WebLogic and BLOB/CLOB datatypes, the physical connection will be
 44  
  * used causing WebLogic to mark it as "infected" and discard it when
 45  
  * the logicical connection is closed. You can change this behavior by setting the
 46  
  * RemoveInfectedConnectionsEnabled attribute on a connection pool.
 47  
  * see <a href="http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1043646">WebLogic docs</a>.
 48  
  *
 49  
  * Optimization: Oracle Batching (not standard JDBC batching)
 50  
  * see http://technet.oracle.com/products/oracle9i/daily/jun07.html
 51  
  *
 52  
  * Optimization: Oracle Prefetching
 53  
  * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html
 54  
  *
 55  
  * Optimization: Oracle Statement Caching
 56  
  * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html
 57  
  *
 58  
  * TODO: Optimization: use ROWNUM to minimize the effects of not having server side cursors
 59  
  * see http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
 60  
  *
 61  
  * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
 62  
  * @author <a href="mailto:mkalen@apache.org">Martin Kal&eacute;n</a>
 63  
  * @author Contributions from: Erik Forkalsrud, Danilo Tommasina, Thierry Hanot, Don Lyon
 64  
  * @version CVS $Id: PlatformOracle9iImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
 65  
  * @see Platform
 66  
  * @see PlatformDefaultImpl
 67  
  * @see PlatformOracleImpl
 68  
  */
 69  
 public class PlatformOracle9iImpl extends PlatformOracleImpl
 70  
 {
 71  
     private Logger logger = LoggerFactory.getLogger(PlatformOracle9iImpl.class);
 72  
 
 73  
     /**
 74  
      * Number of cached statements per connection,
 75  
      * when using implicit caching with OracleConnections.
 76  
      * Set in {@link #initializeJdbcConnection}.
 77  
      * @see <a href="http://www.apache.org/~mkalen/ojb/broker-tests.html">Profiling page</a>
 78  
      * for a discussion re sizing
 79  
      */
 80  
     protected static final int STATEMENT_CACHE_SIZE = 10;
 81  
     /**
 82  
      * Number of rows pre-fetched by the JDBC-driver for each executed query,
 83  
      * when using Oracle row pre-fetching with OracleConnections.
 84  
      * Set in {@link #initializeJdbcConnection}.
 85  
      * <p>
 86  
      * <em>Note</em>: this setting can be overridden by specifying a
 87  
      * connection-pool attribute with name="jdbc.defaultRowPrefetch".
 88  
      * Oracle JDBC-driver default value=10.
 89  
      */
 90  
     protected static final int ROW_PREFETCH_SIZE = 20;
 91  
 
 92  
     // From Oracle9i JDBC Developer's Guide and Reference:
 93  
     // "Batch values between 5 and 30 tend to be the most effective."
 94  
     protected static final int STATEMENTS_PER_BATCH = 20;
 95  
     protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new WeakHashMap(STATEMENTS_PER_BATCH));
 96  
 
 97  
     protected static final Class[] PARAM_TYPE_EMPTY = {};
 98  
     protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
 99  
     protected static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE};
 100  
     protected static final Class[] PARAM_TYPE_STRING = {String.class};
 101  
 
 102  
     protected static final Object[] PARAM_EMPTY = new Object[]{};
 103  
     protected static final Object[] PARAM_STATEMENT_CACHE_SIZE = new Object[]{new Integer(STATEMENT_CACHE_SIZE)};
 104  
     protected static final Object[] PARAM_ROW_PREFETCH_SIZE = new Object[]{new Integer(ROW_PREFETCH_SIZE)};
 105  
     protected static final Object[] PARAM_STATEMENT_BATCH_SIZE = new Object[]{new Integer(STATEMENTS_PER_BATCH)};
 106  
     protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[]{Boolean.TRUE};
 107  
 
 108  
     protected static final String JBOSS_CONN_NAME =
 109  
             "org.jboss.resource.adapter.jdbc.WrappedConnection";
 110  
     protected static Class JBOSS_CONN_CLASS = null;
 111  
 
 112  
     protected static Class ORA_CONN_CLASS;
 113  
     protected static Class ORA_PS_CLASS;
 114  
     protected static Class ORA_CLOB_CLASS;
 115  
     protected static Class ORA_BLOB_CLASS;
 116  
     protected static Class[] PARAM_TYPE_INT_ORACLOB;
 117  
     protected static Class[] PARAM_TYPE_INT_ORABLOB;
 118  
     protected static Method METHOD_SET_STATEMENT_CACHE_SIZE;
 119  
     protected static Method METHOD_SET_IMPLICIT_CACHING_ENABLED;
 120  
     protected static Method METHOD_SET_ROW_PREFETCH;
 121  
     protected static Method METHOD_SET_BLOB = null;
 122  
     protected static Method METHOD_SET_CLOB = null;
 123  
     protected static boolean ORA_STATEMENT_CACHING_AVAILABLE;
 124  
     protected static boolean ORA_ROW_PREFETCH_AVAILABLE;
 125  
     protected static boolean ORA_CLOB_HANDLING_AVAILABLE;
 126  
     protected static boolean ORA_BLOB_HANDLING_AVAILABLE;
 127  
 
 128  
     /** Method names used by {@link #unwrapConnection}. */
 129  
     protected static final String UNWRAP_CONN_METHOD_NAMES[] =
 130  
             {
 131  
                 "unwrapCompletely"          /* Oracle 10g */,
 132  
                 "getInnermostDelegate"      /* Commons DBCP */,
 133  
                 "getUnderlyingConnection"   /* JBoss */,
 134  
                 "getVendorConnection"       /* BEA WebLogic */,
 135  
                 "getJDBC"                   /* P6Spy */
 136  
             };
 137  
     /**
 138  
      * Method parameter signature used by {@link #unwrapConnection} for corresponding
 139  
      * {@link #UNWRAP_CONN_METHOD_NAMES}-index.
 140  
      * If signature is not {@link #PARAM_TYPE_EMPTY}, the actual connection object
 141  
      * will be passed at runtime. (NB: Requires special handling of param type in constructor.)
 142  
      */
 143  
     protected static final Class[][] UNWRAP_CONN_PARAM_TYPES =
 144  
             {
 145  
                 null  /* Index 0 reserved for Oracle 10g - initialized in constructor */,
 146  
                 PARAM_TYPE_EMPTY            /* Commons DBCP */,
 147  
                 PARAM_TYPE_EMPTY            /* JBoss */,
 148  
                 PARAM_TYPE_EMPTY            /* BEA WebLogic */,
 149  
                 PARAM_TYPE_EMPTY            /* P6Spy */
 150  
             };
 151  
     /** Method names used by {@link #unwrapStatement}. */
 152  
     protected static final String UNWRAP_PS_METHOD_NAMES[] =
 153  
             {
 154  
                 "getInnermostDelegate"      /* Commons DBCP */,
 155  
                 "getUnderlyingStatement"    /* JBoss */,
 156  
                 "getJDBC"                   /* P6Spy */,
 157  
                 "ps"                                                /* XAPool, PATCHED: also genericUnwrap modified to unwrap fields */
 158  
             };
 159  
     /**
 160  
      * Method parameter signature used by {@link #unwrapStatement} for corresponding
 161  
      * {@link #UNWRAP_PS_METHOD_NAMES}-index.
 162  
      * If signature is not {@link #PARAM_TYPE_EMPTY}, the actual Statement object
 163  
      * will be passed at runtime. (NB: Requires special handling of param type in constructor.)
 164  
      */
 165  
     protected static final Class[][] UNWRAP_PS_PARAM_TYPES =
 166  
             {
 167  
                 PARAM_TYPE_EMPTY            /* Commons DBCP */,
 168  
                 PARAM_TYPE_EMPTY            /* JBoss */,
 169  
                 PARAM_TYPE_EMPTY            /* P6Spy */,
 170  
                 PARAM_TYPE_EMPTY                        /* XAPool */
 171  
             };
 172  
 
 173  
 
 174  
     /**
 175  
      * Default constructor.
 176  
      */
 177  
     public PlatformOracle9iImpl()
 178  
     {
 179  
         super();
 180  
     }
 181  
 
 182  
     /**
 183  
      * Enables Oracle statement caching and row prefetching if supported by the JDBC-driver.
 184  
      * @param jcd the OJB <code>JdbcConnectionDescriptor</code> (metadata) for the connection to be initialized
 185  
      * @param conn the <code>Connection</code>-object (physical) to be initialized
 186  
      * @see PlatformDefaultImpl#initializeJdbcConnection
 187  
      * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html">
 188  
      * Oracle TechNet Statement Caching Sample</a>
 189  
      * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html">
 190  
      * Oracle TechNet Row Pre-fetch Sample<a>
 191  
      */
 192  
     public void initializeJdbcConnection(final JdbcConnectionDescriptor jcd,
 193  
                                          final Connection conn)
 194  
             throws PlatformException
 195  
     {
 196  
         // Do all the generic initialization in PlatformDefaultImpl first
 197  
         super.initializeJdbcConnection(jcd, conn);
 198  
 
 199  
         // Check for managed environments known to reject Oracle extension at this level
 200  
         // (saves us from trying to unwrap just to catch exceptions next)
 201  
         final Class connClass = conn.getClass();
 202  
         if (JBOSS_CONN_CLASS != null && JBOSS_CONN_CLASS.isAssignableFrom(connClass))
 203  
         {
 204  
             if (logger.isDebugEnabled())
 205  
             {
 206  
                 logger.debug("JBoss detected, Oracle Connection tuning left to J2EE container.");
 207  
             }
 208  
             return;
 209  
         }
 210  
 
 211  
         // Check if this is a wrapped connection and if so unwrap it
 212  
         final Connection oraConn = unwrapConnection(conn);
 213  
         if (oraConn == null)
 214  
         {
 215  
             return;
 216  
         }
 217  
 
 218  
         // At this point we know that we have an OracleConnection instance and can thus
 219  
         // try to invoke methods via reflection (if available)
 220  
         if (ORA_STATEMENT_CACHING_AVAILABLE)
 221  
         {
 222  
             try
 223  
             {
 224  
                 // Set number of cached statements and enable implicit caching
 225  
                 METHOD_SET_STATEMENT_CACHE_SIZE.invoke(oraConn, PARAM_STATEMENT_CACHE_SIZE);
 226  
                 METHOD_SET_IMPLICIT_CACHING_ENABLED.invoke(oraConn, PARAM_BOOLEAN_TRUE);
 227  
             }
 228  
             catch (Exception e)
 229  
             {
 230  
                 if (logger.isDebugEnabled())
 231  
                 {
 232  
                     logger.debug("PlatformOracle9iImpl could not enable Oracle statement caching."
 233  
                                  + " Original/unwrapped connection classes="
 234  
                                  + connClass.getName() + "/" + oraConn.getClass().getName());
 235  
                 }
 236  
             }
 237  
         }
 238  
 
 239  
         /*
 240  
         mkalen: Note from the Oracle documentation:
 241  
             Do not mix the JDBC 2.0 fetch size API and the Oracle row prefetching API
 242  
             in your application. You can use one or the other, but not both.
 243  
         */
 244  
         final ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor();
 245  
         final int cpdFetchSizeHint = cpd.getFetchSize();
 246  
         if (cpdFetchSizeHint == 0 && ORA_ROW_PREFETCH_AVAILABLE)
 247  
         {
 248  
             try
 249  
             {
 250  
                 final String prefetchFromJcd;
 251  
                 prefetchFromJcd = cpd.getJdbcProperties().getProperty("defaultRowPrefetch");
 252  
                 if (prefetchFromJcd == null)
 253  
                 {
 254  
                     METHOD_SET_ROW_PREFETCH.invoke(oraConn, PARAM_ROW_PREFETCH_SIZE);
 255  
                 }
 256  
                 // Else, number of prefetched rows were set via Properties on Connection
 257  
             }
 258  
             catch (Exception e)
 259  
             {
 260  
                 if (logger.isDebugEnabled())
 261  
                 {
 262  
                     logger.debug("PlatformOracle9iImpl could not enable Oracle row pre-fetching."
 263  
                                  + "Original/unwrapped connection classes="
 264  
                                  + connClass.getName() + "/" + oraConn.getClass().getName());
 265  
                 }
 266  
             }
 267  
         }
 268  
     }
 269  
 
 270  
     /**
 271  
      * Performs platform-specific operations on each statement.
 272  
      * @param stmt the statement just created
 273  
      */
 274  
     public void afterStatementCreate(Statement stmt)
 275  
     {
 276  
         // mkalen:  do NOT call super#afterStatementCreate since escape processing for SQL92
 277  
         //          syntax is enabled by default for Oracle9i and higher, and explicit calls
 278  
         //          to setEscapeProcessing for PreparedStatements will make Oracle 10g JDBC-
 279  
         //          driver throw exceptions (and is functionally useless).
 280  
     }
 281  
 
 282  
     /**
 283  
      * Try Oracle update batching and call setExecuteBatch or revert to
 284  
      * JDBC update batching. See 12-2 Update Batching in the Oracle9i
 285  
      * JDBC Developer's Guide and Reference.
 286  
      * @param stmt the prepared statement to be used for batching
 287  
      * @throws PlatformException upon JDBC failure
 288  
      */
 289  
     public void beforeBatch(PreparedStatement stmt) throws PlatformException
 290  
     {
 291  
         // Check for Oracle batching support
 292  
         final Method methodSetExecuteBatch;
 293  
         final Method methodSendBatch;
 294  
         methodSetExecuteBatch = ClassHelper.getMethod(stmt, "setExecuteBatch", PARAM_TYPE_INTEGER);
 295  
         methodSendBatch = ClassHelper.getMethod(stmt, "sendBatch", null);
 296  
 
 297  
         final boolean statementBatchingSupported = methodSetExecuteBatch != null && methodSendBatch != null;
 298  
         if (statementBatchingSupported)
 299  
         {
 300  
             try
 301  
             {
 302  
                 // Set number of statements per batch
 303  
                 methodSetExecuteBatch.invoke(stmt, PARAM_STATEMENT_BATCH_SIZE);
 304  
                 m_batchStatementsInProgress.put(stmt, methodSendBatch);
 305  
             }
 306  
             catch (Exception e)
 307  
             {
 308  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 309  
             }
 310  
         }
 311  
         else
 312  
         {
 313  
             super.beforeBatch(stmt);
 314  
         }
 315  
     }
 316  
 
 317  
     /**
 318  
      * Try Oracle update batching and call executeUpdate or revert to
 319  
      * JDBC update batching.
 320  
      * @param stmt the statement beeing added to the batch
 321  
      * @throws PlatformException upon JDBC failure
 322  
      */
 323  
     public void addBatch(PreparedStatement stmt) throws PlatformException
 324  
     {
 325  
         // Check for Oracle batching support
 326  
         final boolean statementBatchingSupported = m_batchStatementsInProgress.containsKey(stmt);
 327  
         if (statementBatchingSupported)
 328  
         {
 329  
             try
 330  
             {
 331  
                 stmt.executeUpdate();
 332  
             }
 333  
             catch (SQLException e)
 334  
             {
 335  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 336  
             }
 337  
         }
 338  
         else
 339  
         {
 340  
             super.addBatch(stmt);
 341  
         }
 342  
     }
 343  
 
 344  
     /**
 345  
      * Try Oracle update batching and call sendBatch or revert to
 346  
      * JDBC update batching.
 347  
      * @param stmt the batched prepared statement about to be executed
 348  
      * @return always <code>null</code> if Oracle update batching is used,
 349  
      * since it is impossible to dissolve total row count into distinct
 350  
      * statement counts. If JDBC update batching is used, an int array is
 351  
      * returned containing number of updated rows for each batched statement.
 352  
      * @throws PlatformException upon JDBC failure
 353  
      */
 354  
     public int[] executeBatch(PreparedStatement stmt) throws PlatformException
 355  
     {
 356  
         // Check for Oracle batching support
 357  
         final Method methodSendBatch = (Method) m_batchStatementsInProgress.remove(stmt);
 358  
         final boolean statementBatchingSupported = methodSendBatch != null;
 359  
 
 360  
         int[] retval = null;
 361  
         if (statementBatchingSupported)
 362  
         {
 363  
             try
 364  
             {
 365  
                 // sendBatch() returns total row count as an Integer
 366  
                 methodSendBatch.invoke(stmt, null);
 367  
             }
 368  
             catch (Exception e)
 369  
             {
 370  
                 throw new PlatformException(e.getLocalizedMessage(), e);
 371  
             }
 372  
         }
 373  
         else
 374  
         {
 375  
             retval = super.executeBatch(stmt);
 376  
         }
 377  
         return retval;
 378  
     }
 379  
 
 380  
     /** @see Platform#setObjectForStatement */
 381  
     public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException
 382  
     {
 383  
         // Check for Oracle JDBC-driver LOB-support
 384  
         final Statement oraStmt;
 385  
         final Connection oraConn;
 386  
         final boolean oraLargeLobSupportAvailable;
 387  
         if (sqlType == Types.CLOB || sqlType == Types.BLOB)
 388  
         {
 389  
             oraStmt = unwrapStatement(ps);
 390  
             oraConn = unwrapConnection(ps.getConnection());
 391  
             oraLargeLobSupportAvailable =
 392  
                     oraStmt != null && oraConn != null &&
 393  
                     (sqlType == Types.CLOB ? ORA_CLOB_HANDLING_AVAILABLE : ORA_BLOB_HANDLING_AVAILABLE);
 394  
         }
 395  
         else
 396  
         {
 397  
             oraStmt = null;
 398  
             oraConn = null;
 399  
             oraLargeLobSupportAvailable = false;
 400  
         }
 401  
 
 402  
         // Type-specific Oracle conversions
 403  
         if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY)) && (value instanceof byte[]))
 404  
         {
 405  
             byte buf[] = (byte[]) value;
 406  
             ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
 407  
             super.changePreparedStatementResultSetType(ps);
 408  
             ps.setBinaryStream(index, inputStream, buf.length);
 409  
         }
 410  
         else if (value instanceof Double)
 411  
         {
 412  
             // workaround for the bug in Oracle thin driver
 413  
             ps.setDouble(index, ((Double) value).doubleValue());
 414  
         }
 415  
         else if (sqlType == Types.BIGINT && value instanceof Integer)
 416  
         {
 417  
             // workaround: Oracle thin driver problem when expecting long
 418  
             ps.setLong(index, ((Integer) value).intValue());
 419  
         }
 420  
         else if (sqlType == Types.INTEGER && value instanceof Long)
 421  
         {
 422  
             ps.setLong(index, ((Long) value).longValue());
 423  
         }
 424  
         else if (sqlType == Types.CLOB && oraLargeLobSupportAvailable && value instanceof String)
 425  
         {
 426  
             // TODO: If using Oracle update batching with the thin driver, throw exception on 4k limit
 427  
             try
 428  
             {
 429  
                 Object clob = Oracle9iLobHandler.createCLOBFromString(oraConn, (String) value);
 430  
                 METHOD_SET_CLOB.invoke(oraStmt, new Object[]{new Integer(index), clob});
 431  
             }
 432  
             catch (Exception e)
 433  
             {
 434  
                 throw new SQLException(e.getLocalizedMessage());
 435  
             }
 436  
         }
 437  
         else if (sqlType == Types.BLOB && oraLargeLobSupportAvailable && value instanceof byte[])
 438  
         {
 439  
             // TODO: If using Oracle update batching with the thin driver, throw exception on 2k limit
 440  
             try
 441  
             {
 442  
                 Object blob = Oracle9iLobHandler.createBLOBFromByteArray(oraConn, (byte[]) value);
 443  
                 METHOD_SET_BLOB.invoke(oraStmt, new Object[]{new Integer(index), blob});
 444  
             }
 445  
             catch (Exception e)
 446  
             {
 447  
                 throw new SQLException(e.getLocalizedMessage());
 448  
             }
 449  
         }
 450  
         else
 451  
         {
 452  
             // Fall-through to superclass
 453  
             super.setObjectForStatement(ps, index, value, sqlType);
 454  
         }
 455  
     }
 456  
 
 457  
     /**
 458  
      * Get join syntax type for this RDBMS.
 459  
      *
 460  
      * @return SQL92_NOPAREN_JOIN_SYNTAX
 461  
      */
 462  
     public byte getJoinSyntaxType()
 463  
     {
 464  
         return SQL92_NOPAREN_JOIN_SYNTAX;
 465  
     }
 466  
 
 467  
     /**
 468  
      * Return an OracleConnection after trying to unwrap from known Connection wrappers.
 469  
      * @param conn the connection to unwrap (if needed)
 470  
      * @return OracleConnection or null if not able to unwrap
 471  
      */
 472  
     protected Connection unwrapConnection(Connection conn)
 473  
     {
 474  
         Object unwrapped = genericUnwrap(ORA_CONN_CLASS, conn, UNWRAP_CONN_METHOD_NAMES, UNWRAP_CONN_PARAM_TYPES);
 475  
         try {
 476  
                     // We haven't received a different Connection, so we'll assume that there's
 477  
                     // some additional proxying going on. Let's check whether we get something
 478  
                     // different back from the DatabaseMetaData.getConnection() call.
 479  
                     DatabaseMetaData metaData = conn.getMetaData();
 480  
                     // The following check is only really there for mock Connections
 481  
                     // which might not carry a DatabaseMetaData instance.
 482  
                     if (metaData != null) {
 483  
                             Connection metaCon = metaData.getConnection();
 484  
                             if (metaCon != conn) {
 485  
                                     // We've received a different Connection there:
 486  
                                     // Let's retry the native extraction process with it.
 487  
                                     unwrapped = genericUnwrap(ORA_CONN_CLASS, metaCon, UNWRAP_CONN_METHOD_NAMES, UNWRAP_CONN_PARAM_TYPES);
 488  
                             }
 489  
                     }
 490  
             } catch (SQLException e) {
 491  
                     if (logger.isDebugEnabled())
 492  
             {
 493  
                 logger.debug("Failed attempting to unwrap connection via database metadata.", e);
 494  
             }
 495  
             }
 496  
         if (unwrapped == null)
 497  
         {
 498  
             // mkalen:  only log this as debug since it will be logged for every connection
 499  
             //          (ie only useful during development).
 500  
             if (logger.isDebugEnabled())
 501  
             {
 502  
                 logger.debug("PlatformOracle9iImpl could not unwrap " + conn.getClass().getName() +
 503  
                              ", Oracle-extensions disabled.");
 504  
             }
 505  
         }
 506  
         return (Connection) unwrapped;
 507  
     }
 508  
 
 509  
     /**
 510  
      * Return an OraclePreparedStatement after trying to unwrap from known Statement wrappers.
 511  
      * @param ps the PreparedStatement to unwrap (if needed)
 512  
      * @return OraclePreparedStatement or null if not able to unwrap
 513  
      */
 514  
     protected Statement unwrapStatement(Statement ps)
 515  
     {
 516  
         final Object unwrapped;
 517  
         unwrapped = genericUnwrap(ORA_PS_CLASS, ps, UNWRAP_PS_METHOD_NAMES, UNWRAP_PS_PARAM_TYPES);
 518  
         if (unwrapped == null)
 519  
         {
 520  
             // mkalen:  only log this as debug since it will be logged for every connection
 521  
             //          (ie only useful during development).
 522  
             if (logger.isDebugEnabled())
 523  
             {
 524  
                 logger.debug("PlatformOracle9iImpl could not unwrap " + ps.getClass().getName() +
 525  
                              ", large CLOB/BLOB support disabled.");
 526  
             }
 527  
         }
 528  
         return (Statement) unwrapped;
 529  
     }
 530  
 
 531  
     protected Object genericUnwrap(Class classToMatch, Object toUnwrap,
 532  
                                    String[] methodNameCandidates,
 533  
                                    Class[][] methodTypeCandidates)
 534  
     {
 535  
         if (classToMatch == null)
 536  
         {
 537  
             return null;
 538  
         }
 539  
 
 540  
         Object unwrapped = null;
 541  
         final Class psClass = toUnwrap.getClass();
 542  
         if (classToMatch.isAssignableFrom(psClass))
 543  
         {
 544  
             return toUnwrap;
 545  
         }
 546  
         try
 547  
         {
 548  
             String methodName;
 549  
             Class[] paramTypes;
 550  
             Object[] args;
 551  
             for (int i = 0; i < methodNameCandidates.length; i++)
 552  
             {
 553  
                 methodName = methodNameCandidates[i];
 554  
                 paramTypes = methodTypeCandidates[i];
 555  
                 final Method method = ClassHelper.getMethod(toUnwrap, methodName, paramTypes);
 556  
                 if (method != null)
 557  
                 {
 558  
                     args = paramTypes == PARAM_TYPE_EMPTY ? PARAM_EMPTY : new Object[]{ toUnwrap };
 559  
                     unwrapped = method.invoke(toUnwrap, args);
 560  
                                 } else {
 561  
                                         // Check for a field:
 562  
                                         // PACTHED: This next section was added as a patch to allow for XAPool's org.enhydra.jdbc.core.CorePreparedStatement
 563  
                                         // which externalizes it's underlying prepared statement as a public field called 'ps'
 564  
                                         final Field field = ClassHelper.getField(psClass, methodName);
 565  
                                         if (field != null) {
 566  
                                                 unwrapped = field.get(toUnwrap);
 567  
                                         }
 568  
                                 }
 569  
                 if (unwrapped != null)
 570  
                 {
 571  
                     if (classToMatch.isAssignableFrom(unwrapped.getClass()))
 572  
                     {
 573  
                         return unwrapped;
 574  
                     }
 575  
                     // When using eg both DBCP and P6Spy we have to recursively unwrap
 576  
                     return genericUnwrap(classToMatch, unwrapped,
 577  
                             methodNameCandidates, methodTypeCandidates);
 578  
                 }
 579  
             }
 580  
             
 581  
             // PATCHED: Try to use JDBC 4 unwrap mechanism
 582  
             final Method jdbcUnwrapMethod = ClassHelper.getMethod(toUnwrap, "unwrap", new Class[] { Class.class });
 583  
 
 584  
             if (jdbcUnwrapMethod != null && !Modifier.isAbstract(jdbcUnwrapMethod.getModifiers())) {
 585  
                     Class jdbcClassToMatch = null;  // we're going to find 
 586  
                     if (java.sql.Connection.class.isAssignableFrom(classToMatch)) {
 587  
                             jdbcClassToMatch = java.sql.Connection.class;
 588  
                     } else if (java.sql.PreparedStatement.class.isAssignableFrom(classToMatch)) {
 589  
                             jdbcClassToMatch = java.sql.PreparedStatement.class;
 590  
                     } else if (java.sql.Statement.class.isAssignableFrom(classToMatch)) {
 591  
                             jdbcClassToMatch = java.sql.Statement.class;
 592  
                     }
 593  
 
 594  
                     Object result = null;
 595  
                     if (jdbcClassToMatch != null) {
 596  
                             result = jdbcUnwrapMethod.invoke(toUnwrap, jdbcClassToMatch);
 597  
                             if (result != null) {
 598  
                                     // Sometimes we actually get a proxy back
 599  
                                     if (classToMatch.isAssignableFrom(result.getClass()))
 600  
                                     {
 601  
                                             return result;
 602  
                                     }
 603  
                                     // When using eg both DBCP and P6Spy we have to recursively unwrap
 604  
                                     return genericUnwrap(classToMatch, result,
 605  
                                                     methodNameCandidates, methodTypeCandidates);
 606  
                             }
 607  
                     }
 608  
             } // didn't work, fall back.
 609  
         }
 610  
         catch (Exception e)
 611  
         {
 612  
             // ignore
 613  
             if (logger.isDebugEnabled())
 614  
             {
 615  
                 logger.debug("genericUnwrap failed", e);
 616  
             }
 617  
         }
 618  
         return null;
 619  
     }
 620  
 
 621  
     /**
 622  
      * Initializes static variables needed for Oracle-extensions and large BLOB/CLOB support.
 623  
      */
 624  
     protected void initOracleReflectedVars()
 625  
     {
 626  
         super.initOracleReflectedVars();
 627  
         try
 628  
         {
 629  
             /*
 630  
             Check for Oracle-specific classes, OracleConnection-specific
 631  
             statement caching/row pre-fetch methods and Oracle BLOB/CLOB access methods.
 632  
             We can do this in constructor in spite of possible mixing of instance being
 633  
             able vs unable passed at runtime (since withouth these classes and methods
 634  
             it's impossible to enable ORA-extensions at all even if instances are capable).
 635  
             */
 636  
             ORA_CONN_CLASS = ClassHelper.getClass("oracle.jdbc.OracleConnection", false);
 637  
             ORA_PS_CLASS = ClassHelper.getClass("oracle.jdbc.OraclePreparedStatement", false);
 638  
             ORA_CLOB_CLASS = ClassHelper.getClass("oracle.sql.CLOB", false);
 639  
             ORA_BLOB_CLASS = ClassHelper.getClass("oracle.sql.BLOB", false);
 640  
             PARAM_TYPE_INT_ORACLOB = new Class[]{ Integer.TYPE, ORA_CLOB_CLASS };
 641  
             PARAM_TYPE_INT_ORABLOB = new Class[]{ Integer.TYPE, ORA_BLOB_CLASS };
 642  
 
 643  
             // Index 0 reserved for Oracle 10g
 644  
             UNWRAP_CONN_PARAM_TYPES[0] = new Class[]{ ORA_CONN_CLASS };
 645  
 
 646  
             METHOD_SET_STATEMENT_CACHE_SIZE =
 647  
                     ClassHelper.getMethod(ORA_CONN_CLASS, "setStatementCacheSize", PARAM_TYPE_INTEGER);
 648  
             METHOD_SET_IMPLICIT_CACHING_ENABLED =
 649  
                     ClassHelper.getMethod(ORA_CONN_CLASS, "setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN);
 650  
             METHOD_SET_ROW_PREFETCH = ClassHelper.getMethod(ORA_CONN_CLASS, "setDefaultRowPrefetch", PARAM_TYPE_INTEGER);
 651  
             METHOD_SET_CLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setCLOB", PARAM_TYPE_INT_ORACLOB);
 652  
             METHOD_SET_BLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setBLOB", PARAM_TYPE_INT_ORABLOB);
 653  
 
 654  
             ORA_STATEMENT_CACHING_AVAILABLE =
 655  
                     METHOD_SET_STATEMENT_CACHE_SIZE != null && METHOD_SET_IMPLICIT_CACHING_ENABLED != null;
 656  
             ORA_ROW_PREFETCH_AVAILABLE = METHOD_SET_ROW_PREFETCH != null;
 657  
             ORA_CLOB_HANDLING_AVAILABLE = METHOD_SET_CLOB != null;
 658  
             ORA_BLOB_HANDLING_AVAILABLE = METHOD_SET_BLOB != null;
 659  
         }
 660  
         catch (ClassNotFoundException e)
 661  
         {
 662  
             // ignore (we tried...)
 663  
         }
 664  
         // Isolated checks for other connection classes (OK when not found)
 665  
         try
 666  
         {
 667  
             JBOSS_CONN_CLASS = ClassHelper.getClass(JBOSS_CONN_NAME, false);
 668  
         }
 669  
         catch (ClassNotFoundException e)
 670  
         {
 671  
             // ignore (no problem)
 672  
         }
 673  
     }
 674  
 
 675  
 }