// MobyCmdLineClient.java
//    A command-line client of BioMoby Central registry.
//
//    senger@ebi.ac.uk
//    February 2003
//

import org.biomoby.shared.MobyResourceRef;
import org.biomoby.shared.MobyNamespace;
import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.MobyPrimaryDataSimple;
import org.biomoby.shared.MobyService;
import org.biomoby.shared.MobyServiceType;
import org.biomoby.shared.MobySecondaryData;
import org.biomoby.shared.MobyPrimaryDataSet;
import org.biomoby.shared.NoSuccessException;
import org.biomoby.shared.PendingCurationException;
import org.biomoby.shared.Central;
import org.biomoby.client.CmdLineHelper;
import org.biomoby.client.CentralImpl;

import org.tulsoft.tools.BaseCmdLine;
import org.tulsoft.shared.FileUtils;
import org.tulsoft.shared.StringUtils;
import org.tulsoft.shared.GException;

import org.biomoby.registry.meta.RegistriesList;
import org.biomoby.registry.meta.Registries;
import org.biomoby.registry.meta.Registry;

import java.util.*;
import java.io.*;

/**
 * A fully-featured command-line client allowing to access a Moby
 * registry and invoke there most of Moby functions, such as retrieval
 * of various information on registered services, registering and
 * removing services etc.
 *<p>
 *
 * The client does not access Moby Central registry directly but
 * through the class {@link org.biomoby.client.CentralImpl} which
 * helps to hide transport protocol related issues (so, for example,
 * this client does not know anything about SOAP being used, or about
 * the XML snippets being returned by the Moby Central).
 * <p>
 *
 * The client has a rich help page which can be invoked by typing
 *<pre>
 *    java MobyCmdLineClient -h
 *</pre>
 *
 * An another purpose of this client is to demonstrate how to work
 * with Moby Central registry without bothering with Central's API
 * details. However, iIf you are interested in finding more details
 * how to interact directly with Moby Central, have a look at another
 * client {@link TestingCentral}.
 *<p>
 *
 * @author <A HREF="mailto:senger@ebi.ac.uk">Martin Senger</A>
 * @version $Id: MobyCmdLineClient.java,v 1.16 2008/02/22 09:37:38 senger Exp $
 */

