JavaFiles.AbstractWsToolClient.java Source code

Java tutorial

Introduction

Here is the source code for JavaFiles.AbstractWsToolClient.java

Source

/* $Id: AbstractWsToolClient.java 2460 2013-01-25 12:35:43Z hpm $
 * ======================================================================
 * 
 * Copyright 2008-2013 EMBL - European Bioinformatics Institute
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ======================================================================
 * Abstract jDispatcher web services Java client.
 * ====================================================================== */
package JavaFiles;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.commons.cli.Options;

/** Abstract class defining common methods to all jDispatcher SOAP web 
 * service clients.
 * 
 * See:
 * <a href="http://www.ebi.ac.uk/Tools/webservices/">http://www.ebi.ac.uk/Tools/webservices/</a>
 * <a href="http://www.ebi.ac.uk/Tools/webservices/tutorials/java">http://www.ebi.ac.uk/Tools/webservices/tutorials/java</a>
 */
public abstract class AbstractWsToolClient {
    /** Output level. Controlled by the --verbose and --quiet options. */
    protected int outputLevel = 1;
    /** Debug level. Controlled by the --debugLevel option. */
    private int debugLevel = 0;
    /** Maximum interval between polling events (ms). */
    private int maxCheckInterval = 60000;
    /** Temporary line for fasta sequence parsing. */
    private String tmpFastaLine = null;
    /** Buffered reader for fasta sequence input. */
    private BufferedReader fastaInputReader = null;
    /** Buffered reader for identifier list input. */
    private BufferedReader identifierListReader = null;
    /** URL for service endpoint. */
    private String serviceEndPoint = null;
    /** Generic options message. */
    private static final String genericOptsStr = "[General]\n" + "\n"
            + "      --params         :      : list tool parameters\n"
            + "      --paramDetail    : str  : information about a parameter\n"
            + "      --email          : str  : e-mail address, required to submit job\n"
            + "      --title          : str  : title for the job\n"
            + "      --async          :      : perform an asynchronous submission\n"
            + "      --jobid          : str  : job identifier\n"
            + "      --status         :      : get status of a job\n"
            + "      --resultTypes    :      : get list of result formats for a job\n"
            + "      --polljob        :      : get results for a job\n"
            + "      --outfile        : str  : name of the file results should be written to\n"
            + "                                (default is based on the jobid; \"-\" for STDOUT)\n"
            + "      --outformat      : str  : output format, see --resultTypes\n"
            + "      --help           :      : prints this help text\n"
            + "      --quiet          :      : decrease output\n"
            + "      --verbose        :      : increase output\n"
            + "      --debugLevel     : int  : set debug output level\n" + "\n" + "Synchronous job:\n" + "\n"
            + "  The results/errors are returned as soon as the job is finished.\n"
            + "  Usage: java -jar <jarFile> --email <your@email> [options...] seqFile\n"
            + "  Returns: results as an attachment\n" + "\n" + "Asynchronous job:\n" + "\n"
            + "  Use this if you want to retrieve the results at a later time. The results \n"
            + "  are stored for up to 7 days.\n"
            + "  Usage: java -jar <jarFile> --async --email <your@email> [options...] seqFile\n"
            + "  Returns: jobid\n" + "\n" + "  Use the jobid to query for the status of the job.\n"
            + "  Usage: java -jar <jarFile> --status --jobid <jobId>\n"
            + "  Returns: string indicating the status of the job.\n" + "\n"
            + "  If the job is finished, get the available result types.\n"
            + "  Usage: java -jar <jarFile> --resultTypes --jobid <jobId>\n"
            + "  Returns: details of the available results for the job.\n" + "\n"
            + "  If the job is finished get the results.\n" + "  Usage:\n"
            + "   java -jar <jarFile> --polljob --jobid <jobId>\n"
            + "   java -jar <jarFile> --polljob --jobid <jobId> --outformat <format>\n"
            + "  Returns: results in the requested format, or if not specified all \n"
            + "  formats. By default the output file(s) are named after the job, to \n"
            + "  specify a name for the file(s) use the --outfile option.\n";

