com.mnxfst.testing.client.TSClient.java Source code

Java tutorial

Introduction

Here is the source code for com.mnxfst.testing.client.TSClient.java

Source

/*
 *  ptest-server and client provides you with a performance test utility
 *  Copyright (C) 2012  Christian Kreutzfeldt <mnxfst@googlemail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.mnxfst.testing.client;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.xml.sax.SAXException;

import com.mnxfst.testing.AbstractTSCommandLineTool;
import com.mnxfst.testing.exception.TSClientConfigurationException;
import com.mnxfst.testing.exception.TSClientExecutionException;
import com.mnxfst.testing.plan.exec.TSPlanRecurrenceType;

/**
 * Provides a command-line client to control the test servers 
 * @author mnxfst
 * @since 10.02.2012
 */
public class TSClient extends AbstractTSCommandLineTool {

    public static final String CMD_OPT_MODE_EXECUTE = "execute";
    public static final String CMD_OPT_MODE_COLLECT = "collect";
    public static final String CMD_OPT_MODE_SATURATION_TEST = "saturationTest";
    public static final String CMD_OPT_THREADS = "threads";
    public static final String CMD_OPT_THREADS_SHORT = "t";
    public static final String CMD_OPT_RECURRENCES = "recurrences";
    public static final String CMD_OPT_RECURRENCES_SHORT = "r";
    public static final String CMD_OPT_RECURRENCE_TYPE = "recurrenceType";
    public static final String CMD_OPT_RECURRENCE_TYPE_SHORT = "rt";
    public static final String CMD_OPT_TESTPLAN = "testPlan";
    public static final String CMD_OPT_TESTPLAN_SHORT = "tp";
    public static final String CMD_OPT_PTEST_SERVER_HOSTS = "ptestHosts";
    public static final String CMD_OPT_PTEST_SERVER_HOSTS_SHORT = "h";
    public static final String CMD_OPT_PTEST_SERVER_PORT = "ptestPort";
    public static final String CMD_OPT_PTEST_SERVER_PORT_SHORT = "p";
    public static final String CMD_OPT_PTEST_SERVER_ADDITIONAL_PROPERTIES_FILE = "addPropsFile";
    public static final String CMD_OPT_PTEST_SERVER_ADDITIONAL_PROPERTIES_FILE_SHORT = "apfile";
    public static final String CMD_OPT_PTEST_SERVER_URL_ENCODING = "urlEncoding";
    public static final String CMD_OPT_PTEST_SERVER_URL_ENCODING_SHORT = "uenc";
    public static final String CMD_OPT_RESULT_IDENTIFIER = "resultIdentifier";
    public static final String CMD_OPT_RESULT_IDENTIFIER_SHORT = "rid";
    public static final String CMD_OPT_SAT_TEST_MAX_RUNTIME = "maxRuntime";
    public static final String CMD_OPT_SAT_TEST_MAX_RUNTIME_SHORT = "maxrun";
    public static final String CMD_OPT_SAT_TEST_THREAD_INCREMENT = "threadIncrement";
    public static final String CMD_OPT_SAT_TEST_THREAD_INCREMENT_SHORT = "ti";
    public static final String CMD_OPT_SAT_TEST_WAIT_TIME = "waitTime";
    public static final String CMD_OPT_SAT_TEST_WAIT_TIME_SHORT = "wt";

    public static final String REQUEST_PARAMETER_EXECUTE = "execute";
    public static final String REQUEST_PARAMETER_COLLECT = "collect";
    public static final String REQUEST_PARAMETER_RESULT_IDENTIFIER = "resultIdentifier";
    public static final String REQUEST_PARAMETER_THREADS = "threads";
    public static final String REQUEST_PARAMETER_RECURRENCES = "recurrences";
    public static final String REQUEST_PARAMETER_RECURRENCE_TYPE = "recurrencetype";
    public static final String REQUEST_PARAMETER_TESTPLAN = "testplan";

