PreparedStatementWrapper separated to two files; changed state from refcnt+monitor to enum
This commit is contained in:
parent
50c03c9f63
commit
a3909e2a5c
@ -28,6 +28,7 @@ xomrk {
|
||||
optInContracts()
|
||||
explicitApi()
|
||||
|
||||
jvmToolchain(21)
|
||||
jvm {
|
||||
withJava()
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<State> _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<Object> 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> T unwrap(Class<T> 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();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user