    /** Add genetic option to command-line parser.
     * 
     * @param options Command-line parser.
     */
    protected static void addGenericOptions(Options options) {
        options.addOption("help", "help", false, "help on using this client");
        options.addOption("async", "async", false, "perform an asynchronous job");
        options.addOption("polljob", "polljob", false,
                "poll for the status of an asynchronous job and get the results");
        options.addOption("status", "status", false, "poll for the status of an asynchronous job");
        options.addOption("email", "email", true, "Your email address");
        options.addOption("jobid", "jobid", true, "Job identifier of an asynchronous job");
        options.addOption("stdout", "stdout", false, "print to standard output");
        options.addOption("outfile", "outfile", true, "file name to save the results");
        options.addOption("outformat", "outformat", true, "Output format (txt or xml)");
        options.addOption("quiet", "quiet", false, "Decrease output messages");
        options.addOption("verbose", "verbose", false, "Increase output messages");
        options.addOption("params", "params", false, "List parameters");
        options.addOption("paramDetail", "paramDetail", true, "List parameter information");
        options.addOption("resultTypes", "resultTypes", false, "List result types for job");
        options.addOption("debugLevel", "debugLevel", true, "Debug output");
        options.addOption("endpoint", "endpoint", true, "Service endpoint URL");
    }

    /** Print the generic options usage message to STDOUT. */
    protected static void printGenericOptsUsage() {
        System.out.println(genericOptsStr);
    }

    /** Set debug level. 
     * 
     * @param level Debug level. 0 = off.
     */
    public void setDebugLevel(int level) {
        printDebugMessage("setDebugLevel", "Begin " + level, 1);
        if (level > -1) {
            debugLevel = level;
        }
        printDebugMessage("setDebugLevel", "End", 1);
    }

    /** Get current debug level. 
     * 
     * @return Debug level.
     */
    public int getDebugLevel() {
        printDebugMessage("getDebugLevel", new Integer(debugLevel).toString(), 1);
        return debugLevel;
    }

    /** Output debug message at specified level
     * 
     * @param methodName Name of the method to appear in the message
     * @param message The message
     * @param level Level at which to output message
     */
    protected void printDebugMessage(String methodName, String message, int level) {
        if (level <= debugLevel) {
            System.err.println("[" + methodName + "()] " + message);
        }
    }

    /** <p>Get the HTTP user-agent string used for java.net calls by the 
     * client (see RFC2616).</p>
     * 
     * @return User-agent string.
     */
    public String getUserAgent() {
        printDebugMessage("getUserAgent", "Begin/End", 1);
        return System.getProperty("http.agent");
    }

    /** <p>Set the HTTP user-agent string used for java.net calls by the 
     * client (see RFC2616).</p>
     * 
     * @param userAgent User-agent string to prepend to default client 
     * user-agent.
     */
    public void setUserAgent(String userAgent) {
        printDebugMessage("setUserAgent", "Begin", 1);
        // Java web calls use the http.agent property as a prefix to the default user-agent.
        StringBuffer clientUserAgent = new StringBuffer();
        if (userAgent != null && userAgent.length() > 0) {
            clientUserAgent.append(userAgent);
        }
        clientUserAgent.append(getClientUserAgentString());
        if (System.getProperty("http.agent") != null) {
            clientUserAgent.append(" ").append(System.getProperty("http.agent"));
        }
        System.setProperty("http.agent", clientUserAgent.toString());
        printDebugMessage("setUserAgent", "End", 1);
    }

    /** <p>Set the HTTP User-agent header string (see RFC2616) used by the 
     * client for java.net requests.</p>
     */
    protected void setUserAgent() {
        printDebugMessage("setUserAgent", "Begin", 1);
        // Java web calls use the http.agent property as a prefix to the default user-agent.
        String clientUserAgent = getClientUserAgentString();
        if (System.getProperty("http.agent") != null) {
            System.setProperty("http.agent", clientUserAgent + " " + System.getProperty("http.agent"));
        } else
            System.setProperty("http.agent", clientUserAgent);
        printDebugMessage("setUserAgent", "End", 1);
    }

