/**
 * 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$
 * --------------------------------------------------------------------------
 */

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

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

/**
 * 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(Long id, 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(Object proxy, Method method, Object[] args) throws 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
        return method.invoke(wrappedConnection, args);

    }

    /**
     * @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.
 */
class ConnectionClassUtils {

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

}