All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.easybeans.component.jdbcpool.JStatement Maven / Gradle / Ivy

There is a newer version: 3.0.0-M1
Show newest version
/**
 * EasyBeans
 * Copyright (C) 2006 Bull S.A.S.
 * Contact: [email protected]
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: JStatement.java 2240 2008-01-23 18:39:04Z benoitf $
 * --------------------------------------------------------------------------
 */

package org.ow2.easybeans.component.jdbcpool;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * Wrapper on a PreparedStatement. This wrapper is used to track close method in
 * order to avoid closing the statement, and putting it instead in a pool.
 * @author Philippe Durieux
 * @author Florent Benoit
 */
public class JStatement extends AbsProxy {

    /**
     * Properties of this statement has been changed ? Needs to be be cleared
     * when reused.
     */
    private boolean changed = false;

    /**
     * Is that this statement is opened ?
     */
    private boolean opened = false;

    /**
     * Being closed. (in close method).
     */
    private boolean closing = false;

    /**
     * Physical PreparedStatement object on which the wrapper is.
     */
    private PreparedStatement ps;

    /**
     * Managed Connection the Statement belongs to.
     */
    private JManagedConnection mc;

    /**
     * Hashcode computed in constructor.
     */
    private int hashCode;

    /**
     * SQL used as statement.
     */
    private String sql;

    /**
     * Logger.
     */
    private Log logger = LogFactory.getLog(JStatement.class);

    /**
     * Builds a new statement with the given wrapped statement of given
     * connection and given sql query.
     * @param ps the prepared statement.
     * @param mc managed connection
     * @param sql query.
     */
    public JStatement(final PreparedStatement ps, final JManagedConnection mc, final String sql) {
        this.ps = ps;
        this.mc = mc;
        this.sql = sql;
        hashCode = sql.hashCode();
        opened = true;
    }

    /**
     * @return Sql query used.
     */
    public String getSql() {
        return sql;
    }

    /**
     * Gets the preparedstatement used by this wrapper.
     * @return the internal prepared statement
     */
    protected PreparedStatement getInternalPreparedStatement() {
        return this.ps;
    }

    /**
     * @return hashcode of the object
     */
    @Override
    public int hashCode() {
        return hashCode;
    }

    /**
     * @param stmt given statement for comparing it
     * @return true if given object is equals to this current object
     */
    @Override
    public boolean equals(final Object stmt) {
        if (stmt == null) {
            return false;
        }
        // different hashcode, cannot be equals
        if (this.hashCode != stmt.hashCode()) {
            return false;
        }

        // if got same hashcode, try to see if cast is ok.
        if (!(stmt instanceof JStatement)) {
            logger.warn("Bad class {0}", stmt);
            return false;
        }

        // Cast object
        JStatement psw = (JStatement) stmt;
        if (sql == null && psw.getSql() != null) {
            return false;
        }
        if (sql != null && !sql.equals(psw.getSql())) {
            return false;
        }
        try {
            if (psw.getInternalPreparedStatement().getResultSetType() != ps.getResultSetType()) {
                return false;
            }
            if (psw.getInternalPreparedStatement().getResultSetConcurrency() != ps.getResultSetConcurrency()) {
                return false;
            }
        } catch (SQLException e) {
            logger.warn("Cannot compare statements", e);
            return false;
        }
        logger.debug("Found");
        return true;
    }

    /**
     * Processes a method invocation on a proxy instance and returns the result.
     * This method will be invoked on an invocation handler when a method is
     * invoked on a proxy instance that it is associated with.
     * @param proxy the proxy instance that the method was invoked on
     * @param method the Method instance corresponding to the
     *        interface method invoked on the proxy instance.
     * @param args an array of objects containing the values of the arguments
     *        passed in the method invocation on the proxy instance, or
     *        null if interface method takes no arguments.
     * @return the value to return from the method invocation on the proxy
     *         instance.
     * @throws Throwable the exception to throw from the method invocation on
     *         the proxy instance. The exception's type must be assignable
     *         either to any of the exception types declared in the
     *         throws clause of the interface method or to the
     *         unchecked exception types java.lang.RuntimeException
     *         or java.lang.Error. If a checked exception is
     *         thrown by this method that is not assignable to any of the
     *         exception types declared in the throws clause of
     *         the interface method, then an UndeclaredThrowableException
     *         containing the exception that was thrown by this method will be
     *         thrown by the method invocation on the proxy instance.
     */
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {

        // Methods on the Object.class are not send on the connection impl
        if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
            return handleObjectMethods(method, args);
        }

        String methodName = method.getName();

        // Methods that are part of the IPreparedStatement interface
        if ("forceClose".equals(methodName)) {
            return Boolean.valueOf(forceClose());
        } else if ("reuse".equals(methodName)) {
            reuse();
            return null;
        } else if ("isClosed".equals(methodName)) {
            return Boolean.valueOf(isClosed());
        } else if ("forget".equals(methodName)) {
            forget();
            return null;
        } else if ("close".equals(methodName)) {
            close();
            return null;
        }

        // If there are some properties changes on the prepared statement, flag them to
        // avoid to loose time when resetting it
        if ("addBatch".equals(methodName) || "execute".equals(methodName) || "executeUpdate".equals(methodName)
                || "setFetchDirection".equals(methodName) || "setFetchSize".equals(methodName)
                || "setMaxFieldSize".equals(methodName) || "setMaxRows".equals(methodName)
                || "setQueryTimeout".equals(methodName)) {
            changed = true;
        }

        // Delegate to the prepared statement object
        try {
            return method.invoke(this.ps, args);
        } catch (InvocationTargetException e) {
            // Throw the inner exception
            Throwable targetException = e.getTargetException();
            throw targetException;
        }
    }

    /**
     * Force a close on the Prepare Statement. Usually, it's the caller that did
     * not close it explicitly
     * @return true if it was open
     */
    public boolean forceClose() {
        if (opened) {
            logger.debug("Statements should be closed explicitly.");
            opened = false;
            return true;
        }
        return false;
    }

    /**
     * Reuses this statement so reset properties.
     * @throws SQLException if reset fails
     */
    public void reuse() throws SQLException {
        ps.clearParameters();
        ps.clearWarnings();
        opened = true;
        if (changed) {
            logger.debug("Properties statement have been changed, reset default properties");
            ps.clearBatch();
            ps.setFetchDirection(ResultSet.FETCH_FORWARD);
            ps.setMaxFieldSize(0);
            ps.setMaxRows(0);
            ps.setQueryTimeout(0);
            changed = false;
        }
    }

    /**
     * @return true if this statement has been closed, else false.
     */
    public boolean isClosed() {
        return !opened && !closing;
    }

    /**
     * Physically close this Statement.
     * @throws SQLException
     */
    public void forget() {
        try {
            ps.close();
        } catch (SQLException e) {
            logger.error("Cannot close the PreparedStatement", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void close() throws SQLException {
        if (!opened) {
            logger.debug("Statement already closed");
            return;
        }
        opened = false;
        closing = true;
        mc.notifyPsClose(this);
        closing = false;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy