// ExtendedProtocolClient.java
//
// Created: February 2006
//
// This file is a component of the BioMoby project.
// Copyright Martin Senger (martin.senger@gmail.com).
//

package org.biomoby.client;

import org.biomoby.shared.MobyException;
import org.biomoby.shared.MobyService;

import org.tulsoft.shared.GException;
import org.tulsoft.tools.loaders.ICreator;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;


/**
 * A client that can (additionally to the normal features) communicate
 * with an implementation of a Biomoby service using a non-biomoby
 * protocol. It loads an implementation class (taken from a {@link
 * ExtendedServiceLocator}) into the same JVM. It is useful for
 * testing a service implemenation even before it is deployed as a web
 * service. <p>
 *
 * @author <A HREF="mailto:martin.senger@gmail.com">Martin Senger</A>
 * @version $Id: ExtendedProtocolClient.java,v 1.3 2006/02/20 05:51:09 senger Exp $
 */

public abstract class ExtendedProtocolClient
    extends BaseClient {

//    private static org.apache.commons.logging.Log log =
//       org.apache.commons.logging.LogFactory.getLog (ExtendedProtocolClient.class);


    /**************************************************************************
     * Call either a local class implementing a service, or pass it to
     * the superclass that does a real (usual) SOAP call to a service
     * (or first to a registry and then to a service). <p>
     *
     * The local class is used if its name was given on the
     * command-line. This is meant to be used to test services before
     * they are deployed in a servlet environment (such as Apache/Axis). <p>
     *
     * @see BaseClient#callRemoteService
     *
     * @throws MobyException if (a) a local class cannot be
     * instantiated, or if (b) a local class returned an unexpected
     * result type (should return only String or array of bytes), or
     * if a (c) super-class call to remote service failed
     *************************************************************************/
    public String callRemoteService (String xmlInput)
	throws MobyException {
	
	MobyServiceLocator locator = getServiceLocator();
	boolean asBytes = locator.isAsBytes();
	if (locator instanceof ExtendedServiceLocator) {
	    ExtendedServiceLocator betterLocator =
		(ExtendedServiceLocator)locator;
	    if (betterLocator.isLoop())
		return xmlInput;
	    String localClass = betterLocator.getLocalClass();
	    if (localClass != null)
		return callLocalService (localClass, xmlInput, asBytes);
	}
	return super.callRemoteService (xmlInput);
    }

    /**************************************************************************
     *
     *************************************************************************/
    protected String callLocalService (String localClassName,
				       String xmlInput,
				       boolean asBytes)
	throws MobyException {

	MobyServiceLocator locator = getServiceLocator();
	MobyService mobyService = locator.getService();
	if (mobyService == null)
	    throw new MobyException ("Service was not given.");

	Class localClass = null;
	try {
	    localClass = Class.forName (localClassName);
	} catch (ClassNotFoundException e) {
	    throw new MobyException ("Class '" + localClassName + "' was not found.", e);
	}

	String methodName = mobyService.getName();
	Method method = null;
	Object service = null;
	try {
	    service = ICreator.createInstance (localClass);
	    method = localClass.getMethod (methodName,
					   new Class[] { Object.class });
	    return filterMobyResponseType
		(method.invoke (service, new Object[] { sendingFilter (xmlInput, asBytes) }));

	} catch (NoSuchMethodException e) {
	    StringBuffer buf = new StringBuffer();
	    buf.append ("Method '");
	    buf.append (methodName);
	    buf.append ("' was not found");
	    if (service != null) {
		buf.append (" in the object ");
		buf.append (service.getClass().getName());
		buf.append (".\nThe object has only following public methods:\n");
		Method[] methods = service.getClass().getMethods();
		for (int i = 0; i < methods.length; i++) {
		    buf.append ("\t");
		    buf.append (methods[i]);
		    buf.append ("\n");
		}
	    }
	    throw new MobyException (new String (buf));
	    
	} catch (GException e) {
	    throw new MobyException (e.getMessage());
	} catch (IllegalAccessException e) {
	    throw new MobyException ("IllegalAccessException: " + e.getMessage());
	} catch (IllegalArgumentException e) {
	    throw new MobyException ("IllegalArgumentException: " + e.getMessage());
	} catch (InvocationTargetException e) {
	    throw new MobyException ("InvocationTargetException: " +
				     e.getTargetException().toString());
	}
    }

}
