diff --git a/integrations/jdbc/jdbc/etc/spotbugs/exclude.xml b/integrations/jdbc/jdbc/etc/spotbugs/exclude.xml index 86271f9ff5e..3239a8a1322 100644 --- a/integrations/jdbc/jdbc/etc/spotbugs/exclude.xml +++ b/integrations/jdbc/jdbc/etc/spotbugs/exclude.xml @@ -1,6 +1,6 @@ +--> - + - + - + diff --git a/integrations/jdbc/jdbc/pom.xml b/integrations/jdbc/jdbc/pom.xml index f2aaee7a250..4058f2ed010 100644 --- a/integrations/jdbc/jdbc/pom.xml +++ b/integrations/jdbc/jdbc/pom.xml @@ -34,5 +34,29 @@ etc/spotbugs/exclude.xml - + + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.hamcrest + hamcrest-all + test + + + + com.h2database + h2 + test + + + + diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractCommonDataSource.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractCommonDataSource.java index d9e47d6bf58..a62cadd745a 100644 --- a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractCommonDataSource.java +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractCommonDataSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ import javax.sql.CommonDataSource; /** - * A skeletal implementation of the {@link CommonDataSource} - * interface. + * A JDBC + * 4.3-compliant skeletal implementation of the {@link CommonDataSource} interface. */ public abstract class AbstractCommonDataSource implements CommonDataSource { @@ -32,6 +32,9 @@ public abstract class AbstractCommonDataSource implements CommonDataSource { private PrintWriter logWriter; + /** + * Creates a new {@link AbstractCommonDataSource}. + */ protected AbstractCommonDataSource() { super(); } @@ -42,7 +45,7 @@ public PrintWriter getLogWriter() throws SQLException { } @Override - public void setLogWriter(final PrintWriter logWriter) throws SQLException { + public void setLogWriter(PrintWriter logWriter) throws SQLException { this.logWriter = logWriter; } @@ -52,7 +55,7 @@ public int getLoginTimeout() throws SQLException { } @Override - public void setLoginTimeout(final int loginTimeout) throws SQLException { + public void setLoginTimeout(int loginTimeout) throws SQLException { this.loginTimeout = loginTimeout; } diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractDataSource.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractDataSource.java index 6513ac52fea..08bc2fa6397 100644 --- a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractDataSource.java +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/AbstractDataSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,21 +20,25 @@ import javax.sql.DataSource; /** - * A skeletal implementation of the {@link DataSource} interface. + * A JDBC + * 4.3-compliant, skeletal implementation of the {@link DataSource} interface. */ public abstract class AbstractDataSource extends AbstractCommonDataSource implements DataSource { + /** + * Creates a new {@link AbstractDataSource}. + */ protected AbstractDataSource() { super(); } @Override - public boolean isWrapperFor(final Class iface) throws SQLException { + public boolean isWrapperFor(Class iface) throws SQLException { return iface != null && iface.isInstance(this); } @Override - public T unwrap(final Class iface) throws SQLException { + public T unwrap(Class iface) throws SQLException { return iface.cast(this); } diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/ConditionallyCloseableConnection.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/ConditionallyCloseableConnection.java index 7a0889268d9..91ba7b95e89 100644 --- a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/ConditionallyCloseableConnection.java +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/ConditionallyCloseableConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,24 +15,48 @@ */ package io.helidon.integrations.jdbc; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; import java.sql.SQLException; +import java.sql.SQLNonTransientConnectionException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.ShardingKey; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; /** - * A {@link DelegatingConnection} whose {@link #close()} method - * performs a close only if the {@link #isCloseable()} method returns - * {@code true}. + * A JDBC + * 4.3-compliant {@link DelegatingConnection} whose {@link #close()} method may or may not close it depending on + * other partial state. * *

Thread Safety

* - *

Instances of this class are not necessarily safe for concurrent - * use by multiple threads.

+ *

Instances of this class are not necessarily safe for concurrent use by multiple threads because their {@link + * Connection} delegates may not be. JDBC 4.3 does not require thread safety from any JDBC construct.

+ * + * @see #isClosed() * * @see #isCloseable() * * @see #setCloseable(boolean) * * @see #close() + * + * @see #isClosePending() + * + * @see #ConditionallyCloseableConnection(Connection, boolean, boolean) */ public class ConditionallyCloseableConnection extends DelegatingConnection { @@ -43,14 +67,55 @@ public class ConditionallyCloseableConnection extends DelegatingConnection { /** - * Whether or not the {@link #close()} method will actually close - * this {@link DelegatingConnection}. + * A {@link SQLRunnable} representing the logic run by the {@link #checkOpen()} method. + * + *

This field is never {@code null}.

+ * + *

This field is set based on the value of the {@code strictClosedChecking} argument supplied to the {@link + * #ConditionallyCloseableConnection(Connection, boolean, boolean)} constructor. It may end up deliberately doing + * nothing.

+ * + * @see #ConditionallyCloseableConnection(Connection, boolean, boolean) + */ + private final SQLRunnable closedChecker; + + /** + * A {@link SQLBooleanSupplier} that is the effective body of the {@link #isClosed()} method. + * + *

This field is never {@code null}.

+ * + *

This field is set based on the value of the {@code strictClosedChecking} argument supplied to the {@link + * #ConditionallyCloseableConnection(Connection, boolean, boolean)} constructor. + * + * @see #isClosed() + * + * @see #ConditionallyCloseableConnection(Connection, boolean, boolean) + */ + private SQLBooleanSupplier isClosedFunction; + + /** + * Whether or not the {@link #close()} method will actually close this {@link DelegatingConnection}. + * + *

This field is set based on the value of the {@code strictClosedChecking} argument supplied to the {@link + * #ConditionallyCloseableConnection(Connection, boolean, boolean)} constructor. It may end up deliberately doing + * nothing.

* * @see #isCloseable() * * @see #setCloseable(boolean) + * + * @see #ConditionallyCloseableConnection(Connection, boolean, boolean) */ - private boolean closeable; + private volatile boolean closeable; + + /** + * Whether or not a {@link #close()} request has been issued from another thread. + * + * @see #isClosePending() + * + * @see #isClosed() + */ + private volatile boolean closePending; /* @@ -59,44 +124,93 @@ public class ConditionallyCloseableConnection extends DelegatingConnection { /** - * Creates a new {@link ConditionallyCloseableConnection} and - * {@linkplain #setCloseable(boolean) sets its closeable status to - * true}. + * Creates a new {@link ConditionallyCloseableConnection} and {@linkplain #setCloseable(boolean) sets its closeable + * status to true}. * - * @param delegate the {@link Connection} to wrap; must not be - * {@code null} + * @param delegate the {@link Connection} to wrap; must not be {@code null} * - * @exception NullPointerException if {@code delegate} is {@code - * null} + * @exception NullPointerException if {@code delegate} is {@code null} * - * @see #ConditionallyCloseableConnection(Connection, boolean) + * @see #ConditionallyCloseableConnection(Connection, boolean, boolean) * - * @see #setCloseable(boolean) + * @deprecated This constructor continues to exist for backwards compatibility only and its use is strongly + * discouraged. Please use the {@link #ConditionallyCloseableConnection(Connection, boolean, boolean)} + * constructor instead and consider supplying {@code true} for its {@code strictClosedChecking} + * parameter. In the future, this constructor may change, without prior notice, to cause new {@link + * ConditionallyCloseableConnection} instances created by it to behave as if they were created by invocations of the + * {@link #ConditionallyCloseableConnection(Connection, boolean, boolean)} constructor instead, with {@code true} + * supplied for its {@code strictClosedChecking} parameter. + */ + @Deprecated(since = "3.0.3") + public ConditionallyCloseableConnection(Connection delegate) { + this(delegate, true, false); + } + + /** + * Creates a new {@link ConditionallyCloseableConnection}. + * + * @param delegate the {@link Connection} to wrap; must not be {@code null} + * + * @param closeable the initial value for this {@link ConditionallyCloseableConnection}'s {@linkplain #isCloseable() + * closeable} status + * + * @exception NullPointerException if {@code delegate} is {@code null} + * + * @see ConditionallyCloseableConnection(Connection, boolean, boolean) + * + * @deprecated This constructor continues to exist for backwards compatibility only and its use is strongly + * discouraged. Please use the {@link #ConditionallyCloseableConnection(Connection, boolean, boolean)} + * constructor instead and consider supplying {@code true} for its {@code strictClosedChecking} + * parameter. In the future, this constructor may change, without prior notice, to cause new {@link + * ConditionallyCloseableConnection} instances created by it to behave as if they were created by invocations of the + * {@link #ConditionallyCloseableConnection(Connection, boolean, boolean)} constructor instead, with {@code true} + * supplied for its {@code strictClosedChecking} parameter. */ - public ConditionallyCloseableConnection(final Connection delegate) { - this(delegate, true); + @Deprecated(since = "3.0.3") + public ConditionallyCloseableConnection(Connection delegate, boolean closeable) { + this(delegate, closeable, false); } /** * Creates a new {@link ConditionallyCloseableConnection}. * - * @param delegate the {@link Connection} to wrap; must not be - * {@code null} + * @param delegate the {@link Connection} to wrap; must not be {@code null} * - * @param closeable the initial value for this {@link - * ConditionallyCloseableConnection}'s {@linkplain #isCloseable() + * @param closeable the initial value for this {@link ConditionallyCloseableConnection}'s {@linkplain #isCloseable() * closeable} status * - * @exception NullPointerException if {@code delegate} is {@code - * null} + * @param strictClosedChecking if {@code true}, then this {@link ConditionallyCloseableConnection}'s {@link + * #isClosed()} method will be invoked before every operation that cannot take place on a closed connection, and, if + * it returns {@code true}, the operation in question will fail with a {@link SQLException}; it is strongly + * recommended to supply {@code true} as the argument for this parameter ({@code false} is permitted for + * backwards compatibility reasons only) + * + * @exception NullPointerException if {@code delegate} is {@code null} + * + * @see #isCloseable() * * @see #setCloseable(boolean) * + * @see #close() + * + * @see #isClosed() + * + * @see #isClosePending() + * * @see DelegatingConnection#DelegatingConnection(Connection) */ - public ConditionallyCloseableConnection(final Connection delegate, final boolean closeable) { + public ConditionallyCloseableConnection(Connection delegate, + boolean closeable, + boolean strictClosedChecking) { super(delegate); - this.setCloseable(closeable); + if (strictClosedChecking) { + this.closedChecker = this::failWhenClosed; + this.isClosedFunction = () -> this.isClosePending() || super.isClosed(); + } else { + this.closedChecker = ConditionallyCloseableConnection::doNothing; + this.isClosedFunction = super::isClosed; + } + this.closeable = closeable; } @@ -106,74 +220,650 @@ public ConditionallyCloseableConnection(final Connection delegate, final boolean /** - * Overrides the {@link DelegatingConnection#close()} method so - * that when it is invoked this {@link - * ConditionallyCloseableConnection} is {@linkplain - * Connection#close() closed} only if it {@linkplain - * #isCloseable() is closeable}. + * Overrides the {@link DelegatingConnection#close()} method so that when it is invoked this {@link + * ConditionallyCloseableConnection} is {@linkplain Connection#close() closed} only if it {@linkplain #isCloseable() + * is closeable}. + * + *

Subclasses that override this method must not directly or indirectly call {@link #failWhenClosed()} or + * undefined behavior may result.

+ * + *

If {@code strictClosedChecking} was {@code true} {@linkplain #ConditionallyCloseableConnection(Connection, + * boolean, boolean) at construction time} (strongly recommended), then the following pre- and post-conditions + * apply:

* - *

Overrides should normally call {@code super.close()} as part - * of their implementation.

+ *

If {@link #isCloseable()} returns {@code true} at the point of an invocation of this method, then after this + * method completes, successfully or not, {@link #isClosePending()} will return {@code false}.

+ * + *

If {@link #isCloseable()} returns {@code false} at the point of an invocation of this method, then after this + * method completes, successfully or not, {@link #isClosePending()} will return {@code true}.

+ * + *

Overrides should normally call {@code super.close()} as part of their implementation.

* * @exception SQLException if an error occurs * + * @see #isClosed() + * * @see #isCloseable() + * + * @see #setCloseable(boolean) + * + * @see #isClosePending() */ - @Override + @Override // DelegatingConnection public void close() throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. if (this.isCloseable()) { - super.close(); + try { + super.close(); + } finally { + this.closePending = false; + this.onClose(); + } + } else { + this.closePending = true; } } /** - * Returns {@code true} if a call to {@link #close()} will - * actually close this {@link ConditionallyCloseableConnection}. + * Called by the {@link #close()} method to perform work after an actual close operation has completed. + * + *

During an invocation of this method by the {@link #close()} method:

+ * + *
    + * + *
  • The {@link #isClosed()} method will return {@code true}.
  • + * + *
  • The {@link #isCloseable()} method will return {@code false}.
  • + * + *
  • The {@link #isClosePending()} method will return {@code false}.
  • + * + *
* - *

This method returns {@code true} when {@link - * #setCloseable(boolean)} has been called with a value of {@code - * true} and the {@link #isClosed()} method returns {@code - * false}.

+ *

The default implementation of this method does nothing.

* - * @return {@code true} if a call to {@link #close()} will - * actually close this {@link ConditionallyCloseableConnection}; - * {@code false} in all other cases + *

Invoking this method directly may result in undefined behavior, depending on how it is overridden.

* - * @exception SQLException if {@link #isClosed()} throws a {@link - * SQLException} + *

Overrides of this method must not call {@link #close()} or undefined behavior, such as an infinite loop, may + * result.

+ * + *

Overrides of this method must be idempotent.

+ * + * @exception SQLException if a database error occurs + * + * @see #isClosed() + * + * @see #isCloseable() * * @see #setCloseable(boolean) * + * @see #isClosePending() + * * @see #close() + */ + protected void onClose() throws SQLException { + + } + + /** + * Returns {@code true} if a call to {@link #close()} will actually close this {@link + * ConditionallyCloseableConnection}. + * + *

This method returns {@code true} when {@link #setCloseable(boolean)} has been called with a value of {@code + * true} and the {@link #isClosed()} method returns {@code false}.

+ * + *

Subclasses that override this method must not directly or indirectly call {@link #failWhenClosed()} or + * undefined behavior may result.

+ * + * @return {@code true} if a call to {@link #close()} will actually close this {@link + * ConditionallyCloseableConnection}; {@code false} in all other cases + * + * @exception SQLException if {@link #isClosed()} throws a {@link SQLException} * * @see #isClosed() + * + * @see #setCloseable(boolean) + * + * @see #close() + * + * @see #isClosePending() */ - public final boolean isCloseable() throws SQLException { + public boolean isCloseable() throws SQLException { + // this.checkOpen(); // Deliberately omitted. return this.closeable && !this.isClosed(); } /** - * Sets the closeable status of this {@link - * ConditionallyCloseableConnection}. + * Sets the closeable status of this {@link ConditionallyCloseableConnection} and, if the supplied {@code closeable} + * agrument is {@code true}, sets the {@linkplain #isClosePending() close pending status} to {@code false}. + * + *

Subclasses that override this method must not directly or indirectly call {@link #failWhenClosed()} or + * undefined behavior may result.

+ * + *

Note that calling this method with a value of {@code true} does not necessarily mean that the {@link + * #isCloseable()} method will subsequently return {@code true}, since the {@link #isClosed()} method may return + * {@code true}.

+ * + *

Design Note

* - *

Note that calling this method with a value of {@code true} - * does not necessarily mean that the {@link #isCloseable()} - * method will subsequently return {@code true}, since the {@link - * #isClosed()} method may return {@code true}.

+ *

This method does not throw {@link SQLException} only because of an oversight in the design of the original + * version of this class. Callers should consider catching {@link UncheckedSQLException} where appropriate + * instead. The default implementation of this method does not throw any exceptions of any kind.

* - * @param closeable whether or not a call to {@link #close()} will - * actually close this {@link ConditionallyCloseableConnection} + * @param closeable whether or not a call to {@link #close()} will actually close this {@link + * ConditionallyCloseableConnection} + * + * @see #isClosed() * * @see #isCloseable() * * @see #close() * + * @see #isClosePending() + * * @see Connection#close() + */ + public void setCloseable(boolean closeable) { + // this.checkOpen(); // Deliberately omitted. + this.closeable = closeable; + if (closeable) { + this.closePending = false; + } + } + + /** + * Returns {@code true} if and only if this {@link ConditionallyCloseableConnection} is behaving as if the {@link + * #close()} method has been invoked while this {@link ConditionallyCloseableConnection} was {@linkplain + * #isCloseable() not closeable}. + * + *

Subclasses that override this method must not directly or indirectly call {@link #failWhenClosed()} or + * undefined behavior may result.

+ * + *

Subclasses that override this method must not directly or indirectly mutate the state of this {@link + * ConditionallyCloseableConnection} or undefined behavior may result.

+ * + * @return {@code true} if and only if a close operation is pending * * @see #isClosed() + * + * @see #isCloseable() + * + * @see #setCloseable(boolean) + * + * @see #close() */ - public final void setCloseable(final boolean closeable) { - this.closeable = closeable; + public boolean isClosePending() { + // this.checkOpen(); // Deliberately omitted. + return this.closePending; + } + + @Override // DelegatingConnection + public Statement createStatement() throws SQLException { + this.checkOpen(); + return super.createStatement(); + } + + @Override // DelegatingConnection + public PreparedStatement prepareStatement(String sql) throws SQLException { + this.checkOpen(); + return super.prepareStatement(sql); + } + + @Override // DelegatingConnection + public CallableStatement prepareCall(String sql) throws SQLException { + this.checkOpen(); + return super.prepareCall(sql); + } + + @Override // DelegatingConnection + public String nativeSQL(String sql) throws SQLException { + this.checkOpen(); + return super.nativeSQL(sql); + } + + @Override // DelegatingConnection + public void setAutoCommit(boolean autoCommit) throws SQLException { + this.checkOpen(); + super.setAutoCommit(autoCommit); + } + + @Override // DelegatingConnection + public boolean getAutoCommit() throws SQLException { + this.checkOpen(); + return super.getAutoCommit(); + } + + @Override // DelegatingConnection + public void commit() throws SQLException { + this.checkOpen(); + super.commit(); + } + + @Override // DelegatingConnection + public void rollback() throws SQLException { + this.checkOpen(); + super.rollback(); + } + + /** + * Returns {@code true} if and only if this {@link ConditionallyCloseableConnection} either is, or is to be + * considered to be, closed, such that operations which must throw a {@link SQLException} when invoked on a closed + * connection will do so. + * + *

If {@code true} was supplied for the {@code strictClosedChecking} parameter {@linkplain + * #ConditionallyCloseableConnection(Connection, boolean, boolean) at construction time} (strongly recommended), the + * default implementation of this method returns a value as if produced by the following implementation: {@code + * this.}{@link #isClosePending() isClosePending() }{@code || super.isClosed()}.

+ * + *

If {@code false} was supplied for the {@code strictClosedChecking} parameter {@linkplain + * #ConditionallyCloseableConnection(Connection, boolean, boolean) at construction time} (not recommended), the + * default implementation of this method returns a value as if produced by the following implementation: {@code + * super.isClosed()}.

+ * + *

Subclasses that override this method must not directly or indirectly call {@link #failWhenClosed()} or + * undefined behavior may result.

+ * + * @return {@code true} if and only if this {@link ConditionallyCloseableConnection} either is, or is to be + * considered to be, closed + * + * @exception SQLException if a database access error occurs + * + * @see #isCloseable() + * + * @see #setCloseable(boolean) + * + * @see #close() + * + * @see #isClosePending() + */ + @Override // DelegatingConnection + public boolean isClosed() throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec (and common sense). + return this.isClosedFunction.getAsBoolean(); + } + + @Override // DelegatingConnection + public DatabaseMetaData getMetaData() throws SQLException { + this.checkOpen(); + return super.getMetaData(); + } + + @Override // DelegatingConnection + public void setReadOnly(boolean readOnly) throws SQLException { + this.checkOpen(); + super.setReadOnly(readOnly); + } + + @Override // DelegatingConnection + public boolean isReadOnly() throws SQLException { + this.checkOpen(); + return super.isReadOnly(); + } + + @Override // DelegatingConnection + public void setCatalog(String catalog) throws SQLException { + this.checkOpen(); + super.setCatalog(catalog); + } + + @Override // DelegatingConnection + public String getCatalog() throws SQLException { + this.checkOpen(); + return super.getCatalog(); + } + + @Override // DelegatingConnection + public void setTransactionIsolation(int level) throws SQLException { + this.checkOpen(); + super.setTransactionIsolation(level); + } + + @Override // DelegatingConnection + public int getTransactionIsolation() throws SQLException { + this.checkOpen(); + return super.getTransactionIsolation(); + } + + @Override // DelegatingConnection + public SQLWarning getWarnings() throws SQLException { + this.checkOpen(); + return super.getWarnings(); + } + + @Override // DelegatingConnection + public void clearWarnings() throws SQLException { + this.checkOpen(); + super.clearWarnings(); + } + + @Override // DelegatingConnection + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + this.checkOpen(); + return super.createStatement(resultSetType, resultSetConcurrency); + } + + @Override // DelegatingConnection + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + this.checkOpen(); + return super.prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + @Override // DelegatingConnection + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + this.checkOpen(); + return super.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + @Override // DelegatingConnection + public Map> getTypeMap() throws SQLException { + this.checkOpen(); + return super.getTypeMap(); + } + + @Override // DelegatingConnection + public void setTypeMap(Map> map) throws SQLException { + this.checkOpen(); + super.setTypeMap(map); + } + + @Override // DelegatingConnection + public void setHoldability(int holdability) throws SQLException { + this.checkOpen(); + super.setHoldability(holdability); + } + + @Override // DelegatingConnection + public int getHoldability() throws SQLException { + this.checkOpen(); + return super.getHoldability(); + } + + @Override // DelegatingConnection + public Savepoint setSavepoint() throws SQLException { + this.checkOpen(); + return super.setSavepoint(); + } + + @Override // DelegatingConnection + public Savepoint setSavepoint(String name) throws SQLException { + this.checkOpen(); + return super.setSavepoint(name); + } + + @Override // DelegatingConnection + public void rollback(Savepoint savepoint) throws SQLException { + this.checkOpen(); + super.rollback(savepoint); + } + + @Override // DelegatingConnection + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + this.checkOpen(); + super.releaseSavepoint(savepoint); + } + + @Override // DelegatingConnection + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + this.checkOpen(); + return super.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override // DelegatingConnection + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + this.checkOpen(); + return super.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override // DelegatingConnection + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + this.checkOpen(); + return super.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override // DelegatingConnection + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + this.checkOpen(); + return super.prepareStatement(sql, autoGeneratedKeys); + } + + @Override // DelegatingConnection + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + this.checkOpen(); + return super.prepareStatement(sql, columnIndexes); + } + + @Override // DelegatingConnection + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + this.checkOpen(); + return super.prepareStatement(sql, columnNames); + } + + @Override // DelegatingConnection + public Clob createClob() throws SQLException { + this.checkOpen(); + return super.createClob(); + } + + @Override // DelegatingConnection + public Blob createBlob() throws SQLException { + this.checkOpen(); + return super.createBlob(); + } + + @Override // DelegatingConnection + public NClob createNClob() throws SQLException { + this.checkOpen(); + return super.createNClob(); + } + + @Override // DelegatingConnection + public SQLXML createSQLXML() throws SQLException { + this.checkOpen(); + return super.createSQLXML(); + } + + @Override // DelegatingConnection + public boolean isValid(int timeout) throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + return super.isValid(timeout); + } + + @Override // DelegatingConnection + public void setClientInfo(String name, String value) throws SQLClientInfoException { + try { + this.checkOpen(); + } catch (SQLClientInfoException e) { + throw e; + } catch (SQLException e) { + throw new SQLClientInfoException(e.getMessage(), e.getSQLState(), e.getErrorCode(), Map.of(), e); + } + super.setClientInfo(name, value); + } + + @Override // DelegatingConnection + public void setClientInfo(Properties properties) throws SQLClientInfoException { + try { + this.checkOpen(); + } catch (SQLClientInfoException e) { + throw e; + } catch (SQLException e) { + throw new SQLClientInfoException(e.getMessage(), e.getSQLState(), e.getErrorCode(), Map.of(), e); + } + super.setClientInfo(properties); + } + + @Override // DelegatingConnection + public String getClientInfo(String name) throws SQLException { + this.checkOpen(); + return super.getClientInfo(name); + } + + @Override // DelegatingConnection + public Properties getClientInfo() throws SQLException { + this.checkOpen(); + return super.getClientInfo(); + } + + @Override // DelegatingConnection + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + this.checkOpen(); + return super.createArrayOf(typeName, elements); + } + + @Override // DelegatingConnection + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + this.checkOpen(); + return super.createStruct(typeName, attributes); + } + + @Override // DelegatingConnection + public void setSchema(String schema) throws SQLException { + this.checkOpen(); + super.setSchema(schema); + } + + @Override // DelegatingConnection + public String getSchema() throws SQLException { + this.checkOpen(); + return super.getSchema(); + } + + @Override // DelegatingConnection + public void abort(Executor executor) throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + super.abort(executor); + } + + @Override // DelegatingConnection + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + this.checkOpen(); + super.setNetworkTimeout(executor, milliseconds); + } + + @Override // DelegatingConnection + public int getNetworkTimeout() throws SQLException { + this.checkOpen(); + return super.getNetworkTimeout(); + } + + @Override // DelegatingConnection + public void beginRequest() throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + super.beginRequest(); + } + + @Override // DelegatingConnection + public void endRequest() throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + super.endRequest(); + } + + @Override // DelegatingConnection + public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) + throws SQLException { + this.checkOpen(); + return super.setShardingKeyIfValid(shardingKey, superShardingKey, timeout); + } + + @Override // DelegatingConnection + public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLException { + this.checkOpen(); + return super.setShardingKeyIfValid(shardingKey, timeout); + } + + @Override // DelegatingConnection + public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLException { + this.checkOpen(); + super.setShardingKey(shardingKey, superShardingKey); + } + + @Override // DelegatingConnection + public void setShardingKey(ShardingKey shardingKey) throws SQLException { + this.checkOpen(); + super.setShardingKey(shardingKey); + } + + @Override // DelegatingConnection + public T unwrap(Class iface) throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + return super.unwrap(iface); + } + + @Override // DelegatingConnection + public boolean isWrapperFor(Class iface) throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + return super.isWrapperFor(iface); + } + + /** + * Ensures this {@link ConditionallyCloseableConnection} is {@linkplain #isClosed() not closed}, if {@linkplain + * #ConditionallyCloseableConnection(Connection, boolean, boolean) strict closed checking was enabled at + * construction time}, or simply returns if {@linkplain #ConditionallyCloseableConnection(Connection, boolean, + * boolean) strict closed checking was not enabled at construction time}. + * + *

This method is called from almost every method in this class.

+ * + * @exception SQLException if this {@link ConditionallyCloseableConnection} was {@linkplain + * #ConditionallyCloseableConnection(Connection, boolean, boolean) created with strict closed checking enabled} and + * an invocation of the {@link #isClosed()} method returns {@code true}, or if some other database access error + * occurs + * + * @see #closedChecker + */ + private void checkOpen() throws SQLException { + this.closedChecker.run(); + } + + /** + * Invokes the {@link #isClosed()} method, and, if it returns {@code true}, throws a new {@link SQLException} + * indicating that because the connection is closed the operation cannot proceed. + * + *

If this {@link ConditionallyCloseableConnection} was {@linkplain #ConditionallyCloseableConnection(Connection, + * boolean, boolean) created with strict closed checking enabled} (strongly recommended), then this method will be + * called where appropriate. Otherwise this method is not called internally by default implementations of the + * methods in the {@link ConditionallyCloseableConnection} class. Subclasses may, and often will, call this method + * directly for any reason.

+ * + * @exception SQLNonTransientConnectionException when an invocation of the {@link #isClosed()} method returns {@code + * true}; its {@linkplain SQLException#getSQLState() SQLState} will begin with {@code 08} + * + * @exception SQLException if {@link #isClosed()} throws a {@link SQLException} + * + * @see #isClosed() + * + * @see #isCloseable() + * + * @see #setCloseable(boolean) + * + * @see #close() + */ + protected final void failWhenClosed() throws SQLException { + if (this.isClosed()) { + throw new SQLNonTransientConnectionException("Connection is closed", "08000"); + } + } + + + /* + * Static methods. + */ + + + /** + * Deliberately does nothing when invoked. + * + *

Used as a method reference only, and then only as a potential value for the {@link #closedChecker} field, and + * then only when the {@code strictClosedChecking} argument supplied to the {@link + * #ConditionallyCloseableConnection(Connection, boolean, boolean)} constructor was {@code false}.

+ * + * @see #closedChecker + * + * @see #ConditionallyCloseableConnection(Connection, boolean, boolean) + */ + // (Invoked by method reference only.) + private static void doNothing() { + } } diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingCallableStatement.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingCallableStatement.java new file mode 100644 index 00000000000..0c9560f56f2 --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingCallableStatement.java @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Map; + +/** + * A JDBC + * 4.3-compliant {@link CallableStatement} that delegates to another JDBC 4.3-compliant {@link CallableStatement}. + */ +public class DelegatingCallableStatement extends DelegatingPreparedStatement implements CallableStatement { + + /** + * Creates a new {@link DelegatingCallableStatement}. + * + * @param connection the {@link Connection} that created this {@link DelegatingCallableStatement}; must not be + * {@code null} + * + * @param delegate the {@link CallableStatement} instance to which all operations will be delegated; must not be + * {@code null} + * + * @param closeable the initial value for this {@link DelegatingCallableStatement}'s {@linkplain #isCloseable() + * closeable} status + * + * @param strictClosedChecking if {@code true}, then this {@link DelegatingCallableStatement}'s {@link + * #isClosed()} method will be invoked before every operation that cannot take place on a closed statement, and, if + * it returns {@code true}, the operation in question will fail with a {@link SQLException} + * @exception NullPointerException if either argument is {@code + * null} + * + * @see DelegatingStatement#isCloseable() + * + * @see DelegatingStatement#setCloseable(boolean) + * + * @see DelegatingStatement#close() + * + * @see DelegatingStatement#isClosed() + * + * @exception NullPointerException if either {@code connection} or + * {@code delegate} is {@code null} + */ + public DelegatingCallableStatement(Connection connection, + CallableStatement delegate, + boolean closeable, + boolean strictClosedChecking) { + super(connection, delegate, closeable, strictClosedChecking); + } + + @Override + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterIndex, sqlType); + } + + @Override + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterIndex, sqlType, scale); + } + + @Override + public boolean wasNull() throws SQLException { + checkOpen(); + return this.delegate().wasNull(); + } + + @Override + public String getString(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getString(parameterIndex); + } + + @Override + public boolean getBoolean(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getBoolean(parameterIndex); + } + + @Override + public byte getByte(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getByte(parameterIndex); + } + + @Override + public short getShort(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getShort(parameterIndex); + } + + @Override + public int getInt(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getInt(parameterIndex); + } + + @Override + public long getLong(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getLong(parameterIndex); + } + + @Override + public float getFloat(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getFloat(parameterIndex); + } + + @Override + public double getDouble(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getDouble(parameterIndex); + } + + @Deprecated + @Override + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(parameterIndex, scale); + } + + @Override + public byte[] getBytes(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getBytes(parameterIndex); + } + + @Override + public Date getDate(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getDate(parameterIndex); + } + + @Override + public Time getTime(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getTime(parameterIndex); + } + + @Override + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(parameterIndex); + } + + @Override + public Object getObject(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getObject(parameterIndex); + } + + @Override + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(parameterIndex); + } + + @Override + public Object getObject(int parameterIndex, Map> map) throws SQLException { + checkOpen(); + return this.delegate().getObject(parameterIndex, map); + } + + @Override + public Ref getRef(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getRef(parameterIndex); + } + + @Override + public Blob getBlob(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getBlob(parameterIndex); + } + + @Override + public Clob getClob(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getClob(parameterIndex); + } + + @Override + public Array getArray(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getArray(parameterIndex); + } + + @Override + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getDate(parameterIndex, cal); + } + + @Override + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTime(parameterIndex, cal); + } + + @Override + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(parameterIndex, cal); + } + + @Override + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterIndex, sqlType, typeName); + } + + @Override + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterName, sqlType); + } + + @Override + public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterName, sqlType, scale); + } + + @Override + public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterName, sqlType, typeName); + } + + @Override + public URL getURL(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getURL(parameterIndex); + } + + @Override + public void setURL(String parameterName, URL val) throws SQLException { + checkOpen(); + this.delegate().setURL(parameterName, val); + } + + @Override + public void setNull(String parameterName, int sqlType) throws SQLException { + checkOpen(); + this.delegate().setNull(parameterName, sqlType); + } + + @Override + public void setBoolean(String parameterName, boolean x) throws SQLException { + checkOpen(); + this.delegate().setBoolean(parameterName, x); + } + + @Override + public void setByte(String parameterName, byte x) throws SQLException { + checkOpen(); + this.delegate().setByte(parameterName, x); + } + + @Override + public void setShort(String parameterName, short x) throws SQLException { + checkOpen(); + this.delegate().setShort(parameterName, x); + } + + @Override + public void setInt(String parameterName, int x) throws SQLException { + checkOpen(); + this.delegate().setInt(parameterName, x); + } + + @Override + public void setLong(String parameterName, long x) throws SQLException { + checkOpen(); + this.delegate().setLong(parameterName, x); + } + + @Override + public void setFloat(String parameterName, float x) throws SQLException { + checkOpen(); + this.delegate().setFloat(parameterName, x); + } + + @Override + public void setDouble(String parameterName, double x) throws SQLException { + checkOpen(); + this.delegate().setDouble(parameterName, x); + } + + @Override + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + checkOpen(); + this.delegate().setBigDecimal(parameterName, x); + } + + @Override + public void setString(String parameterName, String x) throws SQLException { + checkOpen(); + this.delegate().setString(parameterName, x); + } + + @Override + public void setBytes(String parameterName, byte[] x) throws SQLException { + checkOpen(); + this.delegate().setBytes(parameterName, x); + } + + @Override + public void setDate(String parameterName, Date x) throws SQLException { + checkOpen(); + this.delegate().setDate(parameterName, x); + } + + @Override + public void setTime(String parameterName, Time x) throws SQLException { + checkOpen(); + this.delegate().setTime(parameterName, x); + } + + @Override + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + checkOpen(); + this.delegate().setTimestamp(parameterName, x); + } + + @Override + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().setAsciiStream(parameterName, x, length); + } + + @Override + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().setBinaryStream(parameterName, x, length); + } + + @Override + public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterName, x, targetSqlType, scale); + } + + @Override + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterName, x, targetSqlType); + } + + @Override + public void setObject(String parameterName, Object x) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterName, x); + } + + @Override + public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + checkOpen(); + this.delegate().setCharacterStream(parameterName, reader, length); + } + + @Override + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + checkOpen(); + this.delegate().setDate(parameterName, x, cal); + } + + @Override + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + checkOpen(); + this.delegate().setTime(parameterName, x, cal); + } + + @Override + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + checkOpen(); + this.delegate().setTimestamp(parameterName, x, cal); + } + + @Override + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + checkOpen(); + this.delegate().setNull(parameterName, sqlType, typeName); + } + + @Override + public String getString(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getString(parameterName); + } + + @Override + public boolean getBoolean(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getBoolean(parameterName); + } + + @Override + public byte getByte(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getByte(parameterName); + } + + @Override + public short getShort(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getShort(parameterName); + } + + @Override + public int getInt(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getInt(parameterName); + } + + @Override + public long getLong(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getLong(parameterName); + } + + @Override + public float getFloat(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getFloat(parameterName); + } + + @Override + public double getDouble(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getDouble(parameterName); + } + + @Override + public byte[] getBytes(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getBytes(parameterName); + } + + @Override + public Date getDate(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getDate(parameterName); + } + + @Override + public Time getTime(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getTime(parameterName); + } + + @Override + public Timestamp getTimestamp(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(parameterName); + } + + @Override + public Object getObject(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getObject(parameterName); + } + + @Override + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(parameterName); + } + + @Override + public Object getObject(String parameterName, Map> map) throws SQLException { + checkOpen(); + return this.delegate().getObject(parameterName, map); + } + + @Override + public Ref getRef(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getRef(parameterName); + } + + @Override + public Blob getBlob(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getBlob(parameterName); + } + + @Override + public Clob getClob(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getClob(parameterName); + } + + @Override + public Array getArray(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getArray(parameterName); + } + + @Override + public Date getDate(String parameterName, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getDate(parameterName, cal); + } + + @Override + public Time getTime(String parameterName, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTime(parameterName, cal); + } + + @Override + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(parameterName, cal); + } + + @Override + public URL getURL(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getURL(parameterName); + } + + @Override + public RowId getRowId(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getRowId(parameterIndex); + } + + @Override + public RowId getRowId(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getRowId(parameterName); + } + + @Override + public void setRowId(String parameterName, RowId x) throws SQLException { + checkOpen(); + this.delegate().setRowId(parameterName, x); + } + + @Override + public void setNString(String parameterName, String value) throws SQLException { + checkOpen(); + this.delegate().setNString(parameterName, value); + } + + @Override + public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { + checkOpen(); + this.delegate().setNCharacterStream(parameterName, value, length); + } + + @Override + public void setNClob(String parameterName, NClob value) throws SQLException { + checkOpen(); + this.delegate().setNClob(parameterName, value); + } + + @Override + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().setClob(parameterName, reader, length); + } + + @Override + public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { + checkOpen(); + this.delegate().setBlob(parameterName, inputStream, length); + } + + @Override + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().setNClob(parameterName, reader, length); + } + + @Override + public NClob getNClob(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getNClob(parameterIndex); + } + + @Override + public NClob getNClob(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getNClob(parameterName); + } + + @Override + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + checkOpen(); + this.delegate().setSQLXML(parameterName, xmlObject); + } + + @Override + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getSQLXML(parameterIndex); + } + + @Override + public SQLXML getSQLXML(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getSQLXML(parameterName); + } + + @Override + public String getNString(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getNString(parameterIndex); + } + + @Override + public String getNString(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getNString(parameterName); + } + + @Override + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getNCharacterStream(parameterIndex); + } + + @Override + public Reader getNCharacterStream(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getNCharacterStream(parameterName); + } + + @Override + public Reader getCharacterStream(int parameterIndex) throws SQLException { + checkOpen(); + return this.delegate().getCharacterStream(parameterIndex); + } + + @Override + public Reader getCharacterStream(String parameterName) throws SQLException { + checkOpen(); + return this.delegate().getCharacterStream(parameterName); + } + + @Override + public void setBlob(String parameterName, Blob x) throws SQLException { + checkOpen(); + this.delegate().setBlob(parameterName, x); + } + + @Override + public void setClob(String parameterName, Clob x) throws SQLException { + checkOpen(); + this.delegate().setClob(parameterName, x); + } + + @Override + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().setAsciiStream(parameterName, x, length); + } + + @Override + public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().setBinaryStream(parameterName, x, length); + } + + @Override + public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().setCharacterStream(parameterName, reader, length); + } + + @Override + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + checkOpen(); + this.delegate().setAsciiStream(parameterName, x); + } + + @Override + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + checkOpen(); + this.delegate().setBinaryStream(parameterName, x); + } + + @Override + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + checkOpen(); + this.delegate().setCharacterStream(parameterName, reader); + } + + @Override + public void setNCharacterStream(String parameterName, Reader value) throws SQLException { + checkOpen(); + this.delegate().setNCharacterStream(parameterName, value); + } + + @Override + public void setClob(String parameterName, Reader reader) throws SQLException { + checkOpen(); + this.delegate().setClob(parameterName, reader); + } + + @Override + public void setBlob(String parameterName, InputStream inputStream) throws SQLException { + checkOpen(); + this.delegate().setBlob(parameterName, inputStream); + } + + @Override + public void setNClob(String parameterName, Reader reader) throws SQLException { + checkOpen(); + this.delegate().setNClob(parameterName, reader); + } + + @Override + public T getObject(int parameterIndex, Class type) throws SQLException { + checkOpen(); + return this.delegate().getObject(parameterIndex, type); + } + + @Override + public T getObject(String parameterName, Class type) throws SQLException { + checkOpen(); + return this.delegate().getObject(parameterName, type); + } + + @Override + public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterName, x, targetSqlType, scaleOrLength); + } + + @Override + public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterName, x, targetSqlType); + } + + @Override + public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterIndex, sqlType); + } + + @Override + public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterIndex, sqlType, scale); + } + + @Override + public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterIndex, sqlType, typeName); + } + + @Override + public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterName, sqlType); + } + + @Override + public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterName, sqlType, scale); + } + + @Override + public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { + checkOpen(); + this.delegate().registerOutParameter(parameterName, sqlType, typeName); + } + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingConnection.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingConnection.java index 913f61d8631..14b8b242157 100644 --- a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingConnection.java +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,334 +37,399 @@ import java.util.concurrent.Executor; /** - * A {@link Connection} that delegates to another {@link Connection}. + * A JDBC + * 4.3-compliant {@link Connection} that delegates to another JDBC 4.3-compliant {@link Connection}. */ public class DelegatingConnection implements Connection { private final Connection delegate; - protected DelegatingConnection(final Connection delegate) { + /** + * Creates a new {@link DelegatingConnection}. + * + * @param delegate the {@link Connection} to which all operations will be delegated; must not be {@code null} + * + * @exception NullPointerException if {@code delegate} is {@code null} + */ + public DelegatingConnection(Connection delegate) { super(); - this.delegate = Objects.requireNonNull(delegate); + this.delegate = Objects.requireNonNull(delegate, "delegate"); + } + + /** + * Returns this {@link DelegatingConnection}'s underlying {@link Connection}. + * + *

This method never returns {@code null}.

+ * + * @return this {@link DelegatingConnection}'s underlying {@link Connection}; never {@code null} + */ + public final Connection delegate() { + return this.delegate; } @Override public Statement createStatement() throws SQLException { - return this.delegate.createStatement(); + return + new DelegatingStatement<>(this, // NOTE + this.delegate().createStatement(), + true, + true); } @Override - public PreparedStatement prepareStatement(final String sql) throws SQLException { - return this.delegate.prepareStatement(sql); + public PreparedStatement prepareStatement(String sql) throws SQLException { + return + new DelegatingPreparedStatement<>(this, // NOTE + this.delegate().prepareStatement(sql), + true, + true); } @Override - public CallableStatement prepareCall(final String sql) throws SQLException { - return this.delegate.prepareCall(sql); + public CallableStatement prepareCall(String sql) throws SQLException { + return new DelegatingCallableStatement(this, // NOTE + this.delegate().prepareCall(sql), + true, + true); } @Override - public String nativeSQL(final String sql) throws SQLException { - return this.delegate.nativeSQL(sql); + public String nativeSQL(String sql) throws SQLException { + return this.delegate().nativeSQL(sql); } @Override public void setAutoCommit(boolean autoCommit) throws SQLException { - this.delegate.setAutoCommit(autoCommit); + this.delegate().setAutoCommit(autoCommit); } @Override public boolean getAutoCommit() throws SQLException { - return this.delegate.getAutoCommit(); + return this.delegate().getAutoCommit(); } @Override public void commit() throws SQLException { - this.delegate.commit(); + this.delegate().commit(); } @Override public void rollback() throws SQLException { - this.delegate.rollback(); + this.delegate().rollback(); } @Override public void close() throws SQLException { - this.delegate.close(); + // (No need to check isClosed().) + this.delegate().close(); } @Override public boolean isClosed() throws SQLException { - return this.delegate.isClosed(); + return this.delegate().isClosed(); } @Override public DatabaseMetaData getMetaData() throws SQLException { - return this.delegate.getMetaData(); + return + new DelegatingDatabaseMetaData(this, // NOTE + this.delegate().getMetaData()); } @Override public void setReadOnly(boolean readOnly) throws SQLException { - this.delegate.setReadOnly(readOnly); + this.delegate().setReadOnly(readOnly); } @Override public boolean isReadOnly() throws SQLException { - return this.delegate.isReadOnly(); + return this.delegate().isReadOnly(); } @Override public void setCatalog(String catalog) throws SQLException { - this.delegate.setCatalog(catalog); + this.delegate().setCatalog(catalog); } @Override public String getCatalog() throws SQLException { - return this.delegate.getCatalog(); + return this.delegate().getCatalog(); } @Override - public void setTransactionIsolation(final int level) throws SQLException { - this.delegate.setTransactionIsolation(level); + public void setTransactionIsolation(int level) throws SQLException { + this.delegate().setTransactionIsolation(level); } @Override public int getTransactionIsolation() throws SQLException { - return this.delegate.getTransactionIsolation(); + return this.delegate().getTransactionIsolation(); } @Override public SQLWarning getWarnings() throws SQLException { - return this.delegate.getWarnings(); + return this.delegate().getWarnings(); } @Override public void clearWarnings() throws SQLException { - this.delegate.clearWarnings(); + this.delegate().clearWarnings(); } @Override - public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException { - return this.delegate.createStatement(resultSetType, resultSetConcurrency); + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return + new DelegatingStatement<>(this, // NOTE + this.delegate().createStatement(resultSetType, resultSetConcurrency), + true, + true); } @Override - public PreparedStatement prepareStatement(final String sql, - final int resultSetType, - final int resultSetConcurrency) - throws SQLException { - return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return + new DelegatingPreparedStatement<>(this, // NOTE + this.delegate().prepareStatement(sql, resultSetType, resultSetConcurrency), + true, + true); } @Override - public CallableStatement prepareCall(final String sql, - final int resultSetType, - final int resultSetConcurrency) - throws SQLException { - return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency); + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return + new DelegatingCallableStatement(this, // NOTE + this.delegate().prepareCall(sql, resultSetType, resultSetConcurrency), + true, + true); } @Override public Map> getTypeMap() throws SQLException { - return this.delegate.getTypeMap(); + return this.delegate().getTypeMap(); } @Override public void setTypeMap(Map> map) throws SQLException { - this.delegate.setTypeMap(map); + this.delegate().setTypeMap(map); } @Override - public void setHoldability(final int holdability) throws SQLException { - this.delegate.setHoldability(holdability); + public void setHoldability(int holdability) throws SQLException { + this.delegate().setHoldability(holdability); } @Override public int getHoldability() throws SQLException { - return this.delegate.getHoldability(); + return this.delegate().getHoldability(); } @Override public Savepoint setSavepoint() throws SQLException { - return this.delegate.setSavepoint(); + return this.delegate().setSavepoint(); } @Override - public Savepoint setSavepoint(final String name) throws SQLException { - return this.delegate.setSavepoint(name); + public Savepoint setSavepoint(String name) throws SQLException { + return this.delegate().setSavepoint(name); } @Override - public void rollback(final Savepoint savepoint) throws SQLException { - this.delegate.rollback(savepoint); + public void rollback(Savepoint savepoint) throws SQLException { + this.delegate().rollback(savepoint); } @Override - public void releaseSavepoint(final Savepoint savepoint) throws SQLException { - this.delegate.releaseSavepoint(savepoint); + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + this.delegate().releaseSavepoint(savepoint); } @Override - public Statement createStatement(final int resultSetType, - final int resultSetConcurrency, - final int resultSetHoldability) - throws SQLException { - return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return + new DelegatingStatement<>(this, // NOTE + this.delegate().createStatement(resultSetType, + resultSetConcurrency, + resultSetHoldability), + true, + true); } @Override - public PreparedStatement prepareStatement(final String sql, final int resultSetType, - final int resultSetConcurrency, - final int resultSetHoldability) - throws SQLException { - return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return + new DelegatingPreparedStatement<>(this, // NOTE + this.delegate().prepareStatement(sql, + resultSetType, + resultSetConcurrency, + resultSetHoldability), + true, + true); } @Override - public CallableStatement prepareCall(final String sql, - final int resultSetType, - final int resultSetConcurrency, - final int resultSetHoldability) - throws SQLException { - return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return + new DelegatingCallableStatement(this, // NOTE + this.delegate().prepareCall(sql, + resultSetType, + resultSetConcurrency, + resultSetHoldability), + true, + true); } @Override - public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { - return this.delegate.prepareStatement(sql, autoGeneratedKeys); + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return + new DelegatingPreparedStatement<>(this, // NOTE + this.delegate().prepareStatement(sql, autoGeneratedKeys), + true, + true); } @Override - public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { - return this.delegate.prepareStatement(sql, columnIndexes); + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return + new DelegatingPreparedStatement<>(this, // NOTE + this.delegate().prepareStatement(sql, columnIndexes), + true, + true); } @Override - public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { - return this.delegate.prepareStatement(sql, columnNames); + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return + new DelegatingPreparedStatement<>(this, // NOTE + this.delegate().prepareStatement(sql, columnNames), + true, + true); } @Override public Clob createClob() throws SQLException { - return this.delegate.createClob(); + return this.delegate().createClob(); } @Override public Blob createBlob() throws SQLException { - return this.delegate.createBlob(); + return this.delegate().createBlob(); } @Override public NClob createNClob() throws SQLException { - return this.delegate.createNClob(); + return this.delegate().createNClob(); } @Override public SQLXML createSQLXML() throws SQLException { - return this.delegate.createSQLXML(); + return this.delegate().createSQLXML(); } @Override - public boolean isValid(final int timeout) throws SQLException { - return this.delegate.isValid(timeout); + public boolean isValid(int timeout) throws SQLException { + // (No need to check isClosed().) + return this.delegate().isValid(timeout); } @Override - public void setClientInfo(final String name, final String value) throws SQLClientInfoException { - this.delegate.setClientInfo(name, value); + public void setClientInfo(String name, String value) throws SQLClientInfoException { + this.delegate().setClientInfo(name, value); } @Override - public void setClientInfo(final Properties properties) throws SQLClientInfoException { - this.delegate.setClientInfo(properties); + public void setClientInfo(Properties properties) throws SQLClientInfoException { + this.delegate().setClientInfo(properties); } @Override - public String getClientInfo(final String name) throws SQLException { - return this.delegate.getClientInfo(name); + public String getClientInfo(String name) throws SQLException { + return this.delegate().getClientInfo(name); } @Override public Properties getClientInfo() throws SQLException { - return this.delegate.getClientInfo(); + return this.delegate().getClientInfo(); } @Override - public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { - return this.delegate.createArrayOf(typeName, elements); + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return this.delegate().createArrayOf(typeName, elements); } @Override - public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException { - return this.delegate.createStruct(typeName, attributes); + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return this.delegate().createStruct(typeName, attributes); } @Override - public void setSchema(final String schema) throws SQLException { - this.delegate.setSchema(schema); + public void setSchema(String schema) throws SQLException { + this.delegate().setSchema(schema); } @Override public String getSchema() throws SQLException { - return this.delegate.getSchema(); + return this.delegate().getSchema(); } @Override - public void abort(final Executor executor) throws SQLException { - this.delegate.abort(executor); + public void abort(Executor executor) throws SQLException { + // (No need to check isClosed().) + this.delegate().abort(executor); } @Override - public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException { - this.delegate.setNetworkTimeout(executor, milliseconds); + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + this.delegate().setNetworkTimeout(executor, milliseconds); } @Override public int getNetworkTimeout() throws SQLException { - return this.delegate.getNetworkTimeout(); + return this.delegate().getNetworkTimeout(); } @Override public void beginRequest() throws SQLException { - this.delegate.beginRequest(); + // (No need to check isClosed().) + this.delegate().beginRequest(); } @Override public void endRequest() throws SQLException { - this.delegate.endRequest(); + // (No need to check isClosed().) + this.delegate().endRequest(); } @Override - public boolean setShardingKeyIfValid(final ShardingKey shardingKey, - final ShardingKey superShardingKey, - final int timeout) - throws SQLException { - return this.delegate.setShardingKeyIfValid(shardingKey, superShardingKey, timeout); + public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) + throws SQLException { + return this.delegate().setShardingKeyIfValid(shardingKey, superShardingKey, timeout); } @Override - public boolean setShardingKeyIfValid(final ShardingKey shardingKey, final int timeout) throws SQLException { - return this.delegate.setShardingKeyIfValid(shardingKey, timeout); + public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLException { + return this.delegate().setShardingKeyIfValid(shardingKey, timeout); } @Override - public void setShardingKey(final ShardingKey shardingKey, final ShardingKey superShardingKey) throws SQLException { - this.delegate.setShardingKey(shardingKey, superShardingKey); + public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLException { + this.delegate().setShardingKey(shardingKey, superShardingKey); } @Override - public void setShardingKey(final ShardingKey shardingKey) throws SQLException { - this.delegate.setShardingKey(shardingKey); + public void setShardingKey(ShardingKey shardingKey) throws SQLException { + this.delegate().setShardingKey(shardingKey); } @Override - public T unwrap(final Class iface) throws SQLException { - return this.delegate.unwrap(iface); + public T unwrap(Class iface) throws SQLException { + return iface.isInstance(this) ? iface.cast(this) : this.delegate().unwrap(iface); } @Override - public boolean isWrapperFor(final Class iface) throws SQLException { - return this.delegate.isWrapperFor(iface); + public boolean isWrapperFor(Class iface) throws SQLException { + return iface.isInstance(this) || this.delegate().isWrapperFor(iface); } } diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingDatabaseMetaData.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingDatabaseMetaData.java new file mode 100644 index 00000000000..a829930b9b4 --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingDatabaseMetaData.java @@ -0,0 +1,968 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; +import java.util.Objects; + +/** + * A JDBC + * 4.3-compliant {@link DatabaseMetaData} that delegates to another JDBC 4.3-compliant {@link DatabaseMetaData}. + */ +public class DelegatingDatabaseMetaData implements DatabaseMetaData { + + private final Connection connection; + + private final DatabaseMetaData delegate; + + /** + * Creates a new {@link DelegatingDatabaseMetaData}. + * + * @param connection the {@link Connection} that will be returned by the {@link #getConnection()} method; must not + * be {@code null} + * + * @param delegate the {@link DatabaseMetaData} to which all operations will be delegated; must not be {@code null} + * + * @exception NullPointerException if either {@code connection} or {@code delegate} is {@code null} + */ + protected DelegatingDatabaseMetaData(Connection connection, DatabaseMetaData delegate) { + super(); + this.connection = Objects.requireNonNull(connection, "connection"); + this.delegate = Objects.requireNonNull(delegate, "delegate"); + } + + @Override + public boolean allProceduresAreCallable() throws SQLException { + return this.delegate.allProceduresAreCallable(); + } + + @Override + public boolean allTablesAreSelectable() throws SQLException { + return this.delegate.allTablesAreSelectable(); + } + + @Override + public String getURL() throws SQLException { + return this.delegate.getURL(); + } + + @Override + public String getUserName() throws SQLException { + return this.delegate.getUserName(); + } + + @Override + public boolean isReadOnly() throws SQLException { + return this.delegate.isReadOnly(); + } + + @Override + public boolean nullsAreSortedHigh() throws SQLException { + return this.delegate.nullsAreSortedHigh(); + } + + @Override + public boolean nullsAreSortedLow() throws SQLException { + return this.delegate.nullsAreSortedLow(); + } + + @Override + public boolean nullsAreSortedAtStart() throws SQLException { + return this.delegate.nullsAreSortedAtStart(); + } + + @Override + public boolean nullsAreSortedAtEnd() throws SQLException { + return this.delegate.nullsAreSortedAtEnd(); + } + + @Override + public String getDatabaseProductName() throws SQLException { + return this.delegate.getDatabaseProductName(); + } + + @Override + public String getDatabaseProductVersion() throws SQLException { + return this.delegate.getDatabaseProductVersion(); + } + + @Override + public String getDriverName() throws SQLException { + return this.delegate.getDriverName(); + } + + @Override + public String getDriverVersion() throws SQLException { + return this.delegate.getDriverVersion(); + } + + @Override + public int getDriverMajorVersion() { + return this.delegate.getDriverMajorVersion(); + } + + @Override + public int getDriverMinorVersion() { + return this.delegate.getDriverMinorVersion(); + } + + @Override + public boolean usesLocalFiles() throws SQLException { + return this.delegate.usesLocalFiles(); + } + + @Override + public boolean usesLocalFilePerTable() throws SQLException { + return this.delegate.usesLocalFilePerTable(); + } + + @Override + public boolean supportsMixedCaseIdentifiers() throws SQLException { + return this.delegate.supportsMixedCaseIdentifiers(); + } + + @Override + public boolean storesUpperCaseIdentifiers() throws SQLException { + return this.delegate.storesUpperCaseIdentifiers(); + } + + @Override + public boolean storesLowerCaseIdentifiers() throws SQLException { + return this.delegate.storesLowerCaseIdentifiers(); + } + + @Override + public boolean storesMixedCaseIdentifiers() throws SQLException { + return this.delegate.storesMixedCaseIdentifiers(); + } + + @Override + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + return this.delegate.supportsMixedCaseQuotedIdentifiers(); + } + + @Override + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + return this.delegate.storesUpperCaseQuotedIdentifiers(); + } + + @Override + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + return this.delegate.storesLowerCaseQuotedIdentifiers(); + } + + @Override + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + return this.delegate.storesMixedCaseQuotedIdentifiers(); + } + + @Override + public String getIdentifierQuoteString() throws SQLException { + return this.delegate.getIdentifierQuoteString(); + } + + @Override + public String getSQLKeywords() throws SQLException { + return this.delegate.getSQLKeywords(); + } + + @Override + public String getNumericFunctions() throws SQLException { + return this.delegate.getNumericFunctions(); + } + + @Override + public String getStringFunctions() throws SQLException { + return this.delegate.getStringFunctions(); + } + + @Override + public String getSystemFunctions() throws SQLException { + return this.delegate.getSystemFunctions(); + } + + @Override + public String getTimeDateFunctions() throws SQLException { + return this.delegate.getTimeDateFunctions(); + } + + @Override + public String getSearchStringEscape() throws SQLException { + return this.delegate.getSearchStringEscape(); + } + + @Override + public String getExtraNameCharacters() throws SQLException { + return this.delegate.getExtraNameCharacters(); + } + + @Override + public boolean supportsAlterTableWithAddColumn() throws SQLException { + return this.delegate.supportsAlterTableWithAddColumn(); + } + + @Override + public boolean supportsAlterTableWithDropColumn() throws SQLException { + return this.delegate.supportsAlterTableWithDropColumn(); + } + + @Override + public boolean supportsColumnAliasing() throws SQLException { + return this.delegate.supportsColumnAliasing(); + } + + @Override + public boolean nullPlusNonNullIsNull() throws SQLException { + return this.delegate.nullPlusNonNullIsNull(); + } + + @Override + public boolean supportsConvert() throws SQLException { + return this.delegate.supportsConvert(); + } + + @Override + public boolean supportsConvert(int fromType, int toType) throws SQLException { + return this.delegate.supportsConvert(fromType, toType); + } + + @Override + public boolean supportsTableCorrelationNames() throws SQLException { + return this.delegate.supportsTableCorrelationNames(); + } + + @Override + public boolean supportsDifferentTableCorrelationNames() throws SQLException { + return this.delegate.supportsDifferentTableCorrelationNames(); + } + + @Override + public boolean supportsExpressionsInOrderBy() throws SQLException { + return this.delegate.supportsExpressionsInOrderBy(); + } + + @Override + public boolean supportsOrderByUnrelated() throws SQLException { + return this.delegate.supportsOrderByUnrelated(); + } + + @Override + public boolean supportsGroupBy() throws SQLException { + return this.delegate.supportsGroupBy(); + } + + @Override + public boolean supportsGroupByUnrelated() throws SQLException { + return this.delegate.supportsGroupByUnrelated(); + } + + @Override + public boolean supportsGroupByBeyondSelect() throws SQLException { + return this.delegate.supportsGroupByBeyondSelect(); + } + + @Override + public boolean supportsLikeEscapeClause() throws SQLException { + return this.delegate.supportsLikeEscapeClause(); + } + + @Override + public boolean supportsMultipleResultSets() throws SQLException { + return this.delegate.supportsMultipleResultSets(); + } + + @Override + public boolean supportsMultipleTransactions() throws SQLException { + return this.delegate.supportsMultipleTransactions(); + } + + @Override + public boolean supportsNonNullableColumns() throws SQLException { + return this.delegate.supportsNonNullableColumns(); + } + + @Override + public boolean supportsMinimumSQLGrammar() throws SQLException { + return this.delegate.supportsMinimumSQLGrammar(); + } + + @Override + public boolean supportsCoreSQLGrammar() throws SQLException { + return this.delegate.supportsCoreSQLGrammar(); + } + + @Override + public boolean supportsExtendedSQLGrammar() throws SQLException { + return this.delegate.supportsExtendedSQLGrammar(); + } + + @Override + public boolean supportsANSI92EntryLevelSQL() throws SQLException { + return this.delegate.supportsANSI92EntryLevelSQL(); + } + + @Override + public boolean supportsANSI92IntermediateSQL() throws SQLException { + return this.delegate.supportsANSI92IntermediateSQL(); + } + + @Override + public boolean supportsANSI92FullSQL() throws SQLException { + return this.delegate.supportsANSI92FullSQL(); + } + + @Override + public boolean supportsIntegrityEnhancementFacility() throws SQLException { + return this.delegate.supportsIntegrityEnhancementFacility(); + } + + @Override + public boolean supportsOuterJoins() throws SQLException { + return this.delegate.supportsOuterJoins(); + } + + @Override + public boolean supportsFullOuterJoins() throws SQLException { + return this.delegate.supportsFullOuterJoins(); + } + + @Override + public boolean supportsLimitedOuterJoins() throws SQLException { + return this.delegate.supportsLimitedOuterJoins(); + } + + @Override + public String getSchemaTerm() throws SQLException { + return this.delegate.getSchemaTerm(); + } + + @Override + public String getProcedureTerm() throws SQLException { + return this.delegate.getProcedureTerm(); + } + + @Override + public String getCatalogTerm() throws SQLException { + return this.delegate.getCatalogTerm(); + } + + @Override + public boolean isCatalogAtStart() throws SQLException { + return this.delegate.isCatalogAtStart(); + } + + @Override + public String getCatalogSeparator() throws SQLException { + return this.delegate.getCatalogSeparator(); + } + + @Override + public boolean supportsSchemasInDataManipulation() throws SQLException { + return this.delegate.supportsSchemasInDataManipulation(); + } + + @Override + public boolean supportsSchemasInProcedureCalls() throws SQLException { + return this.delegate.supportsSchemasInProcedureCalls(); + } + + @Override + public boolean supportsSchemasInTableDefinitions() throws SQLException { + return this.delegate.supportsSchemasInTableDefinitions(); + } + + @Override + public boolean supportsSchemasInIndexDefinitions() throws SQLException { + return this.delegate.supportsSchemasInIndexDefinitions(); + } + + @Override + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + return this.delegate.supportsSchemasInPrivilegeDefinitions(); + } + + @Override + public boolean supportsCatalogsInDataManipulation() throws SQLException { + return this.delegate.supportsCatalogsInDataManipulation(); + } + + @Override + public boolean supportsCatalogsInProcedureCalls() throws SQLException { + return this.delegate.supportsCatalogsInProcedureCalls(); + } + + @Override + public boolean supportsCatalogsInTableDefinitions() throws SQLException { + return this.delegate.supportsCatalogsInTableDefinitions(); + } + + @Override + public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + return this.delegate.supportsCatalogsInIndexDefinitions(); + } + + @Override + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + return this.delegate.supportsCatalogsInPrivilegeDefinitions(); + } + + @Override + public boolean supportsPositionedDelete() throws SQLException { + return this.delegate.supportsPositionedDelete(); + } + + @Override + public boolean supportsPositionedUpdate() throws SQLException { + return this.delegate.supportsPositionedUpdate(); + } + + @Override + public boolean supportsSelectForUpdate() throws SQLException { + return this.delegate.supportsSelectForUpdate(); + } + + @Override + public boolean supportsStoredProcedures() throws SQLException { + return this.delegate.supportsStoredProcedures(); + } + + @Override + public boolean supportsSubqueriesInComparisons() throws SQLException { + return this.delegate.supportsSubqueriesInComparisons(); + } + + @Override + public boolean supportsSubqueriesInExists() throws SQLException { + return this.delegate.supportsSubqueriesInExists(); + } + + @Override + public boolean supportsSubqueriesInIns() throws SQLException { + return this.delegate.supportsSubqueriesInIns(); + } + + @Override + public boolean supportsSubqueriesInQuantifieds() throws SQLException { + return this.delegate.supportsSubqueriesInQuantifieds(); + } + + @Override + public boolean supportsCorrelatedSubqueries() throws SQLException { + return this.delegate.supportsCorrelatedSubqueries(); + } + + @Override + public boolean supportsUnion() throws SQLException { + return this.delegate.supportsUnion(); + } + + @Override + public boolean supportsUnionAll() throws SQLException { + return this.delegate.supportsUnionAll(); + } + + @Override + public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + return this.delegate.supportsOpenCursorsAcrossCommit(); + } + + @Override + public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + return this.delegate.supportsOpenCursorsAcrossRollback(); + } + + @Override + public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + return this.delegate.supportsOpenStatementsAcrossCommit(); + } + + @Override + public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + return this.delegate.supportsOpenStatementsAcrossRollback(); + } + + @Override + public int getMaxBinaryLiteralLength() throws SQLException { + return this.delegate.getMaxBinaryLiteralLength(); + } + + @Override + public int getMaxCharLiteralLength() throws SQLException { + return this.delegate.getMaxCharLiteralLength(); + } + + @Override + public int getMaxColumnNameLength() throws SQLException { + return this.delegate.getMaxColumnNameLength(); + } + + @Override + public int getMaxColumnsInGroupBy() throws SQLException { + return this.delegate.getMaxColumnsInGroupBy(); + } + + @Override + public int getMaxColumnsInIndex() throws SQLException { + return this.delegate.getMaxColumnsInIndex(); + } + + @Override + public int getMaxColumnsInOrderBy() throws SQLException { + return this.delegate.getMaxColumnsInOrderBy(); + } + + @Override + public int getMaxColumnsInSelect() throws SQLException { + return this.delegate.getMaxColumnsInSelect(); + } + + @Override + public int getMaxColumnsInTable() throws SQLException { + return this.delegate.getMaxColumnsInTable(); + } + + @Override + public int getMaxConnections() throws SQLException { + return this.delegate.getMaxConnections(); + } + + @Override + public int getMaxCursorNameLength() throws SQLException { + return this.delegate.getMaxCursorNameLength(); + } + + @Override + public int getMaxIndexLength() throws SQLException { + return this.delegate.getMaxIndexLength(); + } + + @Override + public int getMaxSchemaNameLength() throws SQLException { + return this.delegate.getMaxSchemaNameLength(); + } + + @Override + public int getMaxProcedureNameLength() throws SQLException { + return this.delegate.getMaxProcedureNameLength(); + } + + @Override + public int getMaxCatalogNameLength() throws SQLException { + return this.delegate.getMaxCatalogNameLength(); + } + + @Override + public int getMaxRowSize() throws SQLException { + return this.delegate.getMaxRowSize(); + } + + @Override + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + return this.delegate.doesMaxRowSizeIncludeBlobs(); + } + + @Override + public int getMaxStatementLength() throws SQLException { + return this.delegate.getMaxStatementLength(); + } + + @Override + public int getMaxStatements() throws SQLException { + return this.delegate.getMaxStatements(); + } + + @Override + public int getMaxTableNameLength() throws SQLException { + return this.delegate.getMaxTableNameLength(); + } + + @Override + public int getMaxTablesInSelect() throws SQLException { + return this.delegate.getMaxTablesInSelect(); + } + + @Override + public int getMaxUserNameLength() throws SQLException { + return this.delegate.getMaxUserNameLength(); + } + + @Override + public int getDefaultTransactionIsolation() throws SQLException { + return this.delegate.getDefaultTransactionIsolation(); + } + + @Override + public boolean supportsTransactions() throws SQLException { + return this.delegate.supportsTransactions(); + } + + @Override + public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + return this.delegate.supportsTransactionIsolationLevel(level); + } + + @Override + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + return this.delegate.supportsDataDefinitionAndDataManipulationTransactions(); + } + + @Override + public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + return this.delegate.supportsDataManipulationTransactionsOnly(); + } + + @Override + public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + return this.delegate.dataDefinitionCausesTransactionCommit(); + } + + @Override + public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + return this.delegate.dataDefinitionIgnoredInTransactions(); + } + + @Override + public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + return this.delegate.getProcedures(catalog, schemaPattern, procedureNamePattern); + } + + @Override + public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedurePattern, String columnNamePattern) + throws SQLException { + return this.delegate.getProcedureColumns(catalog, schemaPattern, procedurePattern, columnNamePattern); + } + + @Override + public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) + throws SQLException { + return this.delegate.getTables(catalog, schemaPattern, tableNamePattern, types); + } + + @Override + public ResultSet getSchemas() throws SQLException { + return this.delegate.getSchemas(); + } + + @Override + public ResultSet getCatalogs() throws SQLException { + return this.delegate.getCatalogs(); + } + + @Override + public ResultSet getTableTypes() throws SQLException { + return this.delegate.getTableTypes(); + } + + @Override + public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) + throws SQLException { + return this.delegate.getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); + } + + @Override + public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) + throws SQLException { + return this.delegate.getColumnPrivileges(catalog, schema, table, columnNamePattern); + } + + @Override + public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + return this.delegate.getTablePrivileges(catalog, schemaPattern, tableNamePattern); + } + + @Override + public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) + throws SQLException { + return this.delegate.getBestRowIdentifier(catalog, schema, table, scope, nullable); + } + + @Override + public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { + return this.delegate.getVersionColumns(catalog, schema, table); + } + + @Override + public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { + return this.delegate.getPrimaryKeys(catalog, schema, table); + } + + @Override + public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { + return this.delegate.getImportedKeys(catalog, schema, table); + } + + @Override + public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { + return this.delegate.getExportedKeys(catalog, schema, table); + } + + @Override + public ResultSet getCrossReference(String parentCatalog, + String parentSchema, + String parentTable, + String foreignCatalog, + String foreignSchema, + String foreignTable) + throws SQLException { + return + this.delegate.getCrossReference(parentCatalog, + parentSchema, + parentTable, + foreignCatalog, + foreignSchema, + foreignTable); + } + + @Override + public ResultSet getTypeInfo() throws SQLException { + return this.delegate.getTypeInfo(); + } + + @Override + public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) + throws SQLException { + return this.delegate.getIndexInfo(catalog, schema, table, unique, approximate); + } + + @Override + public boolean supportsResultSetType(int type) throws SQLException { + return this.delegate.supportsResultSetType(type); + } + + @Override + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + return this.delegate.supportsResultSetConcurrency(type, concurrency); + } + + @Override + public boolean ownUpdatesAreVisible(int type) throws SQLException { + return this.delegate.ownUpdatesAreVisible(type); + } + + @Override + public boolean ownDeletesAreVisible(int type) throws SQLException { + return this.delegate.ownDeletesAreVisible(type); + } + + @Override + public boolean ownInsertsAreVisible(int type) throws SQLException { + return this.delegate.ownInsertsAreVisible(type); + } + + @Override + public boolean othersUpdatesAreVisible(int type) throws SQLException { + return this.delegate.othersUpdatesAreVisible(type); + } + + @Override + public boolean othersDeletesAreVisible(int type) throws SQLException { + return this.delegate.othersDeletesAreVisible(type); + } + + @Override + public boolean othersInsertsAreVisible(int type) throws SQLException { + return this.delegate.othersInsertsAreVisible(type); + } + + @Override + public boolean updatesAreDetected(int type) throws SQLException { + return this.delegate.updatesAreDetected(type); + } + + @Override + public boolean deletesAreDetected(int type) throws SQLException { + return this.delegate.deletesAreDetected(type); + } + + @Override + public boolean insertsAreDetected(int type) throws SQLException { + return this.delegate.insertsAreDetected(type); + } + + @Override + public boolean supportsBatchUpdates() throws SQLException { + return this.delegate.supportsBatchUpdates(); + } + + @Override + public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { + return this.delegate.getUDTs(catalog, schemaPattern, typeNamePattern, types); + } + + @Override + public Connection getConnection() throws SQLException { + // NOTE + return this.connection; + } + + @Override + public boolean supportsSavepoints() throws SQLException { + return this.delegate.supportsSavepoints(); + } + + @Override + public boolean supportsNamedParameters() throws SQLException { + return this.delegate.supportsNamedParameters(); + } + + @Override + public boolean supportsMultipleOpenResults() throws SQLException { + return this.delegate.supportsMultipleOpenResults(); + } + + @Override + public boolean supportsGetGeneratedKeys() throws SQLException { + return this.delegate.supportsGetGeneratedKeys(); + } + + @Override + public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException { + return this.delegate.getSuperTypes(catalog, schemaPattern, typeNamePattern); + } + + @Override + public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + return this.delegate.getSuperTables(catalog, schemaPattern, tableNamePattern); + } + + @Override + public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) + throws SQLException { + return this.delegate.getAttributes(catalog, schemaPattern, typeNamePattern, attributeNamePattern); + } + + @Override + public boolean supportsResultSetHoldability(int holdability) throws SQLException { + return this.delegate.supportsResultSetHoldability(holdability); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return this.delegate.getResultSetHoldability(); + } + + @Override + public int getDatabaseMajorVersion() throws SQLException { + return this.delegate.getDatabaseMajorVersion(); + } + + @Override + public int getDatabaseMinorVersion() throws SQLException { + return this.delegate.getDatabaseMinorVersion(); + } + + @Override + public int getJDBCMajorVersion() throws SQLException { + return this.delegate.getJDBCMajorVersion(); + } + + @Override + public int getJDBCMinorVersion() throws SQLException { + return this.delegate.getJDBCMinorVersion(); + } + + @Override + public int getSQLStateType() throws SQLException { + return this.delegate.getSQLStateType(); + } + + @Override + public boolean locatorsUpdateCopy() throws SQLException { + return this.delegate.locatorsUpdateCopy(); + } + + @Override + public boolean supportsStatementPooling() throws SQLException { + return this.delegate.supportsStatementPooling(); + } + + @Override + public RowIdLifetime getRowIdLifetime() throws SQLException { + return this.delegate.getRowIdLifetime(); + } + + @Override + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + return this.delegate.getSchemas(catalog, schemaPattern); + } + + @Override + public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + return this.delegate.supportsStoredFunctionsUsingCallSyntax(); + } + + @Override + public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + return this.delegate.autoCommitFailureClosesAllResultSets(); + } + + @Override + public ResultSet getClientInfoProperties() throws SQLException { + return this.delegate.getClientInfoProperties(); + } + + @Override + public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + return this.delegate.getFunctions(catalog, schemaPattern, functionNamePattern); + } + + @Override + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionPattern, String columnNamePattern) + throws SQLException { + return this.delegate.getFunctionColumns(catalog, schemaPattern, functionPattern, columnNamePattern); + } + + @Override + public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) + throws SQLException { + return this.delegate.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); + } + + @Override + public boolean generatedKeyAlwaysReturned() throws SQLException { + return this.delegate.generatedKeyAlwaysReturned(); + } + + @Override + public long getMaxLogicalLobSize() throws SQLException { + return this.delegate.getMaxLogicalLobSize(); + } + + @Override + public boolean supportsRefCursors() throws SQLException { + return this.delegate.supportsRefCursors(); + } + + @Override + public boolean supportsSharding() throws SQLException { + return this.delegate.supportsSharding(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return iface.isInstance(this) ? iface.cast(this) : this.delegate.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return iface.isInstance(this) || this.delegate.isWrapperFor(iface); + } + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingPreparedStatement.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingPreparedStatement.java new file mode 100644 index 00000000000..611a6ae95f5 --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingPreparedStatement.java @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +/** + * A JDBC + * 4.3-compliant {@link PreparedStatement} that delegates to another JDBC 4.3-compliant {@link PreparedStatement}. + * + * @param the type of the {@link PreparedStatement} subclass + */ +public class DelegatingPreparedStatement extends DelegatingStatement implements PreparedStatement { + + /** + * Creates a new {@link DelegatingPreparedStatement}. + * + * @param connection the {@link Connection} that created this {@link DelegatingPreparedStatement}; must not be + * {@code null} + * + * @param delegate the {@link PreparedStatement} instance to which all operations will be delegated; must not be + * {@code null} + * + * @param closeable the initial value for this {@link DelegatingPreparedStatement}'s {@linkplain #isCloseable() + * closeable} status + * + * @param strictClosedChecking if {@code true}, then this {@link DelegatingPreparedStatement}'s {@link + * #isClosed()} method will be invoked before every operation that cannot take place on a closed statement, and, if + * it returns {@code true}, the operation in question will fail with a {@link SQLException} + * @exception NullPointerException if either argument is {@code + * null} + * + * @see DelegatingStatement#isCloseable() + * + * @see DelegatingStatement#setCloseable(boolean) + * + * @see DelegatingStatement#close() + * + * @see DelegatingStatement#isClosed() + */ + public DelegatingPreparedStatement(Connection connection, + S delegate, + boolean closeable, + boolean strictClosedChecking) { + super(connection, delegate, closeable, strictClosedChecking); + } + + @Override + public ResultSet executeQuery() throws SQLException { + checkOpen(); + return + new DelegatingResultSet(this, // NOTE + this.delegate().executeQuery(), + true, + true); + } + + @Override + public int executeUpdate() throws SQLException { + checkOpen(); + return this.delegate().executeUpdate(); + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + checkOpen(); + this.delegate().setNull(parameterIndex, sqlType); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + checkOpen(); + this.delegate().setBoolean(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + checkOpen(); + this.delegate().setByte(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + checkOpen(); + this.delegate().setShort(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + checkOpen(); + this.delegate().setInt(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + checkOpen(); + this.delegate().setLong(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + checkOpen(); + this.delegate().setFloat(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + checkOpen(); + this.delegate().setDouble(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + checkOpen(); + this.delegate().setBigDecimal(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + checkOpen(); + this.delegate().setString(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + checkOpen(); + this.delegate().setBytes(parameterIndex, x); + } + + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + checkOpen(); + this.delegate().setDate(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + checkOpen(); + this.delegate().setTime(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + checkOpen(); + this.delegate().setTimestamp(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().setAsciiStream(parameterIndex, x, length); + } + + @Deprecated + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().setUnicodeStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().setBinaryStream(parameterIndex, x, length); + } + + @Override + public void clearParameters() throws SQLException { + checkOpen(); + this.delegate().clearParameters(); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterIndex, x, targetSqlType); + } + + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterIndex, x); + } + + @Override + public boolean execute() throws SQLException { + checkOpen(); + return this.delegate().execute(); + } + + @Override + public void addBatch() throws SQLException { + checkOpen(); + this.delegate().addBatch(); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + checkOpen(); + this.delegate().setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + checkOpen(); + this.delegate().setRef(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + checkOpen(); + this.delegate().setBlob(parameterIndex, x); + } + + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + checkOpen(); + this.delegate().setClob(parameterIndex, x); + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + checkOpen(); + this.delegate().setArray(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + checkOpen(); + return this.delegate().getMetaData(); + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + checkOpen(); + this.delegate().setDate(parameterIndex, x, cal); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + checkOpen(); + this.delegate().setTime(parameterIndex, x, cal); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + checkOpen(); + this.delegate().setTimestamp(parameterIndex, x, cal); + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + checkOpen(); + this.delegate().setNull(parameterIndex, sqlType, typeName); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + checkOpen(); + this.delegate().setURL(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + checkOpen(); + return this.delegate().getParameterMetaData(); + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + checkOpen(); + this.delegate().setRowId(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + checkOpen(); + this.delegate().setNString(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + checkOpen(); + this.delegate().setNCharacterStream(parameterIndex, value, length); + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + checkOpen(); + this.delegate().setNClob(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().setClob(parameterIndex, reader, length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + checkOpen(); + this.delegate().setBlob(parameterIndex, inputStream, length); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().setNClob(parameterIndex, reader, length); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + checkOpen(); + this.delegate().setSQLXML(parameterIndex, xmlObject); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().setBinaryStream(parameterIndex, x, length); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + checkOpen(); + this.delegate().setAsciiStream(parameterIndex, x); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + checkOpen(); + this.delegate().setBinaryStream(parameterIndex, x); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + checkOpen(); + this.delegate().setCharacterStream(parameterIndex, reader); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + checkOpen(); + this.delegate().setNCharacterStream(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + checkOpen(); + this.delegate().setClob(parameterIndex, reader); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + checkOpen(); + this.delegate().setBlob(parameterIndex, inputStream); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + checkOpen(); + this.delegate().setNClob(parameterIndex, reader); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + checkOpen(); + this.delegate().setObject(parameterIndex, x, targetSqlType); + } + + @Override + public long executeLargeUpdate() throws SQLException { + checkOpen(); + return this.delegate().executeLargeUpdate(); + } + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingResultSet.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingResultSet.java new file mode 100644 index 00000000000..96593c7135a --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingResultSet.java @@ -0,0 +1,1349 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLNonTransientConnectionException; +import java.sql.SQLType; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Map; +import java.util.Objects; + +/** + * A JDBC + * 4.3-compliant {@link ResultSet} that delegates to another JDBC 4.3-compliant {@link ResultSet}. + */ +public class DelegatingResultSet implements ResultSet { + + private final Statement statement; + + private final ResultSet delegate; + + private final SQLRunnable closedChecker; + + private volatile boolean closeable; + + /** + * Creates a new {@link DelegatingResultSet}. + * + * @param statement the {@link Statement} that will be returned by the {@link #getStatement()} method; may be {@code + * null} + * + * @param delegate the {@link ResultSet} to which all operations will be delegated; must not be {@code null} + * + * @param closeable the initial value for this {@link DelegatingResultSet}'s {@linkplain #isCloseable() closeable} + * status + * + * @param strictClosedChecking if {@code true}, then this {@link DelegatingResultSet}'s {@link #isClosed()} + * method will be invoked before every operation that cannot take place on a closed statement, and, if it returns + * {@code true}, the operation in question will fail with a {@link SQLException} + * + * @exception NullPointerException if {@code delegate} is {@code null} + */ + public DelegatingResultSet(Statement statement, ResultSet delegate, boolean closeable, boolean strictClosedChecking) { + super(); + this.statement = statement; + this.delegate = Objects.requireNonNull(delegate, "delegate"); + this.closeable = closeable; + this.closedChecker = strictClosedChecking ? this::failWhenClosed : DelegatingResultSet::doNothing; + } + + /** + * Returns the {@link ResultSet} to which all operations will be delegated. + * + *

This method never returns {@code null}.

+ * + * @return the {@link ResultSet} to which all operations will be delegated; never {@code null} + */ + protected final ResultSet delegate() { + return this.delegate; + } + + /** + * Ensures this {@link DelegatingResultSet} is {@linkplain #isClosed() not closed}, if {@linkplain + * #DelegatingResultSet(Connection, ResultSet, boolean, boolean) strict closed checking was enabled at construction + * time}. + * + *

If a subclass overrides the {@link #isClosed()} method, the override must not call this method or undefined + * behavior, such as an infinite loop, may result.

+ * + *

This method is intended for advanced use cases only and almost all users of this class will have no reason to + * call it.

+ * + * @exception SQLException if this {@link DelegatingResultSet} was {@linkplain #DelegatingResultSet(Connection, + * ResultSet, boolean, boolean) created with strict closed checking enabled} and an invocation of the {@link + * #isClosed()} method returns {@code true}, or if some other database access error occurs + */ + protected final void checkOpen() throws SQLException { + this.closedChecker.run(); + } + + // (Invoked by method reference only.) + private void failWhenClosed() throws SQLException { + if (this.isClosed()) { + throw new SQLNonTransientConnectionException("ResultSet is closed", "08000"); + } + } + + /** + * Returns {@code true} if a call to {@link #close()} will actually close this {@link DelegatingResultSet}. + * + *

This method returns {@code true} when {@link #setCloseable(boolean)} has been called with a value of {@code + * true} and the {@link #isClosed()} method returns {@code false}.

+ * + * @return {@code true} if a call to {@link #close()} will actually close this {@link DelegatingResultSet}; {@code + * false} in all other cases + * + * @exception SQLException if {@link #isClosed()} throws a {@link SQLException} + * + * @see #setCloseable(boolean) + * + * @see #close() + * + * @see #isClosed() + */ + public boolean isCloseable() throws SQLException { + // this.checkOpen(); // Deliberately omitted. + return this.closeable && !this.isClosed(); + } + + /** + * Sets the closeable status of this {@link DelegatingResultSet}. + * + *

Note that calling this method with a value of {@code true} does not necessarily mean that the {@link + * #isCloseable()} method will subsequently return {@code true}, since the {@link #isClosed()} method may return + * {@code true}.

+ * + * @param closeable whether or not a call to {@link #close()} will actually close this {@link DelegatingResultSet} + * + * @see #isCloseable() + * + * @see #close() + * + * @see Statement#close() + * + * @see #isClosed() + */ + public void setCloseable(boolean closeable) { + // this.checkOpen(); // Deliberately omitted. + this.closeable = closeable; + } + + @Override + public boolean next() throws SQLException { + checkOpen(); + return this.delegate().next(); + } + + @Override + public void close() throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + this.delegate().close(); + } + + @Override + public boolean wasNull() throws SQLException { + checkOpen(); + return this.delegate().wasNull(); + } + + @Override + public String getString(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getString(columnIndex); + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getBoolean(columnIndex); + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getByte(columnIndex); + } + + @Override + public short getShort(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getShort(columnIndex); + } + + @Override + public int getInt(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getInt(columnIndex); + } + + @Override + public long getLong(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getLong(columnIndex); + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getFloat(columnIndex); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getDouble(columnIndex); + } + + @Deprecated + @Override + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(columnIndex, scale); + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getBytes(columnIndex); + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getDate(columnIndex); + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getTime(columnIndex); + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(columnIndex); + } + + @Override + public InputStream getAsciiStream(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getAsciiStream(columnIndex); + } + + @Deprecated + @Override + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getUnicodeStream(columnIndex); + } + + @Override + public InputStream getBinaryStream(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getBinaryStream(columnIndex); + } + + @Override + public String getString(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getString(columnLabel); + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getBoolean(columnLabel); + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getByte(columnLabel); + } + + @Override + public short getShort(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getShort(columnLabel); + } + + @Override + public int getInt(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getInt(columnLabel); + } + + @Override + public long getLong(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getLong(columnLabel); + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getFloat(columnLabel); + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getDouble(columnLabel); + } + + @Deprecated + @Override + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(columnLabel, scale); + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getBytes(columnLabel); + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getDate(columnLabel); + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getTime(columnLabel); + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(columnLabel); + } + + @Override + public InputStream getAsciiStream(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getAsciiStream(columnLabel); + } + + @Deprecated + @Override + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getUnicodeStream(columnLabel); + } + + @Override + public InputStream getBinaryStream(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getBinaryStream(columnLabel); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + checkOpen(); + return this.delegate().getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + checkOpen(); + this.delegate().clearWarnings(); + } + + @Override + public String getCursorName() throws SQLException { + checkOpen(); + return this.delegate().getCursorName(); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + checkOpen(); + return this.delegate().getMetaData(); + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getObject(columnIndex); + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getObject(columnLabel); + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().findColumn(columnLabel); + } + + @Override + public Reader getCharacterStream(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getCharacterStream(columnIndex); + } + + @Override + public Reader getCharacterStream(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getCharacterStream(columnLabel); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(columnIndex); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getBigDecimal(columnLabel); + } + + @Override + public boolean isBeforeFirst() throws SQLException { + checkOpen(); + return this.delegate().isBeforeFirst(); + } + + @Override + public boolean isAfterLast() throws SQLException { + checkOpen(); + return this.delegate().isAfterLast(); + } + + @Override + public boolean isFirst() throws SQLException { + checkOpen(); + return this.delegate().isFirst(); + } + + @Override + public boolean isLast() throws SQLException { + checkOpen(); + return this.delegate().isLast(); + } + + @Override + public void beforeFirst() throws SQLException { + checkOpen(); + this.delegate().beforeFirst(); + } + + @Override + public void afterLast() throws SQLException { + checkOpen(); + this.delegate().afterLast(); + } + + @Override + public boolean first() throws SQLException { + checkOpen(); + return this.delegate().first(); + } + + @Override + public boolean last() throws SQLException { + checkOpen(); + return this.delegate().last(); + } + + @Override + public int getRow() throws SQLException { + checkOpen(); + return this.delegate().getRow(); + } + + @Override + public boolean absolute(int row) throws SQLException { + checkOpen(); + return this.delegate().absolute(row); + } + + @Override + public boolean relative(int rows) throws SQLException { + checkOpen(); + return this.delegate().relative(rows); + } + + @Override + public boolean previous() throws SQLException { + checkOpen(); + return this.delegate().previous(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + checkOpen(); + this.delegate().setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + checkOpen(); + return this.delegate().getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + checkOpen(); + this.delegate().setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + checkOpen(); + return this.delegate().getFetchSize(); + } + + @Override + public int getType() throws SQLException { + checkOpen(); + return this.delegate().getType(); + } + + @Override + public int getConcurrency() throws SQLException { + checkOpen(); + return this.delegate().getConcurrency(); + } + + @Override + public boolean rowUpdated() throws SQLException { + checkOpen(); + return this.delegate().rowUpdated(); + } + + @Override + public boolean rowInserted() throws SQLException { + checkOpen(); + return this.delegate().rowInserted(); + } + + @Override + public boolean rowDeleted() throws SQLException { + checkOpen(); + return this.delegate().rowDeleted(); + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + checkOpen(); + this.delegate().updateNull(columnIndex); + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + checkOpen(); + this.delegate().updateBoolean(columnIndex, x); + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + checkOpen(); + this.delegate().updateByte(columnIndex, x); + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + checkOpen(); + this.delegate().updateShort(columnIndex, x); + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + checkOpen(); + this.delegate().updateInt(columnIndex, x); + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + checkOpen(); + this.delegate().updateLong(columnIndex, x); + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + checkOpen(); + this.delegate().updateFloat(columnIndex, x); + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + checkOpen(); + this.delegate().updateDouble(columnIndex, x); + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + checkOpen(); + this.delegate().updateBigDecimal(columnIndex, x); + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + checkOpen(); + this.delegate().updateString(columnIndex, x); + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + checkOpen(); + this.delegate().updateBytes(columnIndex, x); + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + checkOpen(); + this.delegate().updateDate(columnIndex, x); + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + checkOpen(); + this.delegate().updateTime(columnIndex, x); + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + checkOpen(); + this.delegate().updateTimestamp(columnIndex, x); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + checkOpen(); + this.delegate().updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnIndex, x, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnIndex, x); + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + checkOpen(); + this.delegate().updateNull(columnLabel); + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + checkOpen(); + this.delegate().updateBoolean(columnLabel, x); + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + checkOpen(); + this.delegate().updateByte(columnLabel, x); + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + checkOpen(); + this.delegate().updateShort(columnLabel, x); + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + checkOpen(); + this.delegate().updateInt(columnLabel, x); + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + checkOpen(); + this.delegate().updateLong(columnLabel, x); + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + checkOpen(); + this.delegate().updateFloat(columnLabel, x); + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + checkOpen(); + this.delegate().updateDouble(columnLabel, x); + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + checkOpen(); + this.delegate().updateBigDecimal(columnLabel, x); + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + checkOpen(); + this.delegate().updateString(columnLabel, x); + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + checkOpen(); + this.delegate().updateBytes(columnLabel, x); + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + checkOpen(); + this.delegate().updateDate(columnLabel, x); + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + checkOpen(); + this.delegate().updateTime(columnLabel, x); + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + checkOpen(); + this.delegate().updateTimestamp(columnLabel, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + checkOpen(); + this.delegate().updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { + checkOpen(); + this.delegate().updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnLabel, x, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnLabel, x); + } + + @Override + public void insertRow() throws SQLException { + checkOpen(); + this.delegate().insertRow(); + } + + @Override + public void updateRow() throws SQLException { + checkOpen(); + this.delegate().updateRow(); + } + + @Override + public void deleteRow() throws SQLException { + checkOpen(); + this.delegate().deleteRow(); + } + + @Override + public void refreshRow() throws SQLException { + checkOpen(); + this.delegate().refreshRow(); + } + + @Override + public void cancelRowUpdates() throws SQLException { + checkOpen(); + this.delegate().cancelRowUpdates(); + } + + @Override + public void moveToInsertRow() throws SQLException { + checkOpen(); + this.delegate().moveToInsertRow(); + } + + @Override + public void moveToCurrentRow() throws SQLException { + checkOpen(); + this.delegate().moveToCurrentRow(); + } + + @Override + public Statement getStatement() throws SQLException { + checkOpen(); + // NOTE + return this.statement; + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + checkOpen(); + return this.delegate().getObject(columnIndex, map); + } + + @Override + public Ref getRef(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getRef(columnIndex); + } + + @Override + public Blob getBlob(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getBlob(columnIndex); + } + + @Override + public Clob getClob(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getClob(columnIndex); + } + + @Override + public Array getArray(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getArray(columnIndex); + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + checkOpen(); + return this.delegate().getObject(columnLabel, map); + } + + @Override + public Ref getRef(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getRef(columnLabel); + } + + @Override + public Blob getBlob(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getBlob(columnLabel); + } + + @Override + public Clob getClob(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getClob(columnLabel); + } + + @Override + public Array getArray(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getArray(columnLabel); + } + + @Override + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getDate(columnIndex, cal); + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getDate(columnLabel, cal); + } + + @Override + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTime(columnIndex, cal); + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTime(columnLabel, cal); + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(columnIndex, cal); + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + checkOpen(); + return this.delegate().getTimestamp(columnLabel, cal); + } + + @Override + public URL getURL(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getURL(columnIndex); + } + + @Override + public URL getURL(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getURL(columnLabel); + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + checkOpen(); + this.delegate().updateRef(columnIndex, x); + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + checkOpen(); + this.delegate().updateRef(columnLabel, x); + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + checkOpen(); + this.delegate().updateBlob(columnIndex, x); + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + checkOpen(); + this.delegate().updateBlob(columnLabel, x); + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + checkOpen(); + this.delegate().updateClob(columnIndex, x); + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + checkOpen(); + this.delegate().updateClob(columnLabel, x); + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + checkOpen(); + this.delegate().updateArray(columnIndex, x); + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + checkOpen(); + this.delegate().updateArray(columnLabel, x); + } + + @Override + public RowId getRowId(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getRowId(columnIndex); + } + + @Override + public RowId getRowId(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getRowId(columnLabel); + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + checkOpen(); + this.delegate().updateRowId(columnIndex, x); + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + checkOpen(); + this.delegate().updateRowId(columnLabel, x); + } + + @Override + public int getHoldability() throws SQLException { + checkOpen(); + return this.delegate().getHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + // this.checkOpen(); // Deliberately omitted per spec. + return this.delegate().isClosed(); + } + + @Override + public void updateNString(int columnIndex, String nString) throws SQLException { + checkOpen(); + this.delegate().updateNString(columnIndex, nString); + } + + @Override + public void updateNString(String columnLabel, String nString) throws SQLException { + checkOpen(); + this.delegate().updateNString(columnLabel, nString); + } + + @Override + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + checkOpen(); + this.delegate().updateNClob(columnIndex, nClob); + } + + @Override + public void updateNClob(String columnLabel, NClob nClob) throws SQLException { + checkOpen(); + this.delegate().updateNClob(columnLabel, nClob); + } + + @Override + public NClob getNClob(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getNClob(columnIndex); + } + + @Override + public NClob getNClob(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getNClob(columnLabel); + } + + @Override + public SQLXML getSQLXML(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getSQLXML(columnIndex); + } + + @Override + public SQLXML getSQLXML(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getSQLXML(columnLabel); + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + checkOpen(); + this.delegate().updateSQLXML(columnIndex, xmlObject); + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + checkOpen(); + this.delegate().updateSQLXML(columnLabel, xmlObject); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getNString(columnIndex); + } + + @Override + public String getNString(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getNString(columnLabel); + } + + @Override + public Reader getNCharacterStream(int columnIndex) throws SQLException { + checkOpen(); + return this.delegate().getNCharacterStream(columnIndex); + } + + @Override + public Reader getNCharacterStream(String columnLabel) throws SQLException { + checkOpen(); + return this.delegate().getNCharacterStream(columnLabel); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + checkOpen(); + this.delegate().updateNCharacterStream(columnIndex, x, length); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().updateNCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + checkOpen(); + this.delegate().updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + checkOpen(); + this.delegate().updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + checkOpen(); + this.delegate().updateBlob(columnIndex, inputStream, length); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + checkOpen(); + this.delegate().updateBlob(columnLabel, inputStream, length); + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().updateClob(columnIndex, reader, length); + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().updateClob(columnLabel, reader, length); + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().updateNClob(columnIndex, reader, length); + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + checkOpen(); + this.delegate().updateNClob(columnLabel, reader, length); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + checkOpen(); + this.delegate().updateNCharacterStream(columnIndex, x); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + checkOpen(); + this.delegate().updateNCharacterStream(columnLabel, reader); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + checkOpen(); + this.delegate().updateAsciiStream(columnIndex, x); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + checkOpen(); + this.delegate().updateBinaryStream(columnIndex, x); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + checkOpen(); + this.delegate().updateCharacterStream(columnIndex, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + checkOpen(); + this.delegate().updateAsciiStream(columnLabel, x); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + checkOpen(); + this.delegate().updateBinaryStream(columnLabel, x); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + checkOpen(); + this.delegate().updateCharacterStream(columnLabel, reader); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + checkOpen(); + this.delegate().updateBlob(columnIndex, inputStream); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + checkOpen(); + this.delegate().updateBlob(columnLabel, inputStream); + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + checkOpen(); + this.delegate().updateClob(columnIndex, reader); + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + checkOpen(); + this.delegate().updateClob(columnLabel, reader); + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + checkOpen(); + this.delegate().updateNClob(columnIndex, reader); + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + checkOpen(); + this.delegate().updateNClob(columnLabel, reader); + } + + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + checkOpen(); + return this.delegate().getObject(columnIndex, type); + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + checkOpen(); + return this.delegate().getObject(columnLabel, type); + } + + @Override + public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnLabel, x, targetSqlType, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnIndex, x, targetSqlType); + } + + @Override + public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException { + checkOpen(); + this.delegate().updateObject(columnLabel, x, targetSqlType); + } + + @Override + public T unwrap(Class iface) throws SQLException { + // checkOpen(); // Deliberately omitted per spec. + return iface.isInstance(this) ? iface.cast(this) : this.delegate().unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + // checkOpen(); // Deliberately omitted per spec. + return iface.isInstance(this) || this.delegate().isWrapperFor(iface); + } + + + /* + * Static methods. + */ + + + // (Invoked by method reference only.) + private static void doNothing() { + + } + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingStatement.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingStatement.java new file mode 100644 index 00000000000..44640b3228f --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/DelegatingStatement.java @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLNonTransientConnectionException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.Objects; + +/** + * A JDBC + * 4.3-compliant {@link Statement} that delegates to another JDBC 4.3-compliant {@link Statement}. + * + * @param the type of the {@link Statement} subclass + */ +public class DelegatingStatement implements Statement { + + private final Connection connection; + + private final S delegate; + + private final SQLRunnable closedChecker; + + private volatile boolean closeable; + + /** + * Creates a new {@link DelegatingStatement}. + * + * @param connection the {@link Connection} that created this {@link DelegatingStatement}; must not be {@code null} + * + * @param delegate the {@link Statement} instance to which all operations will be delegated; must not be {@code + * null} + * + * @param closeable the initial value for this {@link DelegatingStatement}'s {@linkplain #isCloseable() closeable} + * status + * + * @param strictClosedChecking if {@code true}, then this {@link DelegatingStatement}'s {@link #isClosed()} + * method will be invoked before every operation that cannot take place on a closed statement, and, if it returns + * {@code true}, the operation in question will fail with a {@link SQLException} + * + * @exception NullPointerException if either {@code connection} or {@code delegate} is {@code null} + * + * @see #getConnection() + */ + public DelegatingStatement(Connection connection, + S delegate, + boolean closeable, + boolean strictClosedChecking) { + super(); + this.connection = Objects.requireNonNull(connection, "connection"); + this.delegate = Objects.requireNonNull(delegate, "delegate"); + this.closeable = closeable; + this.closedChecker = strictClosedChecking ? this::failWhenClosed : DelegatingStatement::doNothing; + } + + /** + * Returns the {@link Statement} to which all operations will be delegated. + * + *

This method never returns {@code null}.

+ * + * @return the {@link Statement} to which all operations will be delegated; never {@code null} + */ + protected final S delegate() { + return this.delegate; + } + + /** + * Returns {@code true} if a call to {@link #close()} will actually close this {@link DelegatingStatement}. + * + *

This method returns {@code true} when {@link #setCloseable(boolean)} has been called with a value of {@code + * true} and the {@link #isClosed()} method returns {@code false}.

+ * + * @return {@code true} if a call to {@link #close()} will actually close this {@link DelegatingStatement}; {@code + * false} in all other cases + * + * @exception SQLException if {@link #isClosed()} throws a {@link SQLException} + * + * @see #setCloseable(boolean) + * + * @see #close() + * + * @see #isClosed() + */ + public boolean isCloseable() throws SQLException { + return this.closeable && !this.isClosed(); + } + + /** + * Sets the closeable status of this {@link DelegatingStatement}. + * + *

Note that calling this method with a value of {@code true} does not necessarily mean that the {@link + * #isCloseable()} method will subsequently return {@code true}, since the {@link #isClosed()} method may return + * {@code true}.

+ * + * @param closeable whether or not a call to {@link #close()} will actually close this {@link DelegatingStatement} + * + * @see #isCloseable() + * + * @see #close() + * + * @see Statement#close() + * + * @see #isClosed() + */ + public void setCloseable(boolean closeable) { + // this.checkOpen(); // Deliberately omitted. + this.closeable = closeable; + } + + @Override + public boolean isClosed() throws SQLException { + return this.delegate().isClosed(); + } + + /** + * Overrides the {@link Statement#close()} method so that when it is invoked this {@link DelegatingStatement} is + * {@linkplain Statement#close() closed} only if it {@linkplain #isCloseable() is closeable}. + * + *

Overrides should normally call {@code super.close()} as part of their implementation.

+ * + * @exception SQLException if an error occurs + * + * @see #isCloseable() + */ + @Override + public void close() throws SQLException { + // NOTE + if (this.isCloseable()) { + this.delegate().close(); + } + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + checkOpen(); + return + new DelegatingResultSet(this, // NOTE + this.delegate().executeQuery(sql), + true, + true); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + checkOpen(); + return this.delegate().executeUpdate(sql); + } + + @Override + public int getMaxFieldSize() throws SQLException { + checkOpen(); + return this.delegate().getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + checkOpen(); + this.delegate().setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + checkOpen(); + return this.delegate().getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + checkOpen(); + this.delegate().setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + checkOpen(); + this.delegate().setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + checkOpen(); + return this.delegate().getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + checkOpen(); + this.delegate().setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + checkOpen(); + this.delegate().cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + checkOpen(); + return this.delegate().getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + checkOpen(); + this.delegate().clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + checkOpen(); + this.delegate().setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + checkOpen(); + return this.delegate().execute(sql); + } + + @Override + public ResultSet getResultSet() throws SQLException { + checkOpen(); + return + new DelegatingResultSet(this, // NOTE + this.delegate().getResultSet(), + true, + true); + } + + @Override + public int getUpdateCount() throws SQLException { + checkOpen(); + return this.delegate().getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + checkOpen(); + return this.delegate().getMoreResults(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + checkOpen(); + this.delegate().setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + checkOpen(); + return this.delegate().getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + checkOpen(); + this.delegate().setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + checkOpen(); + return this.delegate().getFetchSize(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + checkOpen(); + return this.delegate().getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + checkOpen(); + return this.delegate().getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + checkOpen(); + this.delegate().addBatch(sql); + } + + @Override + public void clearBatch() throws SQLException { + checkOpen(); + this.delegate().clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + checkOpen(); + return this.delegate().executeBatch(); + } + + /** + * Returns the {@link Connection} {@linkplain #DelegatingStatement(Connection, Statement, boolean, boolean) supplied + * at construction time}. + * + * @return the {@link Connection} {@linkplain #DelegatingStatement(Connection, Statement, boolean, boolean) supplied + * at construction time}; never {@code null} + * + * @exception SQLException not thrown by the default implementation of this method + * + * @see #DelegatingStatement(Connection, Statement, boolean, boolean) + */ + @Override + public Connection getConnection() throws SQLException { + checkOpen(); + // NOTE + return this.connection; + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + checkOpen(); + return this.delegate().getMoreResults(current); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + checkOpen(); + return + new DelegatingResultSet(this, // NOTE + this.delegate().getGeneratedKeys(), + true, + true); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + checkOpen(); + return this.delegate().executeUpdate(sql, autoGeneratedKeys); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + checkOpen(); + return this.delegate().executeUpdate(sql, columnIndexes); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + checkOpen(); + return this.delegate().executeUpdate(sql, columnNames); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + checkOpen(); + return this.delegate().execute(sql, autoGeneratedKeys); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + checkOpen(); + return this.delegate().execute(sql, columnIndexes); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + checkOpen(); + return this.delegate().execute(sql, columnNames); + } + + @Override + public int getResultSetHoldability() throws SQLException { + checkOpen(); + return this.delegate().getResultSetHoldability(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + checkOpen(); + this.delegate().setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + checkOpen(); + return this.delegate().isPoolable(); + } + + @Override + public void closeOnCompletion() throws SQLException { + checkOpen(); + this.delegate().closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + checkOpen(); + return this.delegate().isCloseOnCompletion(); + } + + @Override + public long getLargeUpdateCount() throws SQLException { + checkOpen(); + return this.delegate().getLargeUpdateCount(); + } + + @Override + public void setLargeMaxRows(long max) throws SQLException { + checkOpen(); + this.delegate().setLargeMaxRows(max); + } + + @Override + public long getLargeMaxRows() throws SQLException { + checkOpen(); + return this.delegate().getLargeMaxRows(); + } + + @Override + public long[] executeLargeBatch() throws SQLException { + checkOpen(); + return this.delegate().executeLargeBatch(); + } + + @Override + public long executeLargeUpdate(String sql) throws SQLException { + checkOpen(); + return this.delegate().executeLargeUpdate(sql); + } + + @Override + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + checkOpen(); + return this.delegate().executeLargeUpdate(sql, autoGeneratedKeys); + } + + @Override + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + checkOpen(); + return this.delegate().executeLargeUpdate(sql, columnIndexes); + } + + @Override + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + checkOpen(); + return this.delegate().executeLargeUpdate(sql, columnNames); + } + + @Override + public String enquoteLiteral(String val) throws SQLException { + checkOpen(); + return this.delegate().enquoteLiteral(val); + } + + @Override + public String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { + checkOpen(); + return this.delegate().enquoteIdentifier(identifier, alwaysQuote); + } + + @Override + public boolean isSimpleIdentifier(String identifier) throws SQLException { + checkOpen(); + return this.delegate().isSimpleIdentifier(identifier); + } + + @Override + public String enquoteNCharLiteral(String val) throws SQLException { + checkOpen(); + return this.delegate().enquoteNCharLiteral(val); + } + + @Override + public T unwrap(Class iface) throws SQLException { + // checkOpen(); // Deliberately omitted + return iface.isInstance(this) ? iface.cast(this) : this.delegate().unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + // checkOpen(); // Deliberately omitted + return iface.isInstance(this) || this.delegate().isWrapperFor(iface); + } + + /** + * Ensures this {@link DelegatingStatement} is {@linkplain #isClosed() not closed}, if {@linkplain + * #DelegatingStatement(Connection, Statement, boolean, boolean) strict closed checking was enabled at construction + * time}. + * + *

If a subclass overrides the {@link #isClosed()} method, the override must not call this method or undefined + * behavior, such as an infinite loop, may result.

+ * + *

This method is intended for advanced use cases only and almost all users of this class will have no reason to + * call it.

+ * + * @exception SQLException if this {@link DelegatingStatement} was {@linkplain #DelegatingStatement(Connection, + * Statement, boolean, boolean) created with strict closed checking enabled} and an invocation of the {@link + * #isClosed()} method returns {@code true}, or if some other database access error occurs + */ + protected final void checkOpen() throws SQLException { + this.closedChecker.run(); + } + + // (Invoked by method reference only.) + private void failWhenClosed() throws SQLException { + if (this.isClosed()) { + throw new SQLNonTransientConnectionException("Statement is closed", "08000"); + } + } + + + /* + * Static methods. + */ + + + // (Invoked by method reference only.) + private static void doNothing() { + + } + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/SQLBooleanSupplier.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/SQLBooleanSupplier.java new file mode 100644 index 00000000000..de59b6841f6 --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/SQLBooleanSupplier.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.sql.SQLException; + +/** + * A useful functional interface whose implementations can perform work that may throw a {@link SQLException}. + * + * @see #getAsBoolean() + */ +@FunctionalInterface +public interface SQLBooleanSupplier { + + /** + * Performs work and returns the result. + * + * @return a {@code boolean} + * + * @exception SQLException if a database access error occurs + */ + boolean getAsBoolean() throws SQLException; + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/SQLRunnable.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/SQLRunnable.java new file mode 100644 index 00000000000..e336339c1d7 --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/SQLRunnable.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.sql.SQLException; + +/** + * A useful functional interface whose implementations can perform work that may throw a {@link SQLException}. + * + * @see #run() + */ +@FunctionalInterface +public interface SQLRunnable { + + /** + * Performs work. + * + * @exception SQLException if a database access error occurs + */ + void run() throws SQLException; + +} diff --git a/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/UncheckedSQLException.java b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/UncheckedSQLException.java new file mode 100644 index 00000000000..07ff7e30760 --- /dev/null +++ b/integrations/jdbc/jdbc/src/main/java/io/helidon/integrations/jdbc/UncheckedSQLException.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.sql.SQLException; + +/** + * A {@link RuntimeException} that wraps a {@link SQLException}. + */ +public final class UncheckedSQLException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new {@link UncheckedSQLException}. + * + * @param cause the {@link SQLException} that this {@link UncheckedSQLException} will represent; may be {@code null} + */ + public UncheckedSQLException(SQLException cause) { + super(cause); + } + + /** + * Returns the {@link SQLException} this {@link UncheckedSQLException} represents. + * + *

This method may return {@code null}.

+ * + * @return the {@link SQLException} this {@link UncheckedSQLException} represents, or {@code null} + */ + @Override + public SQLException getCause() { + return (SQLException) super.getCause(); + } + +} diff --git a/integrations/jdbc/jdbc/src/main/java/module-info.java b/integrations/jdbc/jdbc/src/main/java/module-info.java index 922b2013542..7d056c0cad6 100644 --- a/integrations/jdbc/jdbc/src/main/java/module-info.java +++ b/integrations/jdbc/jdbc/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,9 @@ */ module io.helidon.integrations.jdbc { + // Tests use H2 which requires resolution of java.naming at compile time only. + requires static java.naming; + requires transitive java.sql; exports io.helidon.integrations.jdbc; diff --git a/integrations/jdbc/jdbc/src/test/java/io/helidon/integrations/jdbc/TestConditionallyCloseableConnection.java b/integrations/jdbc/jdbc/src/test/java/io/helidon/integrations/jdbc/TestConditionallyCloseableConnection.java new file mode 100644 index 00000000000..a469e723b3f --- /dev/null +++ b/integrations/jdbc/jdbc/src/test/java/io/helidon/integrations/jdbc/TestConditionallyCloseableConnection.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * 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. + */ +package io.helidon.integrations.jdbc; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.DataSource; + +import org.h2.jdbcx.JdbcDataSource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.is; + +final class TestConditionallyCloseableConnection { + + private DataSource ds; + + private TestConditionallyCloseableConnection() { + super(); + } + + @BeforeEach + final void initializeDataSource() throws SQLException { + final JdbcDataSource ds = new JdbcDataSource(); + ds.setURL("jdbc:h2:mem:test"); + ds.setUser("sa"); + ds.setPassword("sa"); + this.ds = ds; + } + + @SuppressWarnings("try") + @Test + final void testLegacyIsCloseable() throws SQLException { + try (@SuppressWarnings("deprecation") + final ConditionallyCloseableConnection c = new ConditionallyCloseableConnection(this.ds.getConnection()); + final Statement s = c.createStatement(); + final ResultSet rs = s.executeQuery("SHOW TABLES")) { + assertThat(rs.next(), is(false)); // no tables + + // ConditionallyCloseableConnections are closeable by default. + assertThat(c.isCloseable(), is(true)); + + c.setCloseable(false); + assertThat(c.isCloseable(), is(false)); + + // Closing a ConditionallyCloseableConnection when + // isCloseable() returns false does not actually close the delegate. + c.close(); + assertThat(c.delegate().isClosed(), is(false)); + + // IN BACKWARDS COMPATIBILITY MODE ONLY, it also does not + // make the ConditionallyCloseableConnection itself look + // like it is closed. + assertThat(c.isClosed(), is(false)); + + // Make sure we recorded that a close() attempt happened. + assertThat(c.isClosePending(), is(true)); + + // We can reset the closeable status. + c.setCloseable(true); + assertThat(c.isCloseable(), is(true)); + assertThat(c.isClosed(), is(false)); + + // This also wipes out the fact that a close was + // previously attempted. + assertThat(c.isClosePending(), is(false)); + + // Closing a ConditionallyCloseableConnection when + // isCloseable() returns true actually irrevocably closes + // the connection. + c.close(); // closes for real + assertThat(c.isClosed(), is(true)); + assertThat(c.isClosePending(), is(false)); + + // Still closed. + c.setCloseable(false); // won't matter + assertThat(c.isClosed(), is(true)); + assertThat(c.isClosePending(), is(false)); + + // Note that the JDBC specification says that closing a + // connection will release "this Connection object's + // database and JDBC resources immediately" + // (https://docs.oracle.com/en/java/javase/19/docs/api/java.sql/java/sql/Connection.html#close()). + // However it is unclear whether a Statement constitutes + // either a "database" or a "JDBC resource" in this + // context. You would think it would, but H2 does not + // close open Statements or ResultSets when their creating + // Connection is closed, and neither does PostgreSQL. + assertThat(s.isClosed(), is(false)); + assertThat(rs.isClosed(), is(false)); + } + } + + @SuppressWarnings("try") + @Test + final void testIsCloseable() throws SQLException { + try (final ConditionallyCloseableConnection c = new ConditionallyCloseableConnection(this.ds.getConnection(), true, true); + final Statement s = c.createStatement(); + final ResultSet rs = s.executeQuery("SHOW TABLES")) { + assertThat(rs.next(), is(false)); // no tables + + // ConditionallyCloseableConnections are closeable by default. + assertThat(c.isCloseable(), is(true)); + + c.setCloseable(false); + assertThat(c.isCloseable(), is(false)); + + // Closing a ConditionallyCloseableConnection when + // isCloseable() returns false does not actually close the delegate. + c.close(); + assertThat(c.delegate().isClosed(), is(false)); + + // It does make the ConditionallyCloseableConnection + // itself look like it is closed, however. + assertThat(c.isClosed(), is(true)); + + // Make sure we recorded that a close() attempt happened. + assertThat(c.isClosePending(), is(true)); + + // We can reset the closeable status. + c.setCloseable(true); + assertThat(c.isCloseable(), is(true)); + assertThat(c.isClosed(), is(false)); + + // This also wipes out the fact that a close was + // previously attempted. + assertThat(c.isClosePending(), is(false)); + + // Closing a ConditionallyCloseableConnection when + // isCloseable() returns true actually irrevocably closes + // the connection. + c.close(); // closes for real + assertThat(c.isClosed(), is(true)); + assertThat(c.isClosePending(), is(false)); + + // Still closed. + c.setCloseable(false); // won't matter + assertThat(c.isClosed(), is(true)); + assertThat(c.isClosePending(), is(false)); + + // Note that the JDBC specification says that closing a + // connection will release "this Connection object's + // database and JDBC resources immediately" + // (https://docs.oracle.com/en/java/javase/19/docs/api/java.sql/java/sql/Connection.html#close()). + // However it is unclear whether a Statement constitutes + // either a "database" or a "JDBC resource" in this + // context. You would think it would, but H2 does not + // close open Statements or ResultSets when their creating + // Connection is closed, and neither does PostgreSQL. + assertThat(s.isClosed(), is(false)); + assertThat(rs.isClosed(), is(false)); + } + } + +}