    /** Get client specific user-agent string for addition to client 
     * user-agent string.
     * 
     * @return Client specific user-agent string.
     */
    protected abstract String getClientUserAgentString();

    /** Generate a string containing the values of the fields with "get" methods. 
     * 
     * @param obj Object the get values from
     * @return String containing values and method names.
     */
    // @SuppressWarnings("unchecked")
    protected String objectFieldsToString(Object obj) {
        printDebugMessage("ObjectFieldsToString", "Begin", 31);
        StringBuilder strBuilder = new StringBuilder();
        try {
            Class objType = obj.getClass();
            printDebugMessage("ObjectFieldsToString", "objType: " + objType, 32);
            java.lang.reflect.Method[] methods = objType.getMethods();
            for (int i = 0; i < methods.length; i++) {
                String methodName = methods[i].getName();
                if (methodName.startsWith("get") && methods[i].getParameterTypes().length == 0
                        && !methodName.equals("getClass") && !methodName.equals("getTypeDesc")) {
                    printDebugMessage("ObjectFieldsToString", "invoke(): " + methodName, 32);
                    Object tmpObj = methods[i].invoke(obj, new Object[0]);
                    // Handle any string lists (e.g. database)
                    if (tmpObj instanceof String[]) {
                        String[] tmpList = (String[]) tmpObj;
                        strBuilder.append(methodName + ":\n");
                        for (int j = 0; j < tmpList.length; j++) {
                            strBuilder.append("\t" + tmpList[j] + "\n");
                        }
                    }
                    // Otherwise just use implicit toString();
                    else {
                        strBuilder.append(methodName + ": " + tmpObj + "\n");
                    }
                }
            }
        } catch (SecurityException e) {
            System.err.println(e.getMessage());
        } catch (IllegalArgumentException e) {
            System.err.println(e.getMessage());
        } catch (IllegalAccessException e) {
            System.err.println(e.getMessage());
        } catch (InvocationTargetException e) {
            System.err.println(e.getMessage());
        }
        printDebugMessage("ObjectFieldsToString", "End", 31);
        return strBuilder.toString();
    }

    /** Set the output level. 
     * 
     * @param level Output level. 0 = quiet, 1 = normal and 2 = verbose.
     */
    public void setOutputLevel(int level) {
        printDebugMessage("setOutputLevel", "Begin " + level, 1);
        if (level > -1) {
            this.outputLevel = level;
        }
        printDebugMessage("setOutputLevel", "End", 1);
    }

    /** Get the current output level.
     * 
     * @return Output level.
     */
    public int getOutputLevel() {
        printDebugMessage("getOutputLevel", new Integer(this.outputLevel).toString(), 1);
        return this.outputLevel;
    }

    /** Set the maximum interval between polling events.
     * 
     * @param checkInterval Maximum interval in milliseconds. Must be greater than 1000.
     */
    public void setMaxCheckInterval(int checkInterval) {
        printDebugMessage("setMaxCheckInterval", "Begin " + checkInterval, 1);
        if (checkInterval > 1000) {
            this.maxCheckInterval = checkInterval;
        }
        printDebugMessage("setMaxCheckInterval", "End", 1);
    }

    /** Get the maximum interval between polling events.
     * 
     * @return Maximum interval in milliseconds
     */
    public int getMaxCheckInterval() {
        printDebugMessage("getMaxCheckInterval", new Integer(this.maxCheckInterval).toString(), 1);
        return this.maxCheckInterval;
    }

    /** Set the service endpoint URL for generating the service connection.
     * 
     * @param urlStr Service endpoint URL as a string.
     */
    public void setServiceEndPoint(String urlStr) {
        this.serviceEndPoint = urlStr;
    }