    public static void main(String[] args) throws ClientProtocolException, IOException, SAXException,
            ParserConfigurationException, ParseException {

        TSClient client = new TSClient();

        client.executeClient(args);

        System.exit(0);
        // TODO parse error message from result
    }

    public void executeClient(String[] args) {

        Options commandLineOptions = getTSClientCommandLineOptions();
        try {
            CommandLine commandLine = parseCommandline(commandLineOptions, args);

            if (!commandLine.hasOption(CMD_OPT_MODE_EXECUTE) && !commandLine.hasOption(CMD_OPT_MODE_COLLECT)
                    && !commandLine.hasOption(CMD_OPT_MODE_SATURATION_TEST)) {
                printHelp(commandLineOptions,
                        "Please select one mode: test plan execution, saturation load test or result collection");
                return;
            }

            int modeCount = 0;
            if (commandLine.hasOption(CMD_OPT_MODE_EXECUTE))
                modeCount = modeCount + 1;
            if (commandLine.hasOption(CMD_OPT_MODE_COLLECT))
                modeCount = modeCount + 1;
            if (commandLine.hasOption(CMD_OPT_MODE_SATURATION_TEST))
                modeCount = modeCount + 1;

            if (modeCount > 1) {
                printHelp(commandLineOptions,
                        "Please select only one mode: test plan execution, saturation load test or result collection");
                return;
            }

            if (commandLine.hasOption(CMD_OPT_MODE_EXECUTE)
                    || commandLine.hasOption(CMD_OPT_MODE_SATURATION_TEST)) {
                Map<String, String> results = executeTestPlan(commandLineOptions, commandLine);
                if (results != null && !results.isEmpty()) {
                    System.out.println(
                            "Requested test plan successfully started on referenced hosts. The results contain the host as well as the result identifier:");
                    for (String hostName : results.keySet())
                        System.out.println("Host: " + hostName + ", Result Identifier: " + results.get(hostName));
                }
            } else if (commandLine.hasOption(CMD_OPT_MODE_COLLECT)) {

                Set<TSClientPlanExecutionResult> results = executeResultCollection(commandLineOptions, commandLine);
                for (TSClientPlanExecutionResult r : results)
                    System.out.println(r);
            }

        } catch (ParseException e) {
            System.out.println("Failed to parse command-line. Error: " + e.getMessage());
        }

    }

    ///////////////////////////////////////////////// TEST PLAN EXECUTION /////////////////////////////////////////////////

    /**
     * Executes the result collection
     * @param options
     * @param cmd
     * @return
     */
    protected Set<TSClientPlanExecutionResult> executeResultCollection(Options options, CommandLine cmd) {

        String ptestServerHosts[] = null;
        try {
            ptestServerHosts = extractStringList(cmd, CMD_OPT_PTEST_SERVER_HOSTS, CMD_OPT_PTEST_SERVER_HOSTS_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please provide a list of hosts running a ptest-server instance");
            return null;
        }
        if (ptestServerHosts == null || ptestServerHosts.length < 1) {
            printHelp(options, "Please provide a list of hosts running a ptest-server instance");
            return null;
        }
        if (ptestServerHosts.length > 1) {
            printHelp(options, "Please provide only one host for collecting results at command-line level");
            return null;
        }

        int ptestServerPort = -1;
        try {
            ptestServerPort = extractIntValue(cmd, CMD_OPT_PTEST_SERVER_PORT, CMD_OPT_PTEST_SERVER_PORT_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please provide port to be used for ptest-server communication");
            return null;
        }
        if (ptestServerPort < 0) {
            printHelp(options, "Please provide port to be used for ptest-server communication");
            return null;
        }

        String resultIdentifier = null;
        try {
            resultIdentifier = extractStringValue(cmd, CMD_OPT_RESULT_IDENTIFIER, CMD_OPT_RESULT_IDENTIFIER_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please provide a result identifier");
            return null;
        }

        Map<String, String> hostResultIdMapping = new HashMap<String, String>();
        hostResultIdMapping.put(ptestServerHosts[0], resultIdentifier);

        return collectTestplanResults(hostResultIdMapping, ptestServerPort);
    }

    /**
     * Implements the test plan execution incl. command-line options handling
     * @param commandLine
     */
    protected Map<String, String> executeTestPlan(Options options, CommandLine cmd) {

        // extract number of threads to start
        int threads = -1;
        try {
            threads = extractIntValue(cmd, CMD_OPT_THREADS, CMD_OPT_THREADS_SHORT);
        } catch (TSClientConfigurationException e) {
        }
        if (threads < 1) {
            printHelp(options, "Please provide a valid number of threads used for test plan execution");
            return null;
        }

        // extract recurrences information
        long recurrences = -1;
        try {
            recurrences = extractLongValue(cmd, CMD_OPT_RECURRENCES, CMD_OPT_RECURRENCES_SHORT);
        } catch (TSClientConfigurationException e) {
        }
        if (recurrences < 1) {
            printHelp(options, "Please provide a valid number of test plan recurrences");
            return null;
        }

        TSPlanRecurrenceType recurrenceType = null;
        try {
            recurrenceType = extractRecurrenceType(cmd, CMD_OPT_RECURRENCE_TYPE, CMD_OPT_RECURRENCE_TYPE_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please provide a valid recurrence type");
            return null;
        }
        if (recurrenceType == TSPlanRecurrenceType.UNKNOWN) {
            printHelp(options, "Please provide a valid recurrence type");
            return null;
        }

        String testPlan = null;
        try {
            testPlan = extractStringValue(cmd, CMD_OPT_TESTPLAN, CMD_OPT_TESTPLAN_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please reference a valid test plan");
            return null;
        }
        if (testPlan == null || testPlan.isEmpty()) {
            printHelp(options, "Please reference a valid test plan");
            return null;
        }

        String ptestServerHosts[] = null;
        try {
            ptestServerHosts = extractStringList(cmd, CMD_OPT_PTEST_SERVER_HOSTS, CMD_OPT_PTEST_SERVER_HOSTS_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please provide a list of hosts running a ptest-server instance");
            return null;
        }
        if (ptestServerHosts == null || ptestServerHosts.length < 1) {
            printHelp(options, "Please provide a list of hosts running a ptest-server instance");
            return null;
        }

        long ptestServerPort = -1;
        try {
            ptestServerPort = extractLongValue(cmd, CMD_OPT_PTEST_SERVER_PORT, CMD_OPT_PTEST_SERVER_PORT_SHORT);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Please provide port to be used for ptest-server communication");
            return null;
        }
        if (ptestServerPort < 0) {
            printHelp(options, "Please provide port to be used for ptest-server communication");
            return null;
        }

        String urlEncoding = cmd.getOptionValue(CMD_OPT_PTEST_SERVER_URL_ENCODING);
        if (urlEncoding == null || urlEncoding.isEmpty())
            urlEncoding = cmd.getOptionValue(CMD_OPT_PTEST_SERVER_URL_ENCODING_SHORT);
        if (urlEncoding == null || urlEncoding.isEmpty())
            urlEncoding = "UTF-8";

        String additionalPropertiesFile = cmd.getOptionValue(CMD_OPT_PTEST_SERVER_ADDITIONAL_PROPERTIES_FILE);
        if (additionalPropertiesFile == null || additionalPropertiesFile.isEmpty())
            additionalPropertiesFile = cmd.getOptionValue(CMD_OPT_PTEST_SERVER_ADDITIONAL_PROPERTIES_FILE_SHORT);
        Properties additionalProperties = null;
        if (additionalPropertiesFile != null && !additionalPropertiesFile.isEmpty()) {
            try {
                additionalProperties = extractAdditionalProperties(additionalPropertiesFile);
            } catch (TSClientConfigurationException e) {
                printHelp(options, "Please provide a valid path to a file containing additional properties");
                return null;
            }
        } else {
            additionalProperties = new Properties();
        }

        byte[] testplanContent = null;
        try {
            testplanContent = loadTestplan(testPlan);
        } catch (TSClientConfigurationException e) {
            printHelp(options, "Error received while attempting to read test plan file: " + e.getMessage());
            return null;
        }

        // TODO remove this as it is required for a company specific test! 
        String waitTime = null;
        try {
            waitTime = extractStringValue(cmd, CMD_OPT_SAT_TEST_WAIT_TIME, CMD_OPT_SAT_TEST_WAIT_TIME_SHORT);
        } catch (TSClientConfigurationException e) {
            waitTime = null;
            System.out.println("No wait time provided, using the one contained in tsclient.properties");
        }

        if (waitTime != null && !waitTime.isEmpty())
            additionalProperties.put("waitTime", waitTime);

        if (cmd.hasOption(CMD_OPT_MODE_EXECUTE)) {
            try {
                return executeTestPlan(ptestServerHosts, (int) ptestServerPort, threads, recurrences,
                        recurrenceType, testplanContent, additionalProperties, urlEncoding);
            } catch (TSClientConfigurationException e) {
                System.out.println("Error while configuring the client: " + e.getMessage());
            } catch (TSClientExecutionException e) {
                System.out.println("Error while executing the client: " + e.getMessage());
            }
        } else if (cmd.hasOption(CMD_OPT_MODE_SATURATION_TEST)) {

            long maxRuntime = -1;
            try {
                maxRuntime = extractLongValue(cmd, CMD_OPT_SAT_TEST_MAX_RUNTIME,
                        CMD_OPT_SAT_TEST_MAX_RUNTIME_SHORT);
            } catch (TSClientConfigurationException e) {
                printHelp(options, "Please provide a max. runtime allowed for test plan execution");
                return null;
            }
            if (maxRuntime < 0) {
                printHelp(options, "Please provide a max. runtime allowed for test plan execution");
                return null;
            }

            int threadIncrement = 1;
            try {
                threadIncrement = extractIntValue(cmd, CMD_OPT_SAT_TEST_THREAD_INCREMENT,
                        CMD_OPT_SAT_TEST_THREAD_INCREMENT_SHORT);
            } catch (TSClientConfigurationException e) {
                System.out.println("No or invalid value provided for '" + CMD_OPT_SAT_TEST_THREAD_INCREMENT + "/"
                        + CMD_OPT_SAT_TEST_THREAD_INCREMENT_SHORT + "'");
                threadIncrement = 1;
            }

            StringBuffer hn = new StringBuffer();
            for (int i = 0; i < ptestServerHosts.length; i++) {
                hn.append(ptestServerHosts[i]);
                if (i < ptestServerHosts.length - 1)
                    hn.append(", ");
            }

            System.out.println("ptest-client");
            System.out.println("test mode:               saturation test");
            System.out.println("ptest-server instances:  " + hn.toString());
            System.out.println("ptest-server port:       " + ptestServerPort);
            System.out.println("max. threads:            " + threads);
            System.out.println("thread increment:        " + threadIncrement);
            System.out.println("allowed max. runtime:    " + maxRuntime);
            System.out.println("recurrences per thread:  " + recurrences);
            System.out.println("recurrence type:         " + recurrenceType);
            System.out.println("test plan:               " + testPlan);
            System.out.println("url encoding:            " + urlEncoding);

            long maxThreads = 0;
            boolean interrupt = false;

            try {

                if (threadIncrement > threads) {
                    threadIncrement = threads;
                    System.out.println("\nthread increment refixed to " + threadIncrement
                            + " since it was greater than num. of threads " + threads);
                }

                System.out.println("\n\n");
                System.out.println("Saturation test execution:\n");

                for (int i = threadIncrement; i <= threads; i = i + threadIncrement) {

                    System.out.println("Hosts: " + ptestServerHosts.length + ", Threads: " + i + ", Recurrences: "
                            + recurrences + ", Recurrence Type: " + recurrenceType + ", Allowed Max. Runtime: "
                            + maxRuntime);
                    System.out.println("Result identifiers (per host):");

                    Map<String, String> resultIdentifiers = executeTestPlan(ptestServerHosts, (int) ptestServerPort,
                            i, recurrences, recurrenceType, testplanContent, additionalProperties, urlEncoding);
                    for (String hostName : resultIdentifiers.keySet())
                        System.out.println("\t\t" + hostName + ": " + resultIdentifiers.get(hostName));

                    // TODO problem with TIMES!!
                    long waitMillis = 0;

                    switch (recurrenceType) {
                    case TIMES: {
                        waitMillis = recurrences * 500;
                    }
                    case MILLIS: {
                        waitMillis = recurrences;
                        break;
                    }
                    case SECONDS: {
                        waitMillis = recurrences * 1000;
                        break;
                    }
                    case MINUTES: {
                        waitMillis = recurrences * 1000 * 60;
                        break;
                    }
                    case HOURS: {
                        waitMillis = recurrences * 1000 * 60 * 60;
                        break;
                    }
                    case DAYS: {
                        waitMillis = recurrences * 1000 * 60 * 60 * 24;
                        break;
                    }
                    }

                    waitMillis = waitMillis + 5000; // add 5sec to be sure :-)

                    System.out.println(
                            "Waiting for approx. " + (waitMillis / 1000) + " seconds before fetching the results");

                    try {
                        Thread.sleep(waitMillis);
                    } catch (InterruptedException e) {
                        System.out.println("Interrupted while waiting. " + e.getMessage());
                    }

                    Set<TSClientPlanExecutionResult> results = collectTestplanResults(resultIdentifiers,
                            (int) ptestServerPort);
                    System.out.println("Results:");
                    for (TSClientPlanExecutionResult r : results)
                        System.out.println("\t\tHost: " + r.getHostName() + ", min: " + r.getSingleMinDuration()
                                + "ms, max: " + r.getSingleMaxDuration() + "ms, median: "
                                + r.getAverageDurationMedian() + "ms (surpasses max runtime for median: "
                                + (r.getAverageDurationMedian() >= maxRuntime) + ")");

                    for (TSClientPlanExecutionResult r : results) {
                        if (r.getAverageDurationMedian() >= maxRuntime) {
                            maxThreads = i;
                            interrupt = true;
                            break;
                        }
                    }

                    if (interrupt)
                        break;
                }

                System.out.println("Saturation test finished. Max. number of threads on " + ptestServerHosts.length
                        + " hosts the destinations are capable to serve: " + maxThreads);
            } catch (TSClientConfigurationException e) {
                System.out.println("Error while configuring the client: " + e.getMessage());
            } catch (TSClientExecutionException e) {
                System.out.println("Error while executing the client: " + e.getMessage());
            }

        }

        return new HashMap<String, String>();
    }

    ///////////////////////////////////////////////// TEST PLAN RESULT COLLECTION /////////////////////////////////////////////////

    ///////////////////////////////////////////////// EXECUTE HTTP CALL /////////////////////////////////////////////////

    /**
     * Executes the referenced test plan for all given host names. The result contains a mapping from a host name to the returned result identifier
     * of that ptest-server instance 
     * @param hostNames
     * @param port
     * @param threads
     * @param recurrences
     * @param recurrenceType
     * @param testplan
     * @param additionalParameters
     * @param urlEncoding
     * @return
     * @throws TSClientConfigurationException
     * @throws TSClientExecutionException
     */
    protected Map<String, String> executeTestPlan(String[] hostNames, int port, long threads, long recurrences,
            TSPlanRecurrenceType recurrenceType, byte[] testplan, Properties additionalParameters,
            String urlEncoding) throws TSClientConfigurationException, TSClientExecutionException {

        // the ptest-server understands http get, thus we use it TODO refactor to post and send testplan as well and do not reference it anymore!
        StringBuffer buffer = new StringBuffer("/?");
        buffer.append(REQUEST_PARAMETER_EXECUTE).append("=1");
        buffer.append("&").append(REQUEST_PARAMETER_RECURRENCES).append("=").append(recurrences);
        buffer.append("&").append(REQUEST_PARAMETER_RECURRENCE_TYPE).append("=").append(recurrenceType.toString());
        buffer.append("&").append(REQUEST_PARAMETER_THREADS).append("=").append(threads);

        try {
            if (additionalParameters != null && !additionalParameters.isEmpty()) {
                for (Object key : additionalParameters.keySet()) {
                    String value = (String) additionalParameters.get(key);
                    buffer.append("&").append(key).append("=").append(URLEncoder.encode(value, urlEncoding));
                }
            }
        } catch (UnsupportedEncodingException e) {
            throw new TSClientConfigurationException(
                    "Unsupported encoding type: " + urlEncoding + ". Error: " + e.getMessage());
        }

        StringBuffer hn = new StringBuffer();
        for (int i = 0; i < hostNames.length; i++) {
            hn.append(hostNames[i]);
            if (i < hostNames.length - 1)
                hn.append(", ");
        }

        System.out.println("Execute testplan:");
        System.out.println("\thostNames: " + hn.toString());
        System.out.println("\tport: " + port);
        System.out.println("\tthreads: " + threads);
        System.out.println("\trecurrences: " + recurrences);
        System.out.println("\trecurrenceType: " + recurrenceType);
        System.out.println("\turl enc: " + urlEncoding);
        System.out.println("\n\turi: " + buffer.toString());

        TSClientPlanExecCallable[] testplanCallables = new TSClientPlanExecCallable[hostNames.length];
        for (int i = 0; i < hostNames.length; i++) {
            testplanCallables[i] = new TSClientPlanExecCallable(hostNames[i], port, buffer.toString(), testplan);
        }

        ExecutorService executorService = Executors.newFixedThreadPool(hostNames.length);
        List<Future<NameValuePair>> executionResults = new ArrayList<Future<NameValuePair>>();
        try {
            executionResults = executorService.invokeAll(Arrays.asList(testplanCallables));
        } catch (InterruptedException e) {
            System.out.println("Test execution interrupted: " + e.getMessage());
        }

        // collect results from callables
        Map<String, String> result = new HashMap<String, String>();
        for (Future<NameValuePair> r : executionResults) {
            try {
                NameValuePair nvp = r.get();
                result.put(nvp.getName(), nvp.getValue());
            } catch (InterruptedException e) {
                System.out.println("Interrupted while waiting for results. Error: " + e.getMessage());
            } catch (ExecutionException e) {
                System.out.println("Interrupted while waiting for results. Error: " + e.getMessage());
            }
        }

        return result;
    }

    /**
     * Collects results of previously started tests
     * @param hostResultIdentifierMapping
     * @param port
     * @return
     */
    protected Set<TSClientPlanExecutionResult> collectTestplanResults(
            Map<String, String> hostResultIdentifierMapping, int port) {

        TSClientPlanResultCollectCallable[] callables = new TSClientPlanResultCollectCallable[hostResultIdentifierMapping
                .size()];
        int count = 0;
        String uri = null;
        for (String host : hostResultIdentifierMapping.keySet()) {
            uri = "/?" + REQUEST_PARAMETER_COLLECT + "=1&" + REQUEST_PARAMETER_RESULT_IDENTIFIER + "="
                    + hostResultIdentifierMapping.get(host);
            callables[count] = new TSClientPlanResultCollectCallable(host, port, uri);
            count = count + 1;
        }

        ExecutorService executorService = Executors.newFixedThreadPool(hostResultIdentifierMapping.size());
        List<Future<TSClientPlanExecutionResult>> collectedResults = new ArrayList<Future<TSClientPlanExecutionResult>>();
        try {
            collectedResults = executorService.invokeAll(Arrays.asList(callables));
        } catch (InterruptedException e) {
            System.out.println("Test execution result collection interrupted: " + e.getMessage());
        }

        Set<TSClientPlanExecutionResult> result = new HashSet<TSClientPlanExecutionResult>();
        for (Future<TSClientPlanExecutionResult> r : collectedResults) {
            try {
                result.add(r.get());
            } catch (InterruptedException e) {
                System.out.println("Interrupted while collection results: " + e.getMessage());
            } catch (ExecutionException e) {
                System.out.println("Interrupted while collection results: " + e.getMessage());
            }
        }

        return result;
    }

    /**
     * Reads in a test plan
     * @param fileName
     * @return
     * @throws TSClientConfigurationException
     */
    protected byte[] loadTestplan(String fileName) throws TSClientConfigurationException {
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(fileName);
            return IOUtils.toByteArray(fin);
        } catch (IOException e) {
            throw new TSClientConfigurationException(
                    "Failed to read test plan file '" + fileName + "'. Error: " + e.getMessage());
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (IOException e) {
                    System.out
                            .println("Failed to close test plan file '" + fileName + "'. Error: " + e.getMessage());
                }
            }
        }

    }

    ///////////////////////////////////////////////// COMMAND-LINE PREPARATION /////////////////////////////////////////////////

    /**
     * Returns the command-line options configured for the client
     * @return
     */
    protected Options getTSClientCommandLineOptions() {
        Options options = new Options();
        options.addOption(CMD_OPT_MODE_EXECUTE, false, "Executes a test plan on the ptest-server instance(s)");
        options.addOption(CMD_OPT_MODE_COLLECT, false, "Collects the results from the ptest-server instance(s)");
        options.addOption(CMD_OPT_MODE_SATURATION_TEST, false,
                "Executes a performance test finding the saturation load");
        options.addOption(CMD_OPT_THREADS_SHORT, CMD_OPT_THREADS, true,
                "Number of threads used for executing the test case. In case of the saturation load this will be used as max, starting with one thread");
        options.addOption(CMD_OPT_RECURRENCES_SHORT, CMD_OPT_RECURRENCES, true, "Number of tesplan recurrences");
        options.addOption(CMD_OPT_RECURRENCE_TYPE_SHORT, CMD_OPT_RECURRENCE_TYPE, true,
                "Recurrence type (TIMES, MILLIS, SECONDS, MINUTES, HOURS, DAYS)");
        options.addOption(CMD_OPT_TESTPLAN_SHORT, CMD_OPT_TESTPLAN, true,
                "References the file containing the test plan definition");
        options.addOption(CMD_OPT_PTEST_SERVER_HOSTS_SHORT, CMD_OPT_PTEST_SERVER_HOSTS, true,
                "Comma-separated list of hosts running an available ptest-server instance");
        options.addOption(CMD_OPT_PTEST_SERVER_PORT_SHORT, CMD_OPT_PTEST_SERVER_PORT, true,
                "Names the port to use for communication with the ptest-server instances");
        options.addOption(CMD_OPT_PTEST_SERVER_URL_ENCODING_SHORT, CMD_OPT_PTEST_SERVER_URL_ENCODING, true,
                "Encoding to be used for url parameters");
        options.addOption(CMD_OPT_PTEST_SERVER_ADDITIONAL_PROPERTIES_FILE_SHORT,
                CMD_OPT_PTEST_SERVER_ADDITIONAL_PROPERTIES_FILE, true,
                "Path to file which contains additional key/value pairs to be forwared to the ptest-server");
        options.addOption(CMD_OPT_RESULT_IDENTIFIER_SHORT, CMD_OPT_RESULT_IDENTIFIER, true,
                "Key to identify results on the ptest-server (to be used with 'collect' option only)");
        options.addOption(CMD_OPT_SAT_TEST_MAX_RUNTIME_SHORT, CMD_OPT_SAT_TEST_MAX_RUNTIME, true,
                "Max. average runtime accepted for saturation test before quitting test");
        options.addOption(CMD_OPT_SAT_TEST_THREAD_INCREMENT_SHORT, CMD_OPT_SAT_TEST_THREAD_INCREMENT, true,
                "Number to increase the running threads by during saturation test (default: 1). The value will be used as initial value.");
        options.addOption(CMD_OPT_SAT_TEST_WAIT_TIME_SHORT, CMD_OPT_SAT_TEST_WAIT_TIME, true,
                "Defines the value for the waitTime parameter being provided as global variable to the test plan (default: 0)");
        options.addOption("ri", true, "Response identifier used by the ptest-server to store results");
        return options;
    }

}