From a3909e2a5cae0d01cf3079fed34ce558fd62d8e0 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Wed, 26 Mar 2025 19:52:01 +0300 Subject: [PATCH] PreparedStatementWrapper separated to two files; changed state from refcnt+monitor to enum --- build.gradle.kts | 1 + ...aredStatementsCompilationPhantomCache.java | 2 +- ... _PreparedStatementWrapper_Delegates.java} | 670 ++++++++++-------- .../_PreparedStatementWrapper_Logic.java | 229 ++++++ 4 files changed, 608 insertions(+), 294 deletions(-) rename src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/{_PreparedStatementWrapper.java => _PreparedStatementWrapper_Delegates.java} (57%) create mode 100644 src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Logic.java diff --git a/build.gradle.kts b/build.gradle.kts index 12b5163..fb71a0d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,6 +28,7 @@ xomrk { optInContracts() explicitApi() + jvmToolchain(21) jvm { withJava() diff --git a/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementsCompilationPhantomCache.java b/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementsCompilationPhantomCache.java index 0c37f00..28a7301 100644 --- a/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementsCompilationPhantomCache.java +++ b/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/PreparedStatementsCompilationPhantomCache.java @@ -98,7 +98,7 @@ public class PreparedStatementsCompilationPhantomCache /*implements ReadOnlyProp } } - private static class BoundPreparedStatementWrapper extends _PreparedStatementWrapper { + private static class BoundPreparedStatementWrapper extends _PreparedStatementWrapper_Delegates { @NotNull private final ConnectionsMapNode _node; diff --git a/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper.java b/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Delegates.java similarity index 57% rename from src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper.java rename to src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Delegates.java index b9e1ef5..767a162 100644 --- a/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper.java +++ b/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Delegates.java @@ -1,8 +1,6 @@ package ru.langrafhomyak.db.jdbc_resources_manager; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import ru.landrafhomyak.utility.reference_counter.CloseableReferenceCounter; import java.io.InputStream; import java.io.Reader; @@ -23,810 +21,896 @@ import java.sql.RowId; import java.sql.SQLException; 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; -/* package */ abstract class _PreparedStatementWrapper implements PreparedStatement { - @NotNull - protected final PreparedStatement _orig; - @NotNull - private final CloseableReferenceCounter _refcnt; - @NotNull - private final Object _sync; - private ResultSet _currentQuery = null; - private boolean _closeOnCompletion = false; +/* package */ abstract class _PreparedStatementWrapper_Delegates extends _PreparedStatementWrapper_Logic { - _PreparedStatementWrapper(@NotNull PreparedStatement orig) { - this._orig = orig; - this._refcnt = new CloseableReferenceCounter("This prepared statement was returned to pool to be used in future"); - this._sync = new Object(); + protected _PreparedStatementWrapper_Delegates(@NotNull PreparedStatement orig) { + super(orig); } - protected abstract void _onClose() throws SQLException; - - @Override - public void close() throws SQLException { - synchronized (this._sync) { - this._refcnt.close("Can't close prepared statement while it's in use"); - this._onClose(); - } - } - - @Override - public boolean isClosed() { - return this._refcnt.isClosed(); - } - - @Override - public void closeOnCompletion() { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); - this._closeOnCompletion = true; - } - } - - @Override - public boolean isCloseOnCompletion() { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); - return this._closeOnCompletion; - } - } - - private class _BoundResultSetWrapper extends ResultSetWrapper { - - public _BoundResultSetWrapper(@NotNull ResultSet orig) { - super(orig); - } - - @Override - public void close() throws SQLException { - if (this._orig.isClosed()) - return; - this._orig.close(); - _PreparedStatementWrapper.this._currentQuery = null; - if (_PreparedStatementWrapper.this._closeOnCompletion) - _PreparedStatementWrapper.this.close(); - } - - @Override - public Statement getStatement() { - return _PreparedStatementWrapper.this; - } - - } - - @NotNull - private ResultSet _prepareRS(@Nullable ResultSet orig) { - Objects.requireNonNull(orig, "Underlying PreparedStatement didn't returned ResultSet"); - ResultSet wrapped = this.new _BoundResultSetWrapper(orig); - this._currentQuery = wrapped; - return wrapped; - } - - @Override - public ResultSet executeQuery() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); - return this._prepareRS(this._orig.executeQuery()); - } - } - - @Override - public ResultSet getResultSet() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); - return this._prepareRS(this._orig.getResultSet()); - } - } - - @Override - public ResultSet getGeneratedKeys() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); - return this._prepareRS(this._orig.getGeneratedKeys()); - } - } - - - @Override - public T unwrap(java.lang.Class iface) throws SQLException { - return iface.isInstance(this) ? iface.cast(this) : this._orig.unwrap(iface); - } - - @Override - public boolean isWrapperFor(java.lang.Class iface) throws SQLException { - return iface.isInstance(this) || this._orig.isWrapperFor(iface); - } - - - @Override - public void setPoolable(boolean value) throws SQLException { - throw new SQLException("Disabling pooling not allowed on prepared statements driven by PreparedStatementsCompilationCache"); - - } - - @Override - public boolean isPoolable() { - return true; - } - - @Override public void cancel() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.cancel(); + } finally { + this._state.exitMethod(); } } @Override public int executeUpdate() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.executeUpdate(); + } finally { + this._state.exitMethod(); } } @Override public void setNull(int parameterIndex, int sqlType) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNull(parameterIndex, sqlType); + } finally { + this._state.exitMethod(); } } @Override public void setBoolean(int parameterIndex, boolean x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBoolean(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setByte(int parameterIndex, byte x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setByte(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setShort(int parameterIndex, short x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setShort(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setInt(int parameterIndex, int x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setInt(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setLong(int parameterIndex, long x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setLong(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setFloat(int parameterIndex, float x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setFloat(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setDouble(int parameterIndex, double x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setDouble(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBigDecimal(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setString(int parameterIndex, String x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setString(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setBytes(int parameterIndex, byte[] x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBytes(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setDate(int parameterIndex, Date x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setDate(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setTime(int parameterIndex, Time x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setTime(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setTimestamp(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setAsciiStream(parameterIndex, x, length); + } finally { + this._state.exitMethod(); } } @Override @SuppressWarnings({"deprecation"}) public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setUnicodeStream(parameterIndex, x, length); + } finally { + this._state.exitMethod(); } } @Override public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBinaryStream(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void clearParameters() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.clearParameters(); + } finally { + this._state.exitMethod(); } } @Override public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setObject(parameterIndex, x, targetSqlType); + } finally { + this._state.exitMethod(); } } @Override public void setObject(int parameterIndex, Object x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setObject(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public boolean execute() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.execute(); + } finally { + this._state.exitMethod(); } } @Override public void addBatch() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.addBatch(); + } finally { + this._state.exitMethod(); } } @Override public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setCharacterStream(parameterIndex, reader, length); + } finally { + this._state.exitMethod(); } } @Override public void setRef(int parameterIndex, Ref x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setRef(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setBlob(int parameterIndex, Blob x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBlob(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setClob(int parameterIndex, Clob x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setClob(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setArray(int parameterIndex, Array x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setArray(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public ResultSetMetaData getMetaData() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getMetaData(); + } finally { + this._state.exitMethod(); } } @Override public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setDate(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setTime(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setTimestamp(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNull(parameterIndex, sqlType, typeName); + } finally { + this._state.exitMethod(); } } @Override public void setURL(int parameterIndex, URL x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setURL(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public ParameterMetaData getParameterMetaData() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getParameterMetaData(); + } finally { + this._state.exitMethod(); } } @Override public void setRowId(int parameterIndex, RowId x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setRowId(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setNString(int parameterIndex, String value) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNString(parameterIndex, value); + } finally { + this._state.exitMethod(); } } @Override public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNCharacterStream(parameterIndex, value, length); + } finally { + this._state.exitMethod(); } } @Override public void setNClob(int parameterIndex, NClob value) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNClob(parameterIndex, value); + } finally { + this._state.exitMethod(); } } @Override public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setClob(parameterIndex, reader, length); + } finally { + this._state.exitMethod(); } } @Override public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBlob(parameterIndex, inputStream, length); + } finally { + this._state.exitMethod(); } } @Override public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNClob(parameterIndex, reader, length); + } finally { + this._state.exitMethod(); } } @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setSQLXML(parameterIndex, xmlObject); + } finally { + this._state.exitMethod(); } } @Override public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } finally { + this._state.exitMethod(); } } @Override public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setAsciiStream(parameterIndex, x, length); + } finally { + this._state.exitMethod(); } } @Override public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBinaryStream(parameterIndex, x, length); + } finally { + this._state.exitMethod(); } } @Override public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setCharacterStream(parameterIndex, reader, length); + } finally { + this._state.exitMethod(); } } @Override public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setAsciiStream(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBinaryStream(parameterIndex, x); + } finally { + this._state.exitMethod(); } } @Override public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setCharacterStream(parameterIndex, reader); + } finally { + this._state.exitMethod(); } } @Override public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNCharacterStream(parameterIndex, value); + } finally { + this._state.exitMethod(); } } @Override public void setClob(int parameterIndex, Reader reader) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setClob(parameterIndex, reader); + } finally { + this._state.exitMethod(); } } @Override public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setBlob(parameterIndex, inputStream); + } finally { + this._state.exitMethod(); } } @Override public void setNClob(int parameterIndex, Reader reader) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setNClob(parameterIndex, reader); + } finally { + this._state.exitMethod(); } } @Override public ResultSet executeQuery(String sql) throws SQLException { - throw new SQLException("executeQuery(String) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeQuery(String) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public int executeUpdate(String sql) throws SQLException { - throw new SQLException("executeUpdate(String) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public int getMaxFieldSize() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getMaxFieldSize(); + } finally { + this._state.exitMethod(); } } @Override public void setMaxFieldSize(int max) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setMaxFieldSize(max); + } finally { + this._state.exitMethod(); } } @Override public int getMaxRows() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getMaxRows(); + } finally { + this._state.exitMethod(); } } @Override public void setMaxRows(int max) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setMaxRows(max); + } finally { + this._state.exitMethod(); } } @Override public void setEscapeProcessing(boolean enable) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setEscapeProcessing(enable); + } finally { + this._state.exitMethod(); } } @Override public int getQueryTimeout() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getQueryTimeout(); + } finally { + this._state.exitMethod(); } } @Override public void setQueryTimeout(int seconds) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setQueryTimeout(seconds); + } finally { + this._state.exitMethod(); } } @Override public SQLWarning getWarnings() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getWarnings(); + } finally { + this._state.exitMethod(); } } @Override public void clearWarnings() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.clearWarnings(); + } finally { + this._state.exitMethod(); } } @Override public void setCursorName(String name) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setCursorName(name); + } finally { + this._state.exitMethod(); } } @Override public boolean execute(String sql) throws SQLException { - throw new SQLException("execute(String) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("execute(String) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public int getUpdateCount() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getUpdateCount(); + } finally { + this._state.exitMethod(); } } @Override public boolean getMoreResults() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getMoreResults(); + } finally { + this._state.exitMethod(); } } @Override public void setFetchDirection(int direction) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setFetchDirection(direction); + } finally { + this._state.exitMethod(); } } @Override public int getFetchDirection() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getFetchDirection(); + } finally { + this._state.exitMethod(); } } @Override public void setFetchSize(int rows) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.setFetchSize(rows); + } finally { + this._state.exitMethod(); } } @Override public int getFetchSize() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getFetchSize(); + } finally { + this._state.exitMethod(); } } @Override public int getResultSetConcurrency() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getResultSetConcurrency(); + } finally { + this._state.exitMethod(); } } @Override public int getResultSetType() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getResultSetType(); + } finally { + this._state.exitMethod(); } } @Override public void addBatch(String sql) throws SQLException { - throw new SQLException("addBatch(String) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("addBatch(String) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public void clearBatch() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { this._orig.clearBatch(); + } finally { + this._state.exitMethod(); } } @Override public int[] executeBatch() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.executeBatch(); + } finally { + this._state.exitMethod(); } } @Override public Connection getConnection() throws SQLException { - return this._orig.getConnection(); + this._state.enterMethod(); + try { + return this._orig.getConnection(); + } finally { + this._state.exitMethod(); + } } @Override public boolean getMoreResults(int current) throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getMoreResults(current); + } finally { + this._state.exitMethod(); } } @Override public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { - throw new SQLException("executeUpdate(String, int) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String, int) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { - throw new SQLException("executeUpdate(String, int[]) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String, int[]) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public int executeUpdate(String sql, String[] columnIndexes) throws SQLException { - throw new SQLException("executeUpdate(String, String[]) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String, String[]) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { - throw new SQLException("executeUpdate(String, int) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String, int) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public boolean execute(String sql, int[] columnIndexes) throws SQLException { - throw new SQLException("executeUpdate(String, int[]) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String, int[]) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public boolean execute(String sql, String[] columnIndexes) throws SQLException { - throw new SQLException("executeUpdate(String, String[]) not allowed on PreparedStatement"); + this._state.enterMethod(); + try { + throw new SQLException("executeUpdate(String, String[]) not allowed on PreparedStatement"); + } finally { + this._state.exitMethod(); + } } @Override public int getResultSetHoldability() throws SQLException { - synchronized (this._sync) { - this._refcnt.assertNotClosed(); + this._state.enterMethod(); + try { return this._orig.getResultSetHoldability(); + } finally { + this._state.exitMethod(); } } } diff --git a/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Logic.java b/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Logic.java new file mode 100644 index 0000000..2cf9c33 --- /dev/null +++ b/src/jvmMain/java/ru/langrafhomyak/db/jdbc_resources_manager/_PreparedStatementWrapper_Logic.java @@ -0,0 +1,229 @@ +package ru.langrafhomyak.db.jdbc_resources_manager; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Objects.requireNonNull; + +/* package */ abstract class _PreparedStatementWrapper_Logic implements PreparedStatement { + private enum State { + OPEN, IN_USE, CLOSED + } + + protected static class StateVar { + private final AtomicReference _value; + + StateVar(State initial) { + this._value = new AtomicReference<>(initial); + } + + private void _throwState(@NotNull State v) { + if (v == State.OPEN) return; + if (v == State.CLOSED) + throw new IllegalStateException("This PreparedStatement was returned to pool to cache compilation for future uses"); + if (v == State.IN_USE) this._throwConcurrent(); + } + + void enterMethod() { + this._throwState(this._compareAndExchange(State.OPEN, State.IN_USE)); + } + + void exitMethod() { + this._value.set(State.OPEN); + } + + void close() { + this._throwState(this._compareAndExchange(State.OPEN, State.CLOSED)); + } + + boolean isClosed() { + return this._value.get() == State.CLOSED; + } + + private void _throwConcurrent() { + throw new IllegalStateException("You are trying concurrently call method on PreparedStatement while it is not designed to be threadsafe"); + } + + @SuppressWarnings("SameParameterValue") + private State _compareAndExchange(State expected, State newValue) { + if (_isNativeCompareAndExchangeExists) { + return this._value.compareAndExchange(expected, newValue); + } else { + while (true) { + final State old = this._value.get(); + if (old != expected) return old; + if (this._value.compareAndSet(expected, newValue)) return old; + } + } + } + + private static final boolean _isNativeCompareAndExchangeExists; + + static { + final AtomicReference a = new AtomicReference<>(new Object()); + boolean f; + try { + a.compareAndExchange(a, a); + f = true; + } catch (NoSuchMethodError e) { + f = false; + } + _isNativeCompareAndExchangeExists = f; + } + } + + + @NotNull + protected final PreparedStatement _orig; + @NotNull + protected final StateVar _state; + // private ResultSet _currentQuery = null; + private boolean _closeOnCompletion = false; + + _PreparedStatementWrapper_Logic(@NotNull PreparedStatement orig) { + this._orig = orig; + this._state = new StateVar(State.OPEN); + } + + protected abstract void _onClose() throws SQLException; + + @Override + public void close() throws SQLException { + this._state.close(); + this._onClose(); + } + + @Override + public boolean isClosed() { + return this._state.isClosed(); + } + + @Override + public void closeOnCompletion() { + this._state.enterMethod(); + try { + this._closeOnCompletion = true; + } finally { + this._state.exitMethod(); + } + } + + @Override + public boolean isCloseOnCompletion() { + this._state.enterMethod(); + try { + return this._closeOnCompletion; + } finally { + this._state.exitMethod(); + } + } + + private class _BoundResultSetWrapper extends ResultSetWrapper { + + public _BoundResultSetWrapper(@NotNull ResultSet orig) { + super(orig); + } + + @Override + public void close() throws SQLException { + if (this._orig.isClosed()) + return; + this._orig.close(); + // _PreparedStatementWrapper_Logic.this._currentQuery = null; + if (_PreparedStatementWrapper_Logic.this._closeOnCompletion) + _PreparedStatementWrapper_Logic.this.close(); + } + + @Override + public Statement getStatement() { + return _PreparedStatementWrapper_Logic.this; + } + + } + + @NotNull + private ResultSet _prepareRS(@Nullable ResultSet orig) { + requireNonNull(orig, "Underlying PreparedStatement didn't returned ResultSet"); + ResultSet wrapped = this.new _BoundResultSetWrapper(orig); + // this._currentQuery = wrapped; + return wrapped; + } + + @Override + public ResultSet executeQuery() throws SQLException { + this._state.enterMethod(); + try { + return this._prepareRS(this._orig.executeQuery()); + } finally { + this._state.exitMethod(); + } + } + + @Override + public ResultSet getResultSet() throws SQLException { + this._state.enterMethod(); + try { + return this._prepareRS(this._orig.getResultSet()); + } finally { + this._state.exitMethod(); + } + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + this._state.enterMethod(); + try { + return this._prepareRS(this._orig.getGeneratedKeys()); + } finally { + this._state.exitMethod(); + } + } + + + @Override + public T unwrap(Class iface) throws SQLException { + this._state.enterMethod(); + try { + return iface.isInstance(this) ? iface.cast(this) : this._orig.unwrap(iface); + } finally { + this._state.exitMethod(); + } + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + this._state.enterMethod(); + try { + return iface.isInstance(this) || this._orig.isWrapperFor(iface); + } finally { + this._state.exitMethod(); + } + } + + + @Override + public void setPoolable(boolean value) throws SQLException { + this._state.enterMethod(); + try { + throw new SQLException("Disabling pooling not allowed on prepared statements driven by PreparedStatementsCompilationCache"); + } finally { + this._state.exitMethod(); + } + } + + @Override + public boolean isPoolable() { + this._state.enterMethod(); + try { + return true; + } finally { + this._state.exitMethod(); + } + } +}