
/**	
 * This class is used by the MobyServlet framework
 * to test a deployed service, and register it with MOBY Central.
 * This is the simplest Java MOBY client you can have.  A fully documented version
 * of a client is available in the TestRequest class, with all the proper error checking.
 */

package org.biomoby.service.test;

import org.biomoby.client.*;
import org.biomoby.registry.meta.Registry;
import org.biomoby.service.*;
import org.biomoby.shared.*;
import org.biomoby.shared.data.*;

import org.w3c.dom.*;

import javax.xml.parsers.*;
import java.net.URL;
import java.util.*;
import java.io.*;

public class ServletTester{
    private String alternateWebXML;

    /**
     * Runs a test on the service described in WEB-INF/web.xml
     */
    public ServletTester(){
	this(null);
    }

    /**
     * Mainly for JUnit tests, specify a web.xml file to be used for configuring the test run,
     * rather than using the default WEB-INF/web.xml in the class path.
     */
    public ServletTester(String altWebXML){
	alternateWebXML = altWebXML;
    }

    public static void main(String[] args) throws Exception{

        if(args.length != 2 && args.length != 3 || 
	   args.length == 3 && !("register".equals(args[2]) || "register_permanent".equals(args[2]))){
	    System.err.println("Usage: java -jar FooService.war " +
			       "<fully-qualified servlet url> <exampleMobyData.xml (register|register_permanent) | unregister>");
	    throw new Exception("Incorrect command arguments");
        }
	if(args[0].indexOf("http://localhost") == 0 || args[0].indexOf("http://127.0.0.1/") == 0){
	    System.err.println("The servlet URL must be fully qualified " + 
			       "(\"localhost\" or \"127.0.0.1\" are not acceptable)");
	    throw new Exception("Servlet URL not fully qualified");
	}

	ServletTester tester = new ServletTester();
	tester.runService(args);
    }