public class MobyCmdLineClient
    extends CmdLineHelper {

    /*************************************************************************
     *
     * Entry point...
     *
     *************************************************************************/
    public static void main (String [] args) {
	try {

	    BaseCmdLine cmd = getCmdLine (args, MobyCmdLineClient.class);
	    String param;

	    // where is a Moby registry
	    Central worker = getRegistryWorker (cmd);

	    //
	    // no other arguments => say what endpoint and URI are in used
	    //
	    if (cmd.params.length == 0) {
		decoration ("Registry endpoint:  ");
		msgln (worker.getRegistryEndpoint());
		decoration ("Registry namespace: ");
		msgln (worker.getRegistryNamespace());
	    }

	    //
	    // simple retrieval of various sets
	    //
	    if (cmd.hasOption ("-ls")) {
		decorationLn ("Service names:");
		decorationLn ("--------------");
		Map authorities = worker.getServiceNamesByAuthority();

		for (Iterator it = authorities.entrySet().iterator(); it.hasNext(); ) {
		    Map.Entry entry = (Map.Entry)it.next();
		    String[] names = (String[])entry.getValue();
		    for (int i = 0; i < names.length; i++) {
			msgln (names[i]);
			msgln ("\t" + entry.getKey());
		    }
		}
	    }

	    if (cmd.hasOption ("-la")) {
		decorationLn ("Service names by authorities:");
		decorationLn ("-----------------------------");
		Map authorities = worker.getServiceNamesByAuthority();

		for (Iterator it = authorities.entrySet().iterator(); it.hasNext(); ) {
		    Map.Entry entry = (Map.Entry)it.next();
		    msgln (entry.getKey());
		    String[] names = (String[])entry.getValue();
		    for (int i = 0; i < names.length; i++)
			msgln ("\t" + names[i]);
		}
	    }

	    if (cmd.hasOption ("-lp")) {
		decorationLn ("Service providers:");
		decorationLn ("------------------");
		String[] names = worker.getProviders();
		for (int i = 0; i < names.length; i++) {
		    decoration ("\t");
		    msgln (names[i]);
		}
	    }

	    if (cmd.hasOption ("-lt")) {
		decorationLn ("Service types:");
		decorationLn ("--------------");
		Map types = worker.getServiceTypes();

		for (Iterator it = types.entrySet().iterator(); it.hasNext(); ) {
		    Map.Entry entry = (Map.Entry)it.next();
		    msgln (entry.getKey());
		    msgln ("\t" + entry.getValue());
		}
	    }

	    if (cmd.hasOption ("-ln")) {
		decorationLn ("Namespaces:");
		decorationLn ("-----------");
		MobyNamespace[] nms = worker.getFullNamespaces();
		for (int i = 0; i < nms.length; i++) {
		    msgln (nms[i]);
		}
	    }

	    if (cmd.hasOption ("-ld")) {
		decorationLn ("Data types:");
		decorationLn ("-----------");
		Map types = worker.getDataTypeNames();

		for (Iterator it = types.entrySet().iterator(); it.hasNext(); ) {
		    Map.Entry entry = (Map.Entry)it.next();
		    msgln (entry.getKey());
		    msgln ("\t" + entry.getValue());
		}
	    }

	    if (cmd.hasOption ("-lu")) {
		decorationLn ("Resource URLs:");
		decorationLn ("--------------");
		MobyResourceRef[] resourceRefs = worker.getResourceRefs();
		for (int i = 0; i < resourceRefs.length; i++) {
		    msgln (resourceRefs[i].getResourceName());
		    msgln ("\t" + resourceRefs[i].getResourceLocation());
		}
	    }

	    if (cmd.hasOption ("-lr")) {
		boolean onlyNames = cmd.hasOption ("-on");
		decorationLn ("Known BioMoby Registries:");
		decorationLn ("-------------------------");
		final Registries regList = RegistriesList.getInstance();
		for (Registry reg: regList.getAll()) {
		    msgln (onlyNames ? reg.getSynonym() : reg.toString());
		}
	    }

	    //
	    // retrieval of more complex entities
	    //
	    if ((param = cmd.getParam ("-data")) != null ) {
		decorationLn ("Data type '" + param + "':");
		decorationLn ("---------");
		msgln (worker.getDataType (param));
	    }

	    if ((param = cmd.getParam ("-wsdl")) != null ) {
		decorationLn ("WSDL for service '" + param + "':");
		decorationLn ("----------------");
		int pos = param.indexOf (",");
		if (pos == -1)
		    msgln (worker.getServiceWSDL (param));
		else if (pos == param.length() - 1)
		    msgln (worker.getServiceWSDL (param, ""));
		else
		    msgln (worker.getServiceWSDL (param.substring (0, pos),
							       param.substring (pos+1)));
	    }

	    if ((param = cmd.getParam ("-ot")) != null ) {
		decorationLn ("Service type '" + param + "' is-a:");
		decorationLn ("------------");
		msgln (StringUtils.join (worker.getServiceTypeRelationships (param, true),
						      "\n"));
	    }

	    if ((param = cmd.getParam ("-od")) != null ) {
		decorationLn ("Relationships of data type '" + param + "':");
		decorationLn ("--------------------------");
		Map types = worker.getDataTypeRelationships (param);

		for (Iterator it = types.entrySet().iterator(); it.hasNext(); ) {
		    Map.Entry entry = (Map.Entry)it.next();
		    msgln (entry.getKey());
		    msgln ("\t" +
					StringUtils.join ((String[])entry.getValue(),
							  "\n\t"));
		}
	    }

	    String[] params = cmd.getParam ("-od2", 2);
	    if ((params != null && params[0] != null && params[1] != null)) {
		decorationLn ("Relationships of data type '" + params[0] + "' of type '" + params[1] + "':");
		decorationLn ("--------------------------");
		msgln (StringUtils.join (worker.getDataTypeRelationships (params[0], params[1]),
						      "\n"));
	    }

	    if ((param = cmd.getParam ("-rdf")) != null ) {
		String resourceName = null;
		if      (param.startsWith ("st")) resourceName = Central.SERVICE_TYPES_RESOURCE_NAME;
		else if (param.startsWith ("dt")) resourceName = Central.DATA_TYPES_RESOURCE_NAME;
		else if (param.startsWith ("se")) resourceName = Central.SERVICE_INSTANCES_RESOURCE_NAME;
		else if (param.startsWith ("na")) resourceName = Central.NAMESPACES_RESOURCE_NAME;
		else if (param.startsWith ("f"))  resourceName = Central.FULL_RESOURCE_NAME;
		if (resourceName != null) {
		    decorationLn ("RDF for '" + resourceName + "':");
		    decorationLn ("-------");
		    BufferedReader in =
			new BufferedReader (new InputStreamReader (worker.getResource (resourceName)));
		    String inputLine;
		    while ((inputLine = in.readLine()) != null)
			msgln (inputLine);
		    in.close();
		}
	    }

	    //
	    // registrations of various entities
	    //
	    if ((param = cmd.getParam ("-rd-name")) != null ) {
		decorationLn ("Register data type '" + param + "':");
		decorationLn ("------------------");
		MobyDataType data = new MobyDataType (param);
		data.setDescription (cmd.getParam ("-rd-desc"));
		data.setAuthority (cmd.getParam ("-rd-auth"));
		data.setEmailContact (cmd.getParam ("-rd-email"));
                if ((param = cmd.getParam ("-rd-isa")) != null ) {
		    String[] isas = param.split (",");
		    for (int i = 0; i < isas.length; i++)
			data.addParentName (isas [i]);
		}
                if ((param = cmd.getParam ("-rd-hasa")) != null ) {
		    String[] hasas = param.split (",");
		    for (int i = 0; i < hasas.length; i++) {
			int pos = hasas[i].indexOf (":");
			if (pos == -1) {
			    data.addChild (null, hasas [i], Central.iHASA);
			} else if (pos == hasas[i].length() - 1) {
			    data.addChild (hasas [i].substring (0, pos), null, Central.iHASA);
			} else {
			    data.addChild (hasas[i].substring (0, pos), hasas[i].substring (pos+1), Central.iHASA);
			}
		    }
		}
                if ((param = cmd.getParam ("-rd-has")) != null ) {
		    String[] hasas = param.split (",");
		    for (int i = 0; i < hasas.length; i++) {
			int pos = hasas[i].indexOf (":");
			if (pos == -1) {
			    data.addChild (null, hasas [i], Central.iHAS);
			} else if (pos == hasas[i].length() - 1) {
			    data.addChild (hasas [i].substring (0, pos), null, Central.iHAS);
			} else {
			    data.addChild (hasas[i].substring (0, pos), hasas[i].substring (pos+1), Central.iHAS);
			}
		    }
		}
		worker.registerDataType (data);
		decorationLn (data.toString());
	    }

	    if ((param = cmd.getParam ("-rt-name")) != null ) {
		decorationLn ("Register service type '" + param + "':");
		decorationLn ("---------------------");
		MobyServiceType stype = new MobyServiceType (param);
		stype.setDescription (cmd.getParam ("-rt-desc"));
		stype.setAuthority (cmd.getParam ("-rt-auth"));
		stype.setEmailContact (cmd.getParam ("-rt-email"));
                if ((param = cmd.getParam ("-rt-isa")) != null ) {
		    String[] isas = param.split (",");
		    for (int i = 0; i < isas.length; i++)
			stype.addParentName (isas [i]);
		}
		worker.registerServiceType (stype);
		decorationLn (stype.toString());
	    }

	    if ((param = cmd.getParam ("-rn-name")) != null ) {
		decorationLn ("Register namespace '" + param + "':");
		decorationLn ("------------------");
		MobyNamespace namespace = new MobyNamespace (param);
		namespace.setDescription (cmd.getParam ("-rn-desc"));
		namespace.setAuthority (cmd.getParam ("-rn-auth"));
		namespace.setEmailContact (cmd.getParam ("-rn-email"));
		worker.registerNamespace (namespace);
		decorationLn (namespace.toString());
	    }

	    if ((param = cmd.getParam ("-rs-name")) != null ) {
		decorationLn ("Register service '" + param + "':");
		decorationLn ("----------------");
		MobyService service = new MobyService (param);
		service.setDescription (cmd.getParam ("-rs-desc"));
		service.setType (cmd.getParam ("-rs-type"));
		service.setCategory (cmd.getParam ("-rs-categ"));
		service.setAuthority (cmd.getParam ("-rs-auth"));
		service.setEmailContact (cmd.getParam ("-rs-email"));
		if ((param = cmd.getParam ("-rs-main")) != null) {
		    service.setAuthoritative (param.equals ("1") ? true : false);
		}
		service.setURL (cmd.getParam ("-rs-url"));
		service.setSignatureURL (cmd.getParam ("-rs-rdf"));
		if ( (param = cmd.getParam ("-rs-rdfpath")) != null ) {
		    service.setPathToRDF (param);
		} else {
		    File tmpFile = File.createTempFile ("Moby-RDF." + service.getName() + ".", ".xml");
		    service.setPathToRDF (tmpFile.getAbsolutePath());
		}

                if ( (param = cmd.getParam ("-rs-in")) != null ) {
		    String[] inputs = param.split (",");
		    for (int i = 0; i < inputs.length; i++) {
			String[] parts = inputs[i].split ("=");
			if (parts.length >= 2) {
			    MobyPrimaryDataSimple data = new MobyPrimaryDataSimple ("dummy");
			    data.setDataType (new MobyDataType (parts[0]));
			    data.addNamespace (new MobyNamespace (parts[1]));
			    service.addInput (data);
			} else {
			    MobyPrimaryDataSimple data = new MobyPrimaryDataSimple ("dummy");
			    data.setDataType (new MobyDataType (parts[0]));
			    service.addInput (data);
			}
		    }
		}
                if ((param = cmd.getParam ("-rs-ifile")) != null ) {
		    String[] files = param.split (",");
		    for (int i = 0; i < files.length; i++) {
			Properties props = new Properties();
			try {
			    props.load (new FileInputStream (files[i]));
			    if (props.containsKey ("secondary")) {
				// for 'secondary'
				String value = props.getProperty ("name", "un-named");
				MobySecondaryData data = new MobySecondaryData (value);
				if ((value = props.getProperty ("datatype")) != null)
				    data.setDataType (value);
				if ((value = props.getProperty ("default")) != null)
				    data.setDefaultValue (value);
				if ((value = props.getProperty ("max")) != null)
				    data.setMaxValue (value);
				if ((value = props.getProperty ("min")) != null)
					data.setMinValue (value);
				for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {
				    String propName = (String)en.nextElement();
				    if (propName.startsWith ("enum"))
					data.addAllowedValue (props.getProperty (propName));
				}
				service.addInput (data);

			    } else if (props.containsKey ("collection")) {
				// for 'collection'
				String value = props.getProperty ("name", "un-named");
				MobyPrimaryDataSet data = new MobyPrimaryDataSet (value);
				for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {
				    String propName = (String)en.nextElement();
				    if (propName.startsWith ("file")) {
					data.addElement (createSimple (props.getProperty (propName)));
				    }
				}
				service.addInput (data);

			    } else {
				// for 'simple'
				MobyPrimaryDataSimple data = createSimple (files[i]);
				if (data != null)
				    service.addInput (data);
			    }
			} catch (IOException e) {
			    System.err.println ("ERROR: Failed to read " + files[i] + " (" + e.toString() + ").");
			}
		    }
		}
                if ((param = cmd.getParam ("-rs-out")) != null ) {
		    String[] outs = param.split (",");
		    for (int i = 0; i < outs.length; i++) {
			String[] parts = outs[i].split ("=");
			if (parts.length >= 2) {
			    MobyPrimaryDataSimple data = new MobyPrimaryDataSimple ("dummy");
			    data.setDataType (new MobyDataType (parts[0]));
			    data.addNamespace (new MobyNamespace (parts[1]));
			    service.addOutput (data);
			} else {
			    MobyPrimaryDataSimple data = new MobyPrimaryDataSimple ("dummy");
			    data.setDataType (new MobyDataType (parts[0]));
			    service.addOutput (data);
			}
		    }
		}
                if ((param = cmd.getParam ("-rs-ofile")) != null ) {
		    String[] files = param.split (",");
		    for (int i = 0; i < files.length; i++) {
			Properties props = new Properties();
			try {
			    props.load (new FileInputStream (files[i]));
			    if (props.containsKey ("secondary")) {
				// ignore 'secondary'

			    } else if (props.containsKey ("collection")) {
				// for 'collection'
				String value = props.getProperty ("name", "un-named");
				MobyPrimaryDataSet data = new MobyPrimaryDataSet (value);
				for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {
				    String propName = (String)en.nextElement();
				    if (propName.startsWith ("file")) {
					data.addElement (createSimple (props.getProperty (propName)));
				    }
				}
				service.addOutput (data);

			    } else {
				// for 'simple'
				MobyPrimaryDataSimple data = createSimple (files[i]);
				if (data != null)
				    service.addOutput (data);
			    }
			} catch (IOException e) {
			    System.err.println ("ERROR: Failed to read " + files[i] + " (" + e.toString() + ").");
			}
		    }
		}
		worker.registerService (service);
		decorationLn (service.toString());
	    }

	    //
	    // unregistrations of various entities
	    //
	    if ((param = cmd.getParam ("-ud")) != null ) {
		decorationLn ("Unregister data type '" + param + "':");
		decorationLn ("--------------------");
		MobyDataType data = new MobyDataType (param);
		worker.unregisterDataType (data);
		decorationLn (data.toString());
	    }

	    if ((param = cmd.getParam ("-ut")) != null ) {
		decorationLn ("Unregister service type '" + param + "':");
		decorationLn ("-----------------------");
		MobyServiceType stype = new MobyServiceType (param);
		worker.unregisterServiceType (stype);
		decorationLn (stype.toString());
	    }

	    if ((param = cmd.getParam ("-un")) != null ) {
		decorationLn ("Unregister namespace '" + param + "':");
		decorationLn ("--------------------");
		MobyNamespace namespace = new MobyNamespace (param);
		worker.unregisterNamespace (namespace);
		decorationLn (namespace.toString());
	    }

	    if ((param = cmd.getParam ("-us")) != null ) {
		decorationLn ("Unregister service '" + param + "':");
		decorationLn ("--------------------");
		int pos = param.indexOf (",");
		if (pos == -1 || pos == param.length() - 1)
		    System.err.println ("ERROR: an authority must be specified");
		else {
		    MobyService service = new MobyService (param.substring (0, pos));
		    service.setAuthority (param.substring (pos+1));
		    worker.unregisterService (service);
		    decorationLn (service.toString());
		}
	    }

	    //
	    // look-up services
	    //
	    if ((param = cmd.getParam ("-fn")) != null ) {
		decorationLn ("Looking for services '" + param + "':");
		decorationLn ("--------------------");
		int pos = param.indexOf (",");
		MobyService[] services;
		MobyService pattern = null;
		if (pos == -1) {
		    pattern = new MobyService (param);
		    pattern.setCategory ("");
		    services = worker.findService (pattern);
		} else if (pos == param.length() - 1) {
		    pattern = new MobyService (param.substring (0, pos));
		    pattern.setCategory ("");
		    services = worker.findService (pattern);
		} else {
		    pattern = new MobyService (param.substring (0, pos));
		    pattern.setAuthority (param.substring (pos+1));
		    pattern.setCategory ("");
		    services = worker.findService (pattern);
		}
		printServices (services, cmd.hasOption ("-on"));
	    }
	    if ((param = cmd.getParam ("-f")) != null ) {
		decorationLn ("Looking for services related to: '" + param + "':");
		decorationLn ("--------------------------------");
		printServices (worker.findService (param.split (",")),
			       cmd.hasOption ("-on"));
	    }

	    if ((param = cmd.getParam ("-ft")) != null ) {
		decorationLn ("Looking for services of type '" + param + "':");
		decorationLn ("----------------------------");
		MobyService pattern = new MobyService ("dummy");
		pattern.setCategory ("");
		pattern.setType (param);
		printServices (worker.findService (pattern, null, !cmd.hasOption ("-nc"), true),
			       cmd.hasOption ("-on"));
	    }

	    if ((param = cmd.getParam ("-fc")) != null ) {
		decorationLn ("Looking for services of category '" + param + "':");
		decorationLn ("--------------------------------");
		MobyService pattern = new MobyService ("dummy");
		pattern.setCategory (param);
		printServices (worker.findService (pattern, null, !cmd.hasOption ("-nc"), true),
			       cmd.hasOption ("-on"));
	    }

	    if (cmd.hasParam ("-fs-type") || cmd.hasParam ("-fs-auth") ||
		cmd.hasParam ("-fs-in")   || cmd.hasParam ("-fs-out")) {
		decorationLn ("Looking for services with the following pattern:");
		MobyService pattern = new MobyService ("dummy");
		pattern.setCategory ("");
		pattern.setType (cmd.getParam ("-fs-type"));
		pattern.setAuthority (cmd.getParam ("-fs-auth"));
                if ((param = cmd.getParam ("-fs-in")) != null ) {
		    String[] inputs = param.split (",");
		    for (int i = 0; i < inputs.length; i++) {
			String[] parts = inputs[i].split ("=");
			MobyPrimaryDataSimple input = new MobyPrimaryDataSimple ("dummy");
			input.setDataType (new MobyDataType (parts[0]));
			if (parts.length >= 2)
			    input.addNamespace (new MobyNamespace (parts[1]));
			pattern.addInput (input);
		    }
		}
                if ((param = cmd.getParam ("-fs-out")) != null ) {
		    String[] outs = param.split (",");
		    for (int i = 0; i < outs.length; i++) {
			MobyPrimaryDataSimple output = new MobyPrimaryDataSimple ("dummy");
			output.setDataType (new MobyDataType (outs[i]));
			pattern.addOutput (output);
// 			pattern.addOutputType (outs [i]);
		    }
		}
		decoration (pattern.toString());
		decorationLn ("-----------------------------------------------");
		boolean includeChildrenServiceTypes = !cmd.hasOption ("-nc");
		boolean includeParentDataTypes = !cmd.hasOption ("-np");
		printServices (worker.findService (pattern, null,
						   includeChildrenServiceTypes,
						   includeParentDataTypes),
			       cmd.hasOption ("-on"));
	    }

	    //
	    // call a raw method (for debugging, mostly)
	    //
	    params = null;
	    if ((params = cmd.getParam ("-call", 2)) != null ) {
		if (params[0] != null) {
		    if (params[1] == null) {
			decorationLn ("Calling method '" + params[0] + "':");
		    } else {
			decorationLn ("Calling method '" + params[0] + "' with the input from '" + params[1] + "':");
			if (new File (params[1]).exists())
			    params[1] =  FileUtils.getFile (params[1]);
			decorationLn (params[1]);
		    }
		    decorationLn ("--------------");
		    msgln (worker.call (params[0], params[1]));
		}
	    }

	    //
	    // call a service instead of a Moby Central
	    //
	    params = null;
	    if ((params = cmd.getParam ("-scall", 2)) != null ) {
		if (params[0] != null && params[1] != null) {

		    // find name and URL of the wanted service
		    String serviceName = null;
		    String authority = null;
		    int pos = params[0].indexOf (",");
		    if (pos == -1) {
			serviceName = params[0];
		    } else if (pos == params[0].length() - 1) {
			serviceName = params[0].substring (0, pos);
		    } else {
			serviceName = params[0].substring (0, pos);
			authority = params[0].substring (pos+1);
		    }
		    String serviceURL = null;
		    String category = MobyService.CATEGORY_MOBY;
		    if ((serviceURL = cmd.getParam ("-url")) == null) {
			// look in the registry
			decorationLn ("Looking for service '" + params[0] + "':");
			MobyService pattern = new MobyService (serviceName, authority);
			pattern.setCategory ("");
			MobyService[] services = worker.findService (pattern);
			if (services != null && services.length > 0) {
			    serviceURL = services[0].getURL();
			    category = services[0].getCategory();
			}
		    }

		    if (! "".equals (serviceURL)) {
			String methodName = null;

			if (MobyService.CATEGORY_MOBY_ASYNC.equals (category) ||
			    cmd.hasOption ("-async")) {
			    methodName = serviceName + MobyService.SUBMIT_ACTION_SUFFIX;
			} else {
			    methodName = serviceName;
			}

			// call the service
			decorationLn ("Calling '" + methodName +
				      "' with the input from '" + params[1] + "':");
			if (new File (params[1]).exists())
			    params[1] =  FileUtils.getFile (params[1]);
			decorationLn (params[1]);
			decorationLn ("---------------");
			Central serviceWorker = new CentralImpl (serviceURL, "http://biomoby.org/");
			msgln (serviceWorker.call (methodName, params[1]));
		    }
		}
	    }

	} catch (PendingCurationException e) {
	    emsgln ("===ERROR===");
	    emsgln ("Pending Curation");
	    emsgln ("===========");

	} catch (NoSuccessException e) {
	    emsgln ("===ERROR===");
	    emsgln (e.getMessage());
	    emsgln ("" + e.getCulprit());
	    emsgln ("===========");

	} catch (Throwable e) {
	    processErrorAndExit (e);
	}
    }


    // print 'msg' but only if in verbose mode
    static void decoration (String msg) {
	if (verbose) msg (msg);
    }
    static void decorationLn (String msg) {
	if (verbose) msgln (msg);
    }

    static MobyPrimaryDataSimple createSimple (String fname) {
	Properties props = new Properties();
	try {
	    props.load (new FileInputStream (fname));
	    String value = props.getProperty ("name", "un-named");
	    MobyPrimaryDataSimple data = new MobyPrimaryDataSimple (value);
	    if ((value = props.getProperty ("datatype")) != null)
		data.setDataType (new MobyDataType (value));
	    for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {
		String propName = (String)en.nextElement();
		if (propName.startsWith ("namespace"))
		    data.addNamespace (new MobyNamespace (props.getProperty (propName)));
	    }
	    return data;
	} catch (IOException e) {
	    emsgln ("ERROR: Failed to read " + fname + " (" + e.toString() + ").");
	    return null;
	}
    }

    static void printServices (MobyService[] services, boolean onlyNames) {
	if (services != null) {
	    for (int i = 0; i < services.length; i++) {
		if (onlyNames)
		    msgln (services[i].getUniqueName());
		else
		    msgln (services[i]);
	    }
	}
    }

}