    /** Get the current service endpoint URL.
     * 
     * @return The service endpoint URL as a string.
     */
    public String getServiceEndPoint() {
        return this.serviceEndPoint;
    }

    /** Print a progress message.
     * 
     * @param msg The message to print.
     * @param level The output level at or above which this message should be displayed.
     */
    protected void printProgressMessage(String msg, int level) {
        if (outputLevel >= level) {
            System.err.println(msg);
        }
    }

    /** Read the contents of a file into a byte array.
     * 
     * @param file the file to read
     * @return the contents of the file in a byte array
     * @throws IOException if all contents not read
     */
    public byte[] readFile(File file) throws IOException, FileNotFoundException {
        printDebugMessage("readFile", "Begin", 1);
        printDebugMessage("readFile", "file: " + file.getPath() + File.pathSeparator + file.getName(), 2);
        if (!file.exists()) {
            throw new FileNotFoundException(file.getName() + " does not exist");
        }
        InputStream is = new FileInputStream(file);
        byte[] bytes = readStream(is);
        is.close();
        printDebugMessage("readFile", "End", 1);
        return bytes;
    }

    /** Read the contents of an input stream into a byte array.
     * 
     * @param inStream the input steam to read
     * @return the contents of the stream in a byte array
     * @throws IOException if all contents not read
     */
    public byte[] readStream(InputStream inStream) throws IOException {
        printDebugMessage("readStream", "Begin", 1);
        byte[] ret = null;
        while (inStream.available() > 0) {
            long length = inStream.available();
            byte[] bytes = new byte[(int) length];
            int offset = 0;
            int numRead = 0;
            while (offset < bytes.length && (numRead = inStream.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }
            if (offset < bytes.length) {
                throw new IOException("Unable to read to end of stream");
            }
            printDebugMessage("readStream", "read " + bytes.length + " bytes", 2);
            if (ret == null)
                ret = bytes;
            else {
                byte[] tmp = ret.clone();
                ret = new byte[ret.length + bytes.length];
                System.arraycopy(tmp, 0, ret, 0, tmp.length);
                System.arraycopy(bytes, 0, ret, tmp.length, bytes.length);
            }
        }
        printDebugMessage("readStream", "End", 1);
        return ret;
    }

    /** <p>The input data can be passed as:</p>
     * <ul>
     * <li>a filename</li>
     * <li>an entry identifier (e.g. UNIPROT:WAP_RAT)</li>
     * <li>raw data (e.g. "MRCSISLVLG")</li>
     * <li>data from standard input (STDIN)</li>
     * </ul>
     * <p>This method gets the data to be passed to the service, checking for 
     * a file and loading it if necessary.</p>
     * 
     * @param fileOptionStr Filename or entry identifier.
     * @return Data to use as input as a byte array.
     * @throws IOException
     */
    public byte[] loadData(String fileOptionStr) throws IOException {
        printDebugMessage("loadData", "Begin", 1);
        printDebugMessage("loadData", "fileOptionStr: " + fileOptionStr, 2);
        byte[] retVal = null;
        if (fileOptionStr != null) {
            if (fileOptionStr.equals("-")) { // STDIN.
                // TODO: wait for input from STDIN.
                retVal = readStream(System.in);
            } else if (new File(fileOptionStr).exists()) { // File.
                retVal = readFile(new File(fileOptionStr));
            } else { // Entry Id or raw data.
                retVal = fileOptionStr.getBytes();
            }
        }
        printDebugMessage("loadData", "End", 1);
        return retVal;
    }

    /** Write a string to a file.
     * 
     * @param file the file to create/write to
     * @param data the string to write
     * @return an integer value indicating success/failure
     * @throws IOException if there is a problem with the file operations
     */
    public int writeFile(File file, String data) throws IOException {
        printDebugMessage("writeFile", "Begin", 1);
        printDebugMessage("writeFile", "file: " + file.getName(), 2);
        printDebugMessage("writeFile", "data: " + data.length() + " characters", 2);
        OutputStream os = new FileOutputStream(file);
        PrintStream p = new PrintStream(os);
        p.println(data);
        p.close();
        printDebugMessage("writeFile", "End", 1);
        return 0;
    }

    /** Write an array of bytes to a file.
     * 
     * @param file the file to create/write to
     * @param data the bytes to write
     * @return an integer value indicating success/failure
     * @throws IOException if there is a problem with the file operations
     */
    public int writeFile(File file, byte[] data) throws IOException {
        printDebugMessage("writeFile", "Begin", 1);
        printDebugMessage("writeFile", "file: " + file.getName(), 2);
        printDebugMessage("writeFile", "data: " + data.length + " bytes", 2);
        OutputStream os = new FileOutputStream(file);
        os.write(data);
        os.close();
        printDebugMessage("writeFile", "End", 1);
        return 0;
    }

    /** Get an instance of the service proxy to use with other methods.
     * 
     * @throws ServiceException
     */
    abstract protected void srvProxyConnect() throws ServiceException;

    /** Get list of tool parameter names.
     * 
     * @return String array containing list of parameter names
     * @throws ServiceException
     * @throws RemoteException
     */
    abstract public String[] getParams() throws ServiceException, RemoteException;

    /** Print list of parameter names for tool.
     * 
     * @throws RemoteException
     * @throws ServiceException
     */
    protected void printParams() throws RemoteException, ServiceException {
        printDebugMessage("printParams", "Begin", 1);
        String[] paramList = getParams();
        for (int i = 0; i < paramList.length; i++) {
            System.out.println(paramList[i]);
        }
        printDebugMessage("printParams", "End", 1);
    }

    /** Print information about a tool parameter
     * 
     * @param paramName Name of the tool parameter to get information for.
     * @throws RemoteException
     * @throws ServiceException
     */
    abstract protected void printParamDetail(String paramName) throws RemoteException, ServiceException;

    /** Get the status of a submitted job given its job identifier.
     * 
     * @param jobid The job identifier
     * @return Job status as a string.
     * @throws IOException
     * @throws ServiceException
     */
    abstract public String checkStatus(String jobid) throws IOException, ServiceException;

    /** Poll the job status until the job completes.
     * 
     * @param jobid The job identifier.
     * @throws ServiceException
     */
    public void clientPoll(String jobId) throws ServiceException {
        printDebugMessage("clientPoll", "Begin", 1);
        printDebugMessage("clientPoll", "jobId: " + jobId, 2);
        int checkInterval = 1000;
        String status = "PENDING";
        // Check status and wait if not finished
        while (status.equals("RUNNING") || status.equals("PENDING")) {
            try {
                status = this.checkStatus(jobId);
                printProgressMessage(status, 1);
                if (status.equals("RUNNING") || status.equals("PENDING")) {
                    // Wait before polling again.
                    printDebugMessage("clientPoll", "checkInterval: " + checkInterval, 2);
                    Thread.sleep(checkInterval);
                    checkInterval *= 2;
                    if (checkInterval > this.maxCheckInterval)
                        checkInterval = this.maxCheckInterval;
                }
            } catch (InterruptedException ex) {
                // Ignore
            } catch (IOException ex) {
                // Report and continue
                System.err.println("Warning: " + ex.getMessage());
            }
        }
        printDebugMessage("clientPoll", "End", 1);
    }

    /** Print details of the available result types for a job.
     * 
     * @param jobId Job identifier to check for result types.
     * @throws ServiceException
     * @throws RemoteException
     */
    abstract protected void printResultTypes(String jobId) throws ServiceException, RemoteException;

    /** Get the results for a job and save them to files.
     * 
     * @param jobid The job identifier.
     * @param outfile The base name of the file to save the results to. If
     * null the jobid will be used.
     * @param outformat The name of the data format to save, e.g. toolraw 
     * or toolxml. If null all available data formats will be saved.
     * @return Array of filenames
     * @throws IOException
     * @throws javax.xml.rpc.ServiceException
     */
    abstract public String[] getResults(String jobid, String outfile, String outformat)
            throws IOException, ServiceException;

    /** Set input fasta format sequence data file.
     * 
     * @param fastaFileName Name of the file.
     * @throws FileNotFoundException 
     */
    public void setFastaInputFile(String fastaFileName) throws FileNotFoundException {
        Reader inputReader = null;
        if (fastaFileName.equals("-")) { // STDIN
            inputReader = new InputStreamReader(System.in);
        } else { // File
            inputReader = new FileReader(fastaFileName);
        }
        this.fastaInputReader = new BufferedReader(inputReader);
    }

    /** Get next fasta sequence from input sequence file.
     * 
     * NB: Assumes files contains correctly formated input sequences,
     * i.e. are in fasta sequence format, and that the file contains only
     * fasta formated sequences. For a more generic solution see BioJava.
     * 
     * @return Fasta input sequence from file.
     * @throws IOException
     */
    public String nextFastaSequence() throws IOException {
        String retVal = null;
        // Read lines until one begins with '>'.
        while (this.fastaInputReader.ready() && (this.tmpFastaLine == null || !this.tmpFastaLine.startsWith(">"))) {
            this.tmpFastaLine = this.fastaInputReader.readLine();
        }
        // Read fasta header line.
        if (this.tmpFastaLine.startsWith(">")) {
            this.printProgressMessage("Sequence: " + tmpFastaLine, 1);
            StringBuffer tmpFastaSeq = new StringBuffer();
            tmpFastaSeq.append(tmpFastaLine).append("\n");
            // Read lines until EOF or a line begins with '>'.
            this.tmpFastaLine = this.fastaInputReader.readLine();
            while (this.fastaInputReader.ready()
                    && (this.tmpFastaLine == null || !this.tmpFastaLine.startsWith(">"))) {
                this.tmpFastaLine = this.fastaInputReader.readLine();
                if (!tmpFastaLine.startsWith(">")) {
                    tmpFastaSeq.append(tmpFastaLine).append("\n");
                }
            }
            retVal = tmpFastaSeq.toString();
        }
        return retVal;
    }

    /** Close the input fasta sequence file.
     * 
     * @throws IOException
     */
    public void closeFastaFile() throws IOException {
        this.fastaInputReader.close();
        this.fastaInputReader = null;
    }

    /** Set the identifier list input file.
     *  
     * @param fileName Name of the identifier list file.
     * @throws FileNotFoundException
     */
    public void setIdentifierListFile(String fileName) throws FileNotFoundException {
        Reader inputReader = null;
        if (fileName.equals("-")) { // STDIN
            inputReader = new InputStreamReader(System.in);
        } else { // File
            inputReader = new FileReader(fileName);
        }
        this.identifierListReader = new BufferedReader(inputReader);
    }

    /** Get the next identifier from the identifier list file.
     * 
     * NB: Assumes identifiers are in DB:ID format.
     * 
     * @return An identifier.
     * @throws IOException
     */
    public String nextIdentifier() throws IOException {
        String retVal = null;
        // Read lines until EOF or a line contains a ':'.
        String tmpLine = this.identifierListReader.readLine();
        while (this.identifierListReader.ready() && (tmpLine == null || !(tmpLine.indexOf(':') > 0))) {
            this.printDebugMessage("nextIdentifier", "tmpLine: " + tmpLine, 12);
            tmpLine = this.identifierListReader.readLine();
        }
        this.printDebugMessage("nextIdentifier", "tmpLine: " + tmpLine, 12);
        if (tmpLine != null && tmpLine.indexOf(':') > 0) {
            retVal = tmpLine;
        }
        return retVal;
    }

    /** Close the identifier list input file.
     * 
     * @throws IOException
     */
    public void closeIdentifierListFile() throws IOException {
        this.identifierListReader.close();
        this.identifierListReader = null;
    }
}