    public void runService(String[] args) throws Exception{
        // Open the web.xml file to determine the service name and inputs/outputs.
	String webXMLPath = alternateWebXML != null ? alternateWebXML : "WEB-INF/web.xml";
	java.net.URL webXmlURL = getClass().getClassLoader().getResource(webXMLPath);
	if(webXmlURL == null){
	    System.err.println("Could not find \""+webXMLPath+"\", aborting!");
	    throw new Exception("No \""+ webXMLPath+"\" in the resource path");
	}
	
        String centralURL = null;
        String testServiceName = null;
        String testServiceClassName = null;
	String inParams = null;
	String outParams = null;

	// Parse the web.xml config to set the servlet environment	
	TestServletConfig servletConfig = new TestServletConfig(new TestServletContext(), 
								webXmlURL);
        MobyServlet servlet = null;
        try{
	    // This line can throw many different exception if you didn't get the class right! 
	    Class servletClass = MobyServlet.class.getClassLoader().loadClass(servletConfig.getServletClassName());
            if(servletClass == null){
                throw new ClassNotFoundException("The servlet Class to run (" + 
						 servletConfig.getServletClassName() + 
						 ") was not found, please ensure the web.xml is up-to-date.");
            }
	    servlet = (MobyServlet) servletClass.newInstance();
        } catch(Exception e){
            System.err.println("The servlet Class was not specified properly in the web.xml file:");
            e.printStackTrace();
	    throw new Exception("Invalid web.xml, the servlet-class tag was not useable");
        }

	servlet.init(servletConfig);
	TestHttpServletRequest request = new TestHttpServletRequest();
        request.setRequestURL(args[0]);

        MobyService testService = servlet.createServiceFromConfig(request);
	// When we GET this URL, the RDF is returned, populated with all the pertinent data for registration
	if(args.length != 3 || !("register_permanent".equals(args[2]))){
	    testService.setSignatureURL(null); //signature not set == MOBY agent, please delete this service if requested!
	}
	// Otherwise we keep the signature URL that servlet.createServiceFromConfig() generated

	// Setup communication with MOBY Central
        Central worker = servlet.getCentralImpl();
	if(centralURL == null){  // shouldn't happen, but just in case...
	    worker = new CentralImpl();  //use default server
	}

	if("unregister".equals(args[1])){
	    unregisterService(testService, worker);
	    return;
	}

	// Make sure the example data provided is okay, if the service takes any input at all.
	MobyContentInstance testData = null;
	MobyData[] inputTypes = testService.getPrimaryInputs();
	if(inputTypes.length > 0){
	    InputStream dataIn = null;
	    try{
		dataIn = new FileInputStream(args[1]);
	    } catch(Exception e){
		System.err.println("The specified resource " + args[1] + " is not a file, trying as a URL");
	    }
	    if(dataIn == null){
		try{
		    dataIn = (new URL(args[1])).openStream();
		} catch(Exception e){
		    //e.printStackTrace();
		}
	    }
	    // Last ditch, try as a resource
	    if(dataIn == null){
		ClassLoader cl = getClass().getClassLoader();
		if(cl == null){
		    cl = ClassLoader.getSystemClassLoader();
		}
		System.err.println("Trying to load as resource with " + cl);
		URL resourceURL = null;
		if(cl != null){
		    resourceURL = cl.getResource(args[1]);
		}
		if(resourceURL == null){
		    System.err.println("Could not find the example data named \"" + args[1] + "\", aborting!");
		    throw new Exception("Example data was not found: " + args[1]);
		}
		try{
		    dataIn = resourceURL.openStream();
		} catch(Exception e){
		    e.printStackTrace();
		}
	    }
	    
	    // Create the data and service instances.
	    Registry registry = new Registry("any_reg_synonym", 
					     worker.getRegistryEndpoint(),
					     worker.getRegistryNamespace());
	    testData = MobyDataUtils.fromXMLDocument(dataIn, registry);
	}
	else{
	    // Create a blank request if the input is void
	    testData = new MobyContentInstance();
	    testData.put(new MobyDataJob());
	}

	// Check the example data against the input spec
	Iterator<String> jobIDs = testData.keySet().iterator();
	while(jobIDs.hasNext()){
	    MobyDataJob job = testData.get(jobIDs.next());
	    MobyServlet.validateArguments(job, testService, "Input value to be sent to the service");
	}

	MobyRequest mr = new MobyRequest(worker);
	//System.err.println("test service desc: " + testService);
	mr.setService(testService);
	mr.setInput(testData);
        long startTime = System.currentTimeMillis();
	MobyContentInstance resultData = mr.invokeService();
        long endTime = System.currentTimeMillis();
	System.out.println(resultData.toString());

	// Check output types, using the same method that checks input types
	MobyService testServiceOut = new MobyService(testServiceName);
	testServiceOut.setInputs(testService.getPrimaryOutputs());
	jobIDs = resultData.keySet().iterator();
	while(jobIDs.hasNext()){
	    MobyDataJob job = resultData.get(jobIDs.next());
	    MobyServlet.validateArguments(job, testServiceOut, "Returned value from service");
	}

        System.out.println("Time to execute service: " + (endTime-startTime) + " ms");

	if(args.length != 3){
	    return;
	}
	else{
	    registerService(testService, worker);
	}

    }

    public static void registerService(MobyService service, Central central) throws Exception{
	// Tell MOBY Central about the service (it serves its own RDF on GET requests)
	//System.err.println(((CentralImpl) central).getRegisterServiceXML(service));

	// Throws an exception if it fails to register
	central.registerService(service);

	// TO DO: check RDF discrepancies?
	System.err.println("Service Successfully Registered!");
    }
    
    public static void unregisterService(MobyService service, Central central) throws Exception{
	// Tell MOBY Central about the service (it serves its own RDF on GET requests)
	//System.err.println(((CentralImpl) central).getRegisterServiceXML(service));

	// Throws an exception if it fails to register
	central.unregisterService(service);

	// TO DO: check RDF discrepancies?
	System.err.println("Service Successfully Unregistered!");
    }
}
