From ba5077d20057208bb783487b9713791b41077fa4 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Wed, 2 Jan 2019 12:09:53 +0100 Subject: [PATCH] [#8145] Add an UnwrapProvider SPI that allows for unwrapping underlying JDBC types through proprietary APIs --- .../src/main/java/org/jooq/Configuration.java | 51 ++++- jOOQ/src/main/java/org/jooq/Unwrapper.java | 78 +++++++ .../main/java/org/jooq/UnwrapperProvider.java | 58 +++++ .../org/jooq/impl/DefaultConfiguration.java | 157 +++++++++++++- .../org/jooq/impl/DefaultExecuteContext.java | 108 +--------- .../jooq/impl/DefaultUnwrapperProvider.java | 201 ++++++++++++++++++ .../jooq/tools/jdbc/MockConfiguration.java | 27 +++ 7 files changed, 574 insertions(+), 106 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/Unwrapper.java create mode 100644 jOOQ/src/main/java/org/jooq/UnwrapperProvider.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultUnwrapperProvider.java diff --git a/jOOQ/src/main/java/org/jooq/Configuration.java b/jOOQ/src/main/java/org/jooq/Configuration.java index ec11678e77..fccdf32773 100644 --- a/jOOQ/src/main/java/org/jooq/Configuration.java +++ b/jOOQ/src/main/java/org/jooq/Configuration.java @@ -305,6 +305,12 @@ public interface Configuration extends Serializable { */ DiagnosticsListenerProvider[] diagnosticsListenerProviders(); + /** + * Get the configured UnwrapperProvider from this + * configuration. + */ + UnwrapperProvider unwrapperProvider(); + /** * Get this configuration's underlying record mapper provider. */ @@ -691,7 +697,7 @@ public interface Configuration extends Serializable { Configuration set(DiagnosticsListener... newDiagnosticsListeners); /** - * Change this configuration to hold a new diagnostics listener providers. + * Change this configuration to hold new diagnostics listener providers. *

* This method is not thread-safe and should not be used in globally * available Configuration objects. @@ -702,6 +708,30 @@ public interface Configuration extends Serializable { */ Configuration set(DiagnosticsListenerProvider... newDiagnosticsListenerProviders); + /** + * Change this configuration to hold a new unwrapper. + *

+ * This method is not thread-safe and should not be used in globally + * available Configuration objects. + * + * @param newUnwrapper The new unwrapper to be contained in the changed + * configuration. + * @return The changed configuration. + */ + Configuration set(Unwrapper newUnwrapper); + + /** + * Change this configuration to hold a new unwrapper provider. + *

+ * This method is not thread-safe and should not be used in globally + * available Configuration objects. + * + * @param newUnwrapperProvider The new unwrapper provider to be contained in + * the changed configuration. + * @return The changed configuration. + */ + Configuration set(UnwrapperProvider newUnwrapperProvider); + /** * Change this configuration to hold a new converter provider. *

@@ -987,6 +1017,25 @@ public interface Configuration extends Serializable { */ Configuration derive(DiagnosticsListenerProvider... newDiagnosticsListenerProviders); + /** + * Create a derived configuration from this one, with a new unwrapper. + * + * @param newUnwrapper The new unwrapper to be contained in the derived + * configuration. + * @return The derived configuration. + */ + Configuration derive(Unwrapper newUnwrapper); + + /** + * Create a derived configuration from this one, with a new unwrapper + * provider. + * + * @param newUnwrapperProvider The new unwrapper provider to be contained in + * the derived configuration. + * @return The derived configuration. + */ + Configuration derive(UnwrapperProvider newUnwrapperProvider); + /** * Create a derived configuration from this one, with new converter * provider. diff --git a/jOOQ/src/main/java/org/jooq/Unwrapper.java b/jOOQ/src/main/java/org/jooq/Unwrapper.java new file mode 100644 index 0000000000..36a046144a --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Unwrapper.java @@ -0,0 +1,78 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import java.sql.Connection; +import java.sql.Wrapper; + +/** + * An unwrapper SPI that can be used to override the default unwrapping + * algorithm. + *

+ * In some cases, jOOQ needs to get access to the native JDBC driver APIs in + * order to call vendor specific methods, such as Oracle's + * OracleConnection.createARRAY(). jOOQ doesn't expect clients to + * provide a native JDBC {@link Connection} through the + * {@link ConnectionProvider} SPI. Implementations may well provide jOOQ with + * some proxy that implements things like logging, thread-bound + * transactionality, connection pooling, etc. + *

+ * In order to access the native API, {@link Wrapper#unwrap(Class)} needs to be + * called, or in some cases, when third party libraries do not properly + * implement this contract, some specific methods are called reflectively, + * including: + *

+ *

+ *

+ * Not all such third party libraries are "known" to jOOQ, so clients can + * implement their own unwrapper to support theirs. + * + * @author Lukas Eder + */ +public interface Unwrapper { + + /** + * Unwrap a wrapped type from a JDBC {@link Wrapper}. + */ + T unwrap(Wrapper wrapper, Class iface); +} diff --git a/jOOQ/src/main/java/org/jooq/UnwrapperProvider.java b/jOOQ/src/main/java/org/jooq/UnwrapperProvider.java new file mode 100644 index 0000000000..9754a210be --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/UnwrapperProvider.java @@ -0,0 +1,58 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import java.sql.Wrapper; + +/** + * A provider for the {@link Unwrapper} SPI which is used to override the + * default behaviour when unwrapping JDBC types through + * {@link Wrapper#unwrap(Class)}. + * + * @author Lukas Eder + */ + +@FunctionalInterface + +public interface UnwrapperProvider { + + /** + * Provide an unwrapper for JDBC types. + */ + Unwrapper provide(); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java index 2ec973fcff..91d44277c0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java @@ -75,6 +75,8 @@ import org.jooq.SQLDialect; import org.jooq.TransactionListener; import org.jooq.TransactionListenerProvider; import org.jooq.TransactionProvider; +import org.jooq.Unwrapper; +import org.jooq.UnwrapperProvider; import org.jooq.VisitListener; import org.jooq.VisitListenerProvider; import org.jooq.conf.Settings; @@ -91,7 +93,6 @@ import org.jooq.impl.Tools.DataCacheKey; * * @author Lukas Eder */ -@SuppressWarnings("deprecation") public class DefaultConfiguration implements Configuration { /** @@ -118,6 +119,7 @@ public class DefaultConfiguration implements Configuration { private transient VisitListenerProvider[] visitListenerProviders; private transient TransactionListenerProvider[] transactionListenerProviders; private transient DiagnosticsListenerProvider[] diagnosticsListenerProviders; + private transient UnwrapperProvider unwrapperProvider; private transient ConverterProvider converterProvider; // [#7062] Apart from the possibility of containing user defined objects, the data @@ -163,6 +165,11 @@ public class DefaultConfiguration implements Configuration { null, null, null, + null, + null, + null, + null, + null, dialect, SettingsTools.defaultSettings(), null @@ -539,7 +546,14 @@ public class DefaultConfiguration implements Configuration { * configuration properties in the future, without breaking client code. * Consider creating a configuration by chaining calls to various * derive() methods. + * + * @deprecated Use + * {@link #DefaultConfiguration(ConnectionProvider, ExecutorProvider, TransactionProvider, RecordMapperProvider, RecordUnmapperProvider, RecordListenerProvider[], ExecuteListenerProvider[], VisitListenerProvider[], TransactionListenerProvider[], DiagnosticsListenerProvider[], UnwrapperProvider, ConverterProvider, SQLDialect, Settings, Map)} + * instead. This constructor is maintained to provide jOOQ 3.2, + * 3.3, 3.7, 3.8, 3.9, 3.10, 3.11 backwards-compatibility if + * called with reflection from Spring configurations. */ + @Deprecated DefaultConfiguration( ConnectionProvider connectionProvider, MetaProvider metaProvider, @@ -556,6 +570,59 @@ public class DefaultConfiguration implements Configuration { Clock clock, + SQLDialect dialect, + Settings settings, + Map data) + { + this( + connectionProvider, + metaProvider, + executorProvider, + transactionProvider, + recordMapperProvider, + recordUnmapperProvider, + recordListenerProviders, + executeListenerProviders, + visitListenerProviders, + transactionListenerProviders, + diagnosticsListenerProviders, + null, + converterProvider, + + clock, + + dialect, + settings, + data + ); + } + + + /** + * Create the actual configuration object. + *

+ * This constructor has been made package-private to allow for adding new + * configuration properties in the future, without breaking client code. + * Consider creating a configuration by chaining calls to various + * derive() methods. + */ + DefaultConfiguration( + ConnectionProvider connectionProvider, + MetaProvider metaProvider, + ExecutorProvider executorProvider, + TransactionProvider transactionProvider, + RecordMapperProvider recordMapperProvider, + RecordUnmapperProvider recordUnmapperProvider, + RecordListenerProvider[] recordListenerProviders, + ExecuteListenerProvider[] executeListenerProviders, + VisitListenerProvider[] visitListenerProviders, + TransactionListenerProvider[] transactionListenerProviders, + DiagnosticsListenerProvider[] diagnosticsListenerProviders, + UnwrapperProvider unwrapperProvider, + ConverterProvider converterProvider, + + Clock clock, + SQLDialect dialect, Settings settings, Map data) @@ -571,6 +638,7 @@ public class DefaultConfiguration implements Configuration { set(visitListenerProviders); set(transactionListenerProviders); set(diagnosticsListenerProviders); + set(unwrapperProvider); set(converterProvider); set(clock); @@ -625,6 +693,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -649,6 +718,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -678,6 +748,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -702,6 +773,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -731,6 +803,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -760,6 +833,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -789,6 +863,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -818,6 +893,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -847,6 +923,7 @@ public class DefaultConfiguration implements Configuration { newVisitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -876,6 +953,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, newTransactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -905,6 +983,37 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, newDiagnosticsListenerProviders, + unwrapperProvider, + converterProvider, + + clock, + + dialect, + settings, + data + ); + } + + @Override + public final Configuration derive(Unwrapper newUnwrapper) { + return derive(new UnwrapperWrapper(newUnwrapper)); + } + + @Override + public final Configuration derive(UnwrapperProvider newUnwrapperProvider) { + return new DefaultConfiguration( + connectionProvider, + metaProvider, + executorProvider, + transactionProvider, + recordMapperProvider, + recordUnmapperProvider, + recordListenerProviders, + executeListenerProviders, + visitListenerProviders, + transactionListenerProviders, + diagnosticsListenerProviders, + newUnwrapperProvider, converterProvider, clock, @@ -929,6 +1038,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, newConverterProvider, clock, @@ -954,6 +1064,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, newClock, dialect, @@ -977,6 +1088,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -1001,6 +1113,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders, transactionListenerProviders, diagnosticsListenerProviders, + unwrapperProvider, converterProvider, clock, @@ -1076,7 +1189,7 @@ public class DefaultConfiguration implements Configuration { } @Override - public Configuration set(RecordMapper newRecordMapper) { + public final Configuration set(RecordMapper newRecordMapper) { return set(new RecordMapperWrapper(newRecordMapper)); } @@ -1087,7 +1200,7 @@ public class DefaultConfiguration implements Configuration { } @Override - public Configuration set(RecordUnmapper newRecordUnmapper) { + public final Configuration set(RecordUnmapper newRecordUnmapper) { return set(new RecordUnmapperWrapper(newRecordUnmapper)); } @@ -1167,6 +1280,19 @@ public class DefaultConfiguration implements Configuration { return this; } + @Override + public final Configuration set(Unwrapper newUnwrapper) { + return newUnwrapper != null + ? set(new UnwrapperWrapper(newUnwrapper)) + : set((UnwrapperProvider) null) ; + } + + @Override + public final Configuration set(UnwrapperProvider newUnwrapperProvider) { + this.unwrapperProvider = newUnwrapperProvider; + return this; + } + @Override public final Configuration set(ConverterProvider newConverterProvider) { this.converterProvider = newConverterProvider != null @@ -1387,6 +1513,13 @@ public class DefaultConfiguration implements Configuration { return diagnosticsListenerProviders; } + @Override + public final UnwrapperProvider unwrapperProvider() { + return unwrapperProvider != null + ? unwrapperProvider + : new DefaultUnwrapperProvider(); + } + @Override public final ConverterProvider converterProvider() { return converterProvider; @@ -1476,6 +1609,10 @@ public class DefaultConfiguration implements Configuration { oos.writeObject(cloneSerializables(transactionListenerProviders)); oos.writeObject(cloneSerializables(diagnosticsListenerProviders)); + oos.writeObject(unwrapperProvider instanceof Serializable + ? unwrapperProvider + : null); + oos.writeObject(converterProvider instanceof Serializable ? converterProvider : null); @@ -1520,6 +1657,7 @@ public class DefaultConfiguration implements Configuration { visitListenerProviders = (VisitListenerProvider[]) ois.readObject(); transactionListenerProviders = (TransactionListenerProvider[]) ois.readObject(); diagnosticsListenerProviders = (DiagnosticsListenerProvider[]) ois.readObject(); + unwrapperProvider = (UnwrapperProvider) ois.readObject(); converterProvider = (ConverterProvider) ois.readObject(); data = new ConcurrentHashMap(); @@ -1572,4 +1710,17 @@ public class DefaultConfiguration implements Configuration { return (RecordUnmapper) newRecordUnmapper; } } + + private final class UnwrapperWrapper implements UnwrapperProvider { + private final Unwrapper newUnwrapper; + + private UnwrapperWrapper(Unwrapper newUnwrapper) { + this.newUnwrapper = newUnwrapper; + } + + @Override + public Unwrapper provide() { + return newUnwrapper; + } + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java index 4d62b87279..850db56dcc 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultExecuteContext.java @@ -77,8 +77,6 @@ import org.jooq.Update; import org.jooq.conf.Settings; import org.jooq.tools.JooqLogger; import org.jooq.tools.jdbc.JDBCUtils; -import org.jooq.tools.reflect.Reflect; -import org.jooq.tools.reflect.ReflectException; /** * A default implementation for the {@link ExecuteContext}. @@ -301,36 +299,16 @@ class DefaultExecuteContext implements ExecuteContext { } /** - * [#3696] We shouldn't infinitely attempt to unwrap connections. - */ - private static int maxUnwrappedConnections = 256; - - /** - * [#7589] No infinite attempts to unwrap statements, either. - */ - private static int maxUnwrappedStatements = 256; - - /** - * Get the registered connection's "target connection" if applicable. - *

- * This will try to unwrap any native connection if it has been wrapped with - * any of: - *

+ * Get the registered connection's "target connection" through + * {@link Configuration#unwrapperProvider()} if applicable. *

* It can be safely assumed that such a connection is available once the * {@link ExecuteContext} has been established, until the statement is * closed. */ - static final Connection localTargetConnection() { + static final Connection localTargetConnection(Configuration configuration) { Connection result = localConnection(); - unwrappingLoop: - for (int i = 0; i < maxUnwrappedConnections; i++) { @@ -352,48 +330,7 @@ class DefaultExecuteContext implements ExecuteContext { - - - - - - - - - // Unwrap nested Spring org.springframework.jdbc.datasource.ConnectionProxy objects - try { - Connection r = Reflect.on(result).call("getTargetConnection").get(); - if (result != r && r != null) { - result = r; - continue unwrappingLoop; - } - } - catch (ReflectException ignore) {} - - // Unwrap nested DBCP org.apache.commons.dbcp.DelegatingConnection - try { - Connection r = Reflect.on(result).call("getDelegate").get(); - if (result != r && r != null) { - result = r; - continue unwrappingLoop; - } - } - catch (ReflectException ignore) {} - - // [#7641] Unwrap nested org.jboss.jca.adapters.jdbc.WrappedConnection - try { - Connection r = Reflect.on(result).call("getUnderlyingConnection").get(); - if (result != r && r != null) { - result = r; - continue unwrappingLoop; - } - } - catch (ReflectException ignore) {} - - // No unwrapping method was found. - break; - } - + log.info("Could not unwrap native Connection type. Consider implementing an org.jooq.UnwrapperProvider"); return result; } /** @@ -406,11 +343,9 @@ class DefaultExecuteContext implements ExecuteContext { *

  • ...
  • * */ - static final PreparedStatement targetPreparedStatement(PreparedStatement stmt) { + static final PreparedStatement targetPreparedStatement(Configuration configuration, PreparedStatement stmt) { PreparedStatement result = stmt; - unwrappingLoop: - for (int i = 0; i < maxUnwrappedStatements; i++) { @@ -432,38 +367,7 @@ class DefaultExecuteContext implements ExecuteContext { - - - - - - - - - // Unwrap nested DBCP org.apache.commons.dbcp.DelegatingPreparedStatement - try { - PreparedStatement r = Reflect.on(result).call("getDelegate").get(); - if (result != r && r != null) { - result = r; - continue unwrappingLoop; - } - } - catch (ReflectException ignore) {} - - // [#7641] Unwrap nested org.jboss.jca.adapters.jdbc.WrappedStatement - try { - PreparedStatement r = Reflect.on(result).call("getUnderlyingStatement").get(); - if (result != r && r != null) { - result = r; - continue unwrappingLoop; - } - } - catch (ReflectException ignore) {} - - // No unwrapping method was found. - break; - } - + log.info("Could not unwrap native PreparedStatement type. Consider implementing an org.jooq.UnwrapperProvider"); return result; } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultUnwrapperProvider.java b/jOOQ/src/main/java/org/jooq/impl/DefaultUnwrapperProvider.java new file mode 100644 index 0000000000..15dfc46f75 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultUnwrapperProvider.java @@ -0,0 +1,201 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Wrapper; + +import org.jooq.Unwrapper; +import org.jooq.UnwrapperProvider; +import org.jooq.tools.reflect.Reflect; +import org.jooq.tools.reflect.ReflectException; + +/** + * @author Lukas Eder + */ +final class DefaultUnwrapperProvider implements UnwrapperProvider { + + static final Unwrapper INSTANCE = new DefaultUnwrapper(); + + static final class DefaultUnwrapper implements Unwrapper { + + /** + * [#3696] We shouldn't infinitely attempt to unwrap connections. + */ + private static int maxUnwrappedConnections = 256; + + /** + * [#7589] No infinite attempts to unwrap statements, either. + */ + private static int maxUnwrappedStatements = 256; + + @Override + public T unwrap(Wrapper wrapper, Class iface) { + if (wrapper instanceof Connection) + return unwrap((Connection) wrapper, iface); + else if (wrapper instanceof Statement) + return unwrap((Statement) wrapper, iface); + else + throw new IllegalArgumentException("Cannot unwrap: " + wrapper); + } + + @SuppressWarnings("unchecked") + private T unwrap(Connection wrapper, Class iface) { + Connection result = wrapper; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return (T) result; + } + + @SuppressWarnings("unchecked") + private T unwrap(Statement wrapper, Class iface) { + Statement result = wrapper; + + + + + + + + + + + + + + + + + + + + + + + + // Unwrap nested DBCP org.apache.commons.dbcp.DelegatingPreparedStatement + try { + PreparedStatement r = Reflect.on(result).call("getDelegate").get(); + if (result != r && r != null) { + result = r; + continue unwrappingLoop; + } + } + catch (ReflectException ignore) {} + + // [#7641] Unwrap nested org.jboss.jca.adapters.jdbc.WrappedStatement + try { + PreparedStatement r = Reflect.on(result).call("getUnderlyingStatement").get(); + if (result != r && r != null) { + result = r; + continue unwrappingLoop; + } + } + catch (ReflectException ignore) {} + + // No unwrapping method was found. + break; + } + /* [/pro] */ + + return (T) result; + } + }; + + @Override + public Unwrapper provide() { + return INSTANCE; + } +} diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java index 07b3db0ffc..190f7d90b9 100644 --- a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java +++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java @@ -64,6 +64,8 @@ import org.jooq.SQLDialect; import org.jooq.TransactionListener; import org.jooq.TransactionListenerProvider; import org.jooq.TransactionProvider; +import org.jooq.Unwrapper; +import org.jooq.UnwrapperProvider; import org.jooq.VisitListener; import org.jooq.VisitListenerProvider; import org.jooq.conf.Settings; @@ -175,6 +177,11 @@ public class MockConfiguration implements Configuration { return delegate.diagnosticsListenerProviders(); } + @Override + public UnwrapperProvider unwrapperProvider() { + return delegate.unwrapperProvider(); + } + @Override public ConverterProvider converterProvider() { return delegate.converterProvider(); @@ -312,6 +319,16 @@ public class MockConfiguration implements Configuration { return delegate.set(newDiagnosticsListenerProviders); } + @Override + public Configuration set(Unwrapper newUnwrapper) { + return delegate.set(newUnwrapper); + } + + @Override + public Configuration set(UnwrapperProvider newUnwrapperProvider) { + return delegate.set(newUnwrapperProvider); + } + @Override public Configuration set(ConverterProvider newConverterProvider) { return delegate.set(newConverterProvider); @@ -444,6 +461,16 @@ public class MockConfiguration implements Configuration { return delegate.derive(newDiagnosticsListenerProviders); } + @Override + public Configuration derive(Unwrapper newUnwrapper) { + return delegate.derive(newUnwrapper); + } + + @Override + public Configuration derive(UnwrapperProvider newUnwrapperProvider) { + return delegate.derive(newUnwrapperProvider); + } + @Override public Configuration derive(ConverterProvider newConverterProvider) { return delegate.derive(newConverterProvider);