/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * 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 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$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.ws.axis;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;

import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;


/**
 * <code>JServiceProxy</code>
 *
 * @author Guillaume Sauthier
 */
public class JServiceProxy implements InvocationHandler {

    /**
     * inner JService implementation.
     * used for delegation
     */
    private JService service = null;

    /**
     * Forbidden method : Service.getTypeMappingRegistry()
     */
    private static Method getTypeMappingRegistryMethod = null;

    /**
     * Forbidden method : Service.getHandlerRegistry()
     */
    private static Method getHandlerRegistryMethod = null;

    /**
     * adding behavior to method : Service.getPort(QName, Class)
     */
    private static Method getPortQNameClass = null;

    /**
     * Constructs a new JServiceProxy wrapping given JService instance.
     * @param service the wrapped JService instance
     * @throws ServiceException should be never thrown
     */
    public JServiceProxy(JService service) throws ServiceException {
        this.service = service;
        try {
            getTypeMappingRegistryMethod = Service.class.getDeclaredMethod("getTypeMappingRegistry", new Class[]{});
            getHandlerRegistryMethod = Service.class.getDeclaredMethod("getHandlerRegistry", new Class[]{});
            getPortQNameClass = Service.class.getDeclaredMethod("getPort", new Class[]{QName.class, Class.class});
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    /**
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // avoid getHandlerRegistry method call
        if (getHandlerRegistryMethod.equals(method)) {
            throw new UnsupportedOperationException("J2EE components shouldn't use getHandlerRegistry method");
        }
        // avoid getTypeMappingRegistry method call
        if (getTypeMappingRegistryMethod.equals(method)) {
            throw new UnsupportedOperationException("J2EE components shouldn't use getTypeMappingRegistry method");
        }
        // avoid getPort method call
        if (getPortQNameClass.equals(method)) {
            return getPort(args);
        }
        //delegate
        try {
            return method.invoke(service, args);
        } catch (InvocationTargetException ite) {
            throw ite.getTargetException();
        }
    }

    /**
     * @param args Method call arguments
     * @return Returns the correct Port
     * @throws ServiceException if port's QName is an unknown Port (not defined in WSDL).
     */
    private Object getPort(Object[] args) throws ServiceException {
        QName name = (QName) args[0];
        Class<?> clazz = (Class<?>) args[1];
        boolean portFound = false;
        for (Iterator ports = service.getPorts(); ports.hasNext() && !portFound;) {
            QName portName = (QName) ports.next();
            if (portName.equals(name)) {
                return service.getPort(name, clazz);
            }
        }

        // if we come here, no ports have been found,
        // so we fail with a ServiceException
        throw new ServiceException("Unknown Port : " + name);
    }

}
