org.firebirdsql.ds.StatementHandler 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 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 source control history command.
*
* All rights reserved.
*/
package org.firebirdsql.ds;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.sql.Statement;
import org.firebirdsql.jdbc.FBSQLException;
import org.firebirdsql.jdbc.FirebirdStatement;
import org.firebirdsql.jdbc.SQLStateConstants;
import static org.firebirdsql.util.ReflectionHelper.*;
/**
* InvocationHandler for statements.
*
* Using an InvocationHandler together with a Proxy removes the need to create
* wrappers for every individual JDBC version.
*
*
* @author Mark Rotteveel
* @since 2.2
*/
class StatementHandler implements InvocationHandler {
private final PooledConnectionHandler owner;
private volatile Statement stmt;
private volatile Statement proxy;
/**
* Constructor for StatementHandler.
*
* @param owner
* The PooledConnectionHandler which owns the Statement
* @param stmt
* Statement to proxy
*/
StatementHandler(PooledConnectionHandler owner, Statement stmt) {
this.owner = owner;
this.stmt = stmt;
proxy = (Statement) Proxy.newProxyInstance(getClass().getClassLoader(),
getAllInterfaces(stmt.getClass()), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Methods from object
if (method.equals(TO_STRING)) {
return "Proxy for " + stmt;
}
if (method.equals(EQUALS)) {
// Using parameter proxy (and not field) on purpose as field is
// nulled after closing
return proxy == args[0];
}
if (method.equals(HASH_CODE)) {
// Using parameter proxy (and not field) on purpose as field is
// nulled after closing
return System.identityHashCode(proxy);
}
// Other methods from object
if (method.getDeclaringClass().equals(Object.class)) {
try {
return method.invoke(stmt, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
// Methods of statement and subinterfaces
if (method.equals(STATEMENT_IS_CLOSED) || method.equals(FIREBIRDSTATEMENT_IS_CLOSED)) {
return isClosed();
}
if (isClosed() && !method.equals(STATEMENT_CLOSE)) {
throw new FBSQLException("Statement already closed", SQLStateConstants.SQL_STATE_INVALID_STATEMENT_ID);
}
try {
if (method.equals(STATEMENT_CLOSE)) {
if (!isClosed()) {
handleClose();
}
return null;
}
if (method.equals(GET_CONNECTION)) {
// Ensure we do not leak the physical connection by returning the proxy
return owner.getProxy();
}
// All other methods
return method.invoke(stmt, args);
} catch (InvocationTargetException ite) {
Throwable inner = ite.getTargetException();
if (inner instanceof SQLException) {
owner.statementErrorOccurred(this, (SQLException) inner);
}
throw inner;
} catch (SQLException se) {
owner.statementErrorOccurred(this, se);
throw se;
}
}
/**
* @return Proxy for the Statement object
*/
protected Statement getProxy() {
return proxy;
}
/**
* Handle {@link Statement#close()} method. This method closes the wrapped
* Statement and notifies the owner it can forget the statement.
*
* @throws SQLException
* If closing the statement threw an Exception
*/
private void handleClose() throws SQLException {
if (isClosed()) {
return;
}
try {
stmt.close();
} finally {
owner.forgetStatement(this);
stmt = null;
proxy = null;
}
}
/**
* Closes this StatementHandler
*/
protected void close() throws SQLException {
handleClose();
}
/**
* @return true
when this handler is closed
*/
public boolean isClosed() {
return proxy == null || stmt == null;
}
// Statement methods
private final static Method STATEMENT_IS_CLOSED = findMethod(Statement.class, "isClosed",
new Class[0]);
private final static Method FIREBIRDSTATEMENT_IS_CLOSED = findMethod(FirebirdStatement.class, "isClosed",
new Class[0]);
private final static Method STATEMENT_CLOSE = findMethod(Statement.class, "close", new Class[0]);
private final static Method GET_CONNECTION = findMethod(Statement.class, "getConnection", new Class[0]);
// Object Methods
private final static Method TO_STRING = findMethod(Object.class, "toString", new Class[0]);
private final static Method EQUALS = findMethod(Object.class, "equals",
new Class[] { Object.class });
private final static Method HASH_CODE = findMethod(Object.class, "hashCode", new Class[0]);
}