org.firebirdsql.pool.PooledPreparedStatementHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaybird Show documentation
Show all versions of jaybird Show documentation
JDBC Driver for the Firebird RDBMS
/*
* Firebird Open Source J2ee connector - jdbc driver
*
* Distributable under LGPL license.
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGPL License for more details.
*
* This file was created by members of the firebird development team.
* All individual contributions remain the Copyright (C) of those
* individuals. Contributors to this file are either listed here or
* can be obtained from a CVS history command.
*
* All rights reserved.
*/
package org.firebirdsql.pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.xalan.lib.sql.XConnection;
import org.firebirdsql.jdbc.FBSQLException;
/**
* Wrapper for prepared statements. This invocation handler notifies statement
* manager about closing prepared statement.
*
* @author Roman Rokytskyy
*/
public class PooledPreparedStatementHandler implements InvocationHandler {
/**
* Flag whether check is performed in tolerant mode or not. In tolerant
* mode {@link SQLException} is thrown, otherwise
* {@link IllegalStateException}.
*/
private static final boolean TOLERANT_CHECK_MODE = false;
public static Method findMethod(Class clazz, String name, Class[] params) {
return PooledConnectionHandler.findMethod(clazz, name, params);
}
private final static Method PREPARED_STATEMENT_CLOSE = findMethod(
PreparedStatement.class, "close", new Class[0]);
private final static Method PREPARED_STATEMENT_GET_CONNECTION = findMethod(
PreparedStatement.class, "getConnection", new Class[0]);
private final static Method PREPARED_STATEMENT_FORCE_CLOSE = findMethod(
XCachablePreparedStatement.class, "forceClose", new Class[0]);
private final static Method PREPARED_STATEMENT_SET_CONNECTION = findMethod(
XCachablePreparedStatement.class, "setConnection", new Class[] {
Connection.class});
private final static Method PREPARED_STATEMENT_GET_ORIGINAL = findMethod(
XCachablePreparedStatement.class, "getOriginal", new Class[0]);
private final static Method PREPARED_STATEMENT_IS_CACHED = findMethod(
XCachablePreparedStatement.class, "isCached", new Class[0]);
private String statement;
private PreparedStatement preparedStatement;
private XStatementManager owner;
private Connection associatedConnection;
private boolean cached;
private boolean invalid;
private ObjectCloseTraceException invalidateStackTrace;
/**
* Create instance of this class.
*
* @param statement SQL statement to which prepared statement corresponds
* to.
*
* @param preparedStatement prepared statement corresponding to statement.
*
* @param owner instance of {@link XConnection} that created prepared
* statement.
*/
PooledPreparedStatementHandler(String statement, PreparedStatement preparedStatement,
XStatementManager owner, boolean cached)
{
this.statement = statement;
this.preparedStatement = preparedStatement;
this.owner = owner;
this.invalid = false;
this.cached = cached;
}
/**
* Handle {@link Statement#close()} call.
*
* @param statement SQL statement corresponding to the wrapped prepared
* statement.
*
* @param proxy dynamic proxy wrapping prepared statement.
*
* @throws SQLException if something went wrong.
*/
protected void handleStatementClose(String statement, Object proxy)
throws SQLException
{
if (invalid)
throw new SQLException("Statement is already closed.");
owner.statementClosed(statement, proxy);
}
protected void handleForceClose() throws SQLException {
preparedStatement.close();
associatedConnection = null;
invalid = true;
invalidateStackTrace = new ObjectCloseTraceException();
}
protected boolean handleIsCached() throws SQLException {
return cached;
}
/**
* Invoke method on a specified proxy. Here we check if method
* is a method {@link PreparedStatement#close()}. If yes, we notify owner
* that this prepared statement was closed.
*
* @param proxy proxy on which method is invoked.
* @param method instance of {@link Method} describing method being invoked.
* @param args array with arguments.
*
* @return result of method invokation.
*
* @throws Throwable if invoked method threw an exception.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
checkCorrectness(method);
try {
if (method.equals(PREPARED_STATEMENT_CLOSE)) {
handleStatementClose(statement, proxy);
return Void.TYPE;
} else
if (method.equals(PREPARED_STATEMENT_GET_CONNECTION)) {
return associatedConnection;
} else
if (method.equals(PREPARED_STATEMENT_SET_CONNECTION)) {
this.associatedConnection = (Connection)args[0];
return Void.TYPE;
} else
if (method.equals(PREPARED_STATEMENT_GET_ORIGINAL)) {
return preparedStatement;
} else
if (method.equals(PREPARED_STATEMENT_FORCE_CLOSE)) {
handleForceClose();
return Void.TYPE;
} else
if (method.equals(PREPARED_STATEMENT_IS_CACHED)) {
return new Boolean(handleIsCached());
} else {
Object result = method.invoke(preparedStatement, args);
if (result instanceof ResultSet) {
ResultSetHandler handler = new ResultSetHandler(
(PreparedStatement) proxy, (ResultSet) result);
result = Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] { ResultSet.class}, handler);
}
return result;
}
} catch(InvocationTargetException ex) {
throw ex.getTargetException();
}
}
/**
* Check if method call is correct. If method was declared in
* {@link PreparedStatement} class and there is no connection proxy
* associated with this statement (i.e. statement in in pool), exception is
* thrown (this situation usually happens if somebody tries to call method
* on prepared statement after it has been closed).
*
* @param method method to check.
*
* @throws SQLException if statement has incorrect statement.
*/
protected void checkCorrectness(Method method) throws SQLException {
String message = "Statemenet has illegal state because it "+
"does not have any associated connection at this time. " +
"Usually this means that Statement.getConnection() " +
"method was called on a closed statement that currently " +
"lives in a statement pool. ";
if (invalid)
message += "See the attached exception " +
"for the information where the object was closed.";
boolean incorrectState =
(associatedConnection == null &&
method.getDeclaringClass().equals(PreparedStatement.class)) ||
invalid;
if (incorrectState)
if (TOLERANT_CHECK_MODE) {
FBSQLException ex = new FBSQLException(message);
if (invalid)
ex.setNextException(invalidateStackTrace);
throw ex;
} else
throw new IllegalStateException(message);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy