001 package org.apache.ojb.broker.platforms;
002
003 /* Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
019 import org.apache.ojb.broker.metadata.ConnectionPoolDescriptor;
020 import org.apache.ojb.broker.util.ClassHelper;
021 import org.apache.ojb.broker.util.logging.Logger;
022 import org.apache.ojb.broker.util.logging.LoggerFactory;
023
024 import java.io.ByteArrayInputStream;
025 import java.lang.reflect.Field;
026 import java.lang.reflect.Method;
027 import java.lang.reflect.Modifier;
028 import java.sql.Connection;
029 import java.sql.DatabaseMetaData;
030 import java.sql.PreparedStatement;
031 import java.sql.SQLException;
032 import java.sql.Statement;
033 import java.sql.Types;
034 import java.util.Collections;
035 import java.util.Map;
036 import java.util.WeakHashMap;
037
038 /**
039 * This class is a concrete implementation of <code>Platform</code>. Provides
040 * an implementation that works around some issues with Oracle in general and
041 * Oracle 9i's Thin driver in particular.
042 *
043 * NOTE: When using BEA WebLogic and BLOB/CLOB datatypes, the physical connection will be
044 * used causing WebLogic to mark it as "infected" and discard it when
045 * the logicical connection is closed. You can change this behavior by setting the
046 * RemoveInfectedConnectionsEnabled attribute on a connection pool.
047 * see <a href="http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1043646">WebLogic docs</a>.
048 *
049 * Optimization: Oracle Batching (not standard JDBC batching)
050 * see http://technet.oracle.com/products/oracle9i/daily/jun07.html
051 *
052 * Optimization: Oracle Prefetching
053 * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html
054 *
055 * Optimization: Oracle Statement Caching
056 * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html
057 *
058 * TODO: Optimization: use ROWNUM to minimize the effects of not having server side cursors
059 * see http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
060 *
061 * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
062 * @author <a href="mailto:mkalen@apache.org">Martin Kalén</a>
063 * @author Contributions from: Erik Forkalsrud, Danilo Tommasina, Thierry Hanot, Don Lyon
064 * @version CVS $Id: PlatformOracle9iImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
065 * @see Platform
066 * @see PlatformDefaultImpl
067 * @see PlatformOracleImpl
068 */
069 public class PlatformOracle9iImpl extends PlatformOracleImpl
070 {
071 private Logger logger = LoggerFactory.getLogger(PlatformOracle9iImpl.class);
072
073 /**
074 * Number of cached statements per connection,
075 * when using implicit caching with OracleConnections.
076 * Set in {@link #initializeJdbcConnection}.
077 * @see <a href="http://www.apache.org/~mkalen/ojb/broker-tests.html">Profiling page</a>
078 * for a discussion re sizing
079 */
080 protected static final int STATEMENT_CACHE_SIZE = 10;
081 /**
082 * Number of rows pre-fetched by the JDBC-driver for each executed query,
083 * when using Oracle row pre-fetching with OracleConnections.
084 * Set in {@link #initializeJdbcConnection}.
085 * <p>
086 * <em>Note</em>: this setting can be overridden by specifying a
087 * connection-pool attribute with name="jdbc.defaultRowPrefetch".
088 * Oracle JDBC-driver default value=10.
089 */
090 protected static final int ROW_PREFETCH_SIZE = 20;
091
092 // From Oracle9i JDBC Developer's Guide and Reference:
093 // "Batch values between 5 and 30 tend to be the most effective."
094 protected static final int STATEMENTS_PER_BATCH = 20;
095 protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new WeakHashMap(STATEMENTS_PER_BATCH));
096
097 protected static final Class[] PARAM_TYPE_EMPTY = {};
098 protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
099 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 }