/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2009 France Telecom R&D
 * Contact: jonas-team@ow2.org
 *
 * 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id: ConnectionProxy.java 21566 2011-08-08 12:28:12Z cazauxj $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.jndi.interceptors.impl.datasource;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Invocation handler that will monitor the calls to the stop() method.
 * @author Florent Benoit
 */
public class ConnectionProxy implements InvocationHandler {

    /**
     * Close method.
     */
    private static final Method CLOSE_METHOD = ConnectionClassUtils.getCloseMethod();

    /**
     * Id of this connection.
     */
    private Long id = null;

    /**
     * Link to the datasource wrapper.
     */
    private DatasourceWrapper datasourceWrapper = null;

    /**
     * Link to the wrapped connection.
     */
    private Connection wrappedConnection = null;

    /**
     * Stack trace elements when connection was obtained.
     */
    private StackTraceElement[] stackTraceElements = null;

    /**
     * Constructor of the handler of the proxy.
     * @param id the given id
     * @param datasourceWrapper the wrapped datasource
     * @param wrappedConnection the wrapped connection
     * @param stackTraceElements the stack trace
     */
    public ConnectionProxy(final Long id, final DatasourceWrapper datasourceWrapper, final Connection wrappedConnection,
            final StackTraceElement[] stackTraceElements) {
        this.id = id;
        this.datasourceWrapper = datasourceWrapper;
        this.wrappedConnection = wrappedConnection;
        this.stackTraceElements = stackTraceElements;
    }

    /**
     * Intercept methods called on the connection object.
     * @param proxy the proxy instance that the method was invoked on
     * @param method the <code>Method</code> 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
     *        <code>null</code> 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.
     */
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws SQLException, Throwable {

        // A close method is called, can remove this connection as an opened
        // connection
        if (CLOSE_METHOD.equals(method)) {
            datasourceWrapper.remove(id);
        }

        // finally call the method on the object
        try {
            return method.invoke(wrappedConnection, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

    /**
     * @return id of this object.
     */
    protected Long getId() {
        return id;
    }

    /**
     * @return stack trace elements.
     */
    protected StackTraceElement[] getStackTraceElements() {
        return stackTraceElements;
    }

    /**
     * @return wrapped connection.
     */
    protected Connection getWrappedConnection() {
        return wrappedConnection;
    }
}

/**
 * Helper class in order to cache close() method.
 */
final class ConnectionClassUtils {

    /**
     * Utility class.
     */
    private ConnectionClassUtils() {

    }

    /**
     * @return the close() method
     */
    public static Method getCloseMethod() {
        try {
            return Connection.class.getMethod("close");
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Cannot get close method on connection", e);
        }
    }

}
