eu.stratosphere.client.CliFrontend.java Source code

Java tutorial

Introduction

Here is the source code for eu.stratosphere.client.CliFrontend.java

Source

/***********************************************************************************************************************
 * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu)
 *
 * 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.
 **********************************************************************************************************************/

package eu.stratosphere.client;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.commons.io.FileUtils;

import eu.stratosphere.api.common.JobExecutionResult;
import eu.stratosphere.api.common.accumulators.AccumulatorHelper;
import eu.stratosphere.client.program.Client;
import eu.stratosphere.client.program.PackagedProgram;
import eu.stratosphere.client.program.ProgramInvocationException;
import eu.stratosphere.configuration.ConfigConstants;
import eu.stratosphere.configuration.Configuration;
import eu.stratosphere.configuration.GlobalConfiguration;
import eu.stratosphere.nephele.event.job.RecentJobEvent;
import eu.stratosphere.nephele.ipc.RPC;
import eu.stratosphere.nephele.jobgraph.JobID;
import eu.stratosphere.nephele.jobgraph.JobStatus;
import eu.stratosphere.nephele.net.NetUtils;
import eu.stratosphere.nephele.protocols.ExtendedManagementProtocol;
import eu.stratosphere.util.StringUtils;

/**
 * Implementation of a simple command line fronted for executing programs.
 */
public class CliFrontend {

    //actions
    private static final String ACTION_RUN = "run";
    private static final String ACTION_INFO = "info";
    private static final String ACTION_LIST = "list";
    private static final String ACTION_CANCEL = "cancel";

    // general options
    private static final Option HELP_OPTION = new Option("h", "help", false, "Show the help for the CLI Frontend.");
    private static final Option VERBOSE_OPTION = new Option("v", "verbose", false,
            "Print more detailed error messages.");

    // program (jar file) specific options
    private static final Option JAR_OPTION = new Option("j", "jarfile", true, "Stratosphere program JAR file.");
    private static final Option CLASS_OPTION = new Option("c", "class", true,
            "Class with the program entry point (\"main\" method or \"getPlan()\" method. Only needed if the JAR file does not specify the class in its manifest.");
    private static final Option PARALLELISM_OPTION = new Option("p", "parallelism", true,
            "The parallelism with which to run the program. Optional flag to override the default value specified in the configuration.");
    private static final Option ARGS_OPTION = new Option("a", "arguments", true,
            "Program arguments. Arguments can also be added without -a, simply as trailing parameters.");

    private static final Option ADDRESS_OPTION = new Option("m", "jobmanager", true,
            "Address of the JobManager (master) to which to connect. Use this flag to connect to a different JobManager than the one specified in the configuration.");

    // info specific options
    private static final Option DESCR_OPTION = new Option("d", "description", false,
            "Show description of expected program arguments");
    private static final Option PLAN_OPTION = new Option("e", "executionplan", false,
            "Show optimized execution plan of the program (JSON)");

    // list specific options
    private static final Option RUNNING_OPTION = new Option("r", "running", false,
            "Show running programs and their JobIDs");
    private static final Option SCHEDULED_OPTION = new Option("s", "scheduled", false,
            "Show scheduled prorgrams and their JobIDs");

    // canceling
    private static final Option ID_OPTION = new Option("i", "jobid", true, "JobID of program to cancel");

    static {
        initOptions();
    }

    // general options
    private static final Options GENRAL_OPTIONS = createGeneralOptions();

    // action options all include the general options
    private static final Options RUN_OPTIONS = getRunOptions(createGeneralOptions());
    private static final Options INFO_OPTIONS = getInfoOptions(createGeneralOptions());
    private static final Options LIST_OPTIONS = getListOptions(createGeneralOptions());
    private static final Options CANCEL_OPTIONS = getCancelOptions(createGeneralOptions());

    // config dir parameters
    private static final String ENV_CONFIG_DIRECTORY = "STRATOSPHERE_CONF_DIR";
    private static final String CONFIG_DIRECTORY_FALLBACK_1 = "../conf";
    private static final String CONFIG_DIRECTORY_FALLBACK_2 = "conf";

    public static final String JOBMANAGER_ADDRESS_FILE = ".yarn-jobmanager";

    private CommandLineParser parser;

    private boolean verbose;
    private boolean printHelp;

    private boolean globalConfigurationLoaded;

    /**
     * Initializes the class
     */
    public CliFrontend() {
        parser = new PosixParser();
    }

    // --------------------------------------------------------------------------------------------
    //  Setup of options
    // --------------------------------------------------------------------------------------------

    private static void initOptions() {
        HELP_OPTION.setRequired(false);
        VERBOSE_OPTION.setRequired(false);

        JAR_OPTION.setRequired(false);
        JAR_OPTION.setArgName("jarfile");

        CLASS_OPTION.setRequired(false);
        CLASS_OPTION.setArgName("classname");

        ADDRESS_OPTION.setRequired(false);
        ADDRESS_OPTION.setArgName("host:port");

        PARALLELISM_OPTION.setRequired(false);
        PARALLELISM_OPTION.setArgName("parallelism");

        ARGS_OPTION.setRequired(false);
        ARGS_OPTION.setArgName("programArgs");
        ARGS_OPTION.setArgs(Option.UNLIMITED_VALUES);

        PLAN_OPTION.setRequired(false);
        DESCR_OPTION.setRequired(false);

        RUNNING_OPTION.setRequired(false);
        SCHEDULED_OPTION.setRequired(false);

        ID_OPTION.setRequired(false);
        ID_OPTION.setArgName("jobID");
    }

    static Options createGeneralOptions() {
        Options options = new Options();
        options.addOption(HELP_OPTION);
        options.addOption(VERBOSE_OPTION);

        return options;
    }

    // gets the program options with the old flags for jar file and arguments
    static Options getProgramSpecificOptions(Options options) {
        options.addOption(JAR_OPTION);
        options.addOption(CLASS_OPTION);
        options.addOption(PARALLELISM_OPTION);
        options.addOption(ARGS_OPTION);
        return options;
    }

    // gets the program options without the old flags for jar file and arguments
    static Options getProgramSpecificOptionsWithoutDeprecatedOptions(Options options) {
        options.addOption(CLASS_OPTION);
        options.addOption(PARALLELISM_OPTION);
        return options;
    }

    /**
     * Builds command line options for the run action.
     * 
     * @return Command line options for the run action.
     */
    static Options getRunOptions(Options options) {
        Options o = getProgramSpecificOptions(options);
        return getJobManagerAddressOption(o);
    }

    static Options getRunOptionsWithoutDeprecatedOptions(Options options) {
        Options o = getProgramSpecificOptionsWithoutDeprecatedOptions(options);
        return getJobManagerAddressOption(o);
    }

    static Options getJobManagerAddressOption(Options options) {
        options.addOption(ADDRESS_OPTION);
        return options;
    }

    /**
     * Builds command line options for the info action.
     * 
     * @return Command line options for the info action.
     */
    static Options getInfoOptions(Options options) {
        options = getProgramSpecificOptions(options);
        options = getJobManagerAddressOption(options);
        options.addOption(DESCR_OPTION);
        options.addOption(PLAN_OPTION);
        return options;
    }

    static Options getInfoOptionsWithoutDeprecatedOptions(Options options) {
        options = getProgramSpecificOptionsWithoutDeprecatedOptions(options);
        options = getJobManagerAddressOption(options);
        options.addOption(DESCR_OPTION);
        options.addOption(PLAN_OPTION);
        return options;
    }

    /**
     * Builds command line options for the list action.
     * 
     * @return Command line options for the list action.
     */
    static Options getListOptions(Options options) {
        options.addOption(RUNNING_OPTION);
        options.addOption(SCHEDULED_OPTION);
        options = getJobManagerAddressOption(options);
        return options;
    }

    /**
     * Builds command line options for the cancel action.
     * 
     * @return Command line options for the cancel action.
     */
    static Options getCancelOptions(Options options) {
        options.addOption(ID_OPTION);
        options = getJobManagerAddressOption(options);
        return options;
    }

    // --------------------------------------------------------------------------------------------
    //  Execute Actions
    // --------------------------------------------------------------------------------------------

    /**
     * Executions the run action.
     * 
     * @param args Command line arguments for the run action.
     */
    protected int run(String[] args) {
        // Parse command line options
        CommandLine line;
        try {
            line = parser.parse(RUN_OPTIONS, args, false);
            evaluateGeneralOptions(line);
        } catch (MissingOptionException e) {
            System.out.println(e.getMessage());
            printHelpForRun();
            return 1;
        } catch (UnrecognizedOptionException e) {
            System.out.println(e.getMessage());
            printHelpForRun();
            return 2;
        } catch (Exception e) {
            return handleError(e);
        }

        // ------------ check for help first --------------

        if (printHelp) {
            printHelpForRun();
            return 0;
        }

        try {
            PackagedProgram program = buildProgram(line);
            if (program == null) {
                printHelpForRun();
                return 1;
            }

            Client client = getClient(line);
            if (client == null) {
                printHelpForRun();
                return 1;
            }

            int parallelism = -1;
            if (line.hasOption(PARALLELISM_OPTION.getOpt())) {
                String parString = line.getOptionValue(PARALLELISM_OPTION.getOpt());
                try {
                    parallelism = Integer.parseInt(parString);
                } catch (NumberFormatException e) {
                    System.out.println("The value " + parString + " is invalid for the degree of parallelism.");
                    printHelpForRun();
                    return 1;
                }

                if (parallelism <= 0) {
                    System.out.println(
                            "Invalid value for the degree-of-parallelism. Parallelism must be greater than zero.");
                    printHelpForRun();
                    return 1;
                }
            }

            return executeProgram(program, client, parallelism);
        } catch (Throwable t) {
            return handleError(t);
        }
    }

    // --------------------------------------------------------------------------------------------

    protected int executeProgram(PackagedProgram program, Client client, int parallelism) {
        JobExecutionResult execResult;
        try {
            client.setPrintStatusDuringExecution(true);
            execResult = client.run(program, parallelism, true);
        } catch (ProgramInvocationException e) {
            return handleError(e);
        } finally {
            program.deleteExtractedLibraries();
        }

        // we come here after the job has finished
        if (execResult != null) {
            System.out.println("Job Runtime: " + execResult.getNetRuntime());
            Map<String, Object> accumulatorsResult = execResult.getAllAccumulatorResults();
            if (accumulatorsResult.size() > 0) {
                System.out.println("Accumulator Results: ");
                System.out.println(AccumulatorHelper.getResultsFormated(accumulatorsResult));
            }
        }
        return 0;
    }

    // --------------------------------------------------------------------------------------------

    /**
     * Executes the info action.
     * 
     * @param args Command line arguments for the info action. 
     */
    protected int info(String[] args) {
        // Parse command line options
        CommandLine line;
        try {
            line = parser.parse(INFO_OPTIONS, args, false);
            evaluateGeneralOptions(line);
        } catch (MissingOptionException e) {
            System.out.println(e.getMessage());
            printHelpForInfo();
            return 1;
        } catch (UnrecognizedOptionException e) {
            System.out.println(e.getMessage());
            printHelpForInfo();
            return 2;
        } catch (Exception e) {
            return handleError(e);
        }

        if (printHelp) {
            printHelpForInfo();
            return 0;
        }

        boolean description = line.hasOption(DESCR_OPTION.getOpt());
        boolean plan = line.hasOption(PLAN_OPTION.getOpt());

        if (!description && !plan) {
            System.out.println("ERROR: Specify the information to display.");
            printHelpForInfo();
            return 1;
        }

        // -------- build the packaged program -------------

        PackagedProgram program;
        try {
            program = buildProgram(line);
        } catch (Throwable t) {
            return handleError(t);
        }

        if (program == null) {
            printHelpForInfo();
            return 1;
        }

        int parallelism = -1;
        if (line.hasOption(PARALLELISM_OPTION.getOpt())) {
            String parString = line.getOptionValue(PARALLELISM_OPTION.getOpt());
            try {
                parallelism = Integer.parseInt(parString);
            } catch (NumberFormatException e) {
                System.out.println("The value " + parString + " is invalid for the degree of parallelism.");
                printHelpForRun();
                return 1;
            }

            if (parallelism <= 0) {
                System.out.println(
                        "Invalid value for the degree-of-parallelism. Parallelism must be greater than zero.");
                printHelpForRun();
                return 1;
            }
        }

        try {
            // check for description request
            if (description) {
                String descr = program.getDescription();

                if (descr != null) {
                    System.out.println("-------------------- Program Description ---------------------");
                    System.out.println(descr);
                    System.out.println("--------------------------------------------------------------");
                } else {
                    System.out.println("No description available for this program.");
                }
            }

            // check for json plan request
            if (plan) {
                Client client = getClient(line);
                String jsonPlan = client.getOptimizedPlanAsJson(program, parallelism);

                if (jsonPlan != null) {
                    System.out.println("----------------------- Execution Plan -----------------------");
                    System.out.println(jsonPlan);
                    System.out.println("--------------------------------------------------------------");
                } else {
                    System.out.println("JSON plan could not be compiled.");
                }
            }

            return 0;
        } catch (Throwable t) {
            return handleError(t);
        } finally {
            program.deleteExtractedLibraries();
        }
    }

    /**
     * Executes the list action.
     * 
     * @param args Command line arguments for the list action.
     */
    protected int list(String[] args) {
        // Parse command line options
        CommandLine line;
        try {
            line = parser.parse(LIST_OPTIONS, args, false);
        } catch (MissingOptionException e) {
            System.out.println(e.getMessage());
            printHelpForList();
            return 1;
        } catch (UnrecognizedOptionException e) {
            System.out.println(e.getMessage());
            printHelpForList();
            return 2;
        } catch (Exception e) {
            return handleError(e);
        }

        if (printHelp) {
            printHelpForList();
            return 0;
        }

        // get list options
        boolean running = line.hasOption(RUNNING_OPTION.getOpt());
        boolean scheduled = line.hasOption(SCHEDULED_OPTION.getOpt());

        if (!running && !scheduled) {
            System.out.println("Error: Specify the status of the jobs to list.");
            printHelpForList();
            return 1;
        }

        ExtendedManagementProtocol jmConn = null;
        try {
            jmConn = getJobManagerConnection(line);
            if (jmConn == null) {
                printHelpForList();
                return 1;
            }

            List<RecentJobEvent> recentJobs = jmConn.getRecentJobs();

            ArrayList<RecentJobEvent> runningJobs = null;
            ArrayList<RecentJobEvent> scheduledJobs = null;
            if (running) {
                runningJobs = new ArrayList<RecentJobEvent>();
            }
            if (scheduled) {
                scheduledJobs = new ArrayList<RecentJobEvent>();
            }

            for (RecentJobEvent rje : recentJobs) {

                if (running && rje.getJobStatus().equals(JobStatus.RUNNING)) {
                    runningJobs.add(rje);
                }
                if (scheduled && rje.getJobStatus().equals(JobStatus.SCHEDULED)) {
                    scheduledJobs.add(rje);
                }
            }

            SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
            Comparator<RecentJobEvent> njec = new Comparator<RecentJobEvent>() {

                @Override
                public int compare(RecentJobEvent o1, RecentJobEvent o2) {
                    return (int) (o1.getTimestamp() - o2.getTimestamp());
                }
            };

            if (running) {
                if (runningJobs.size() == 0) {
                    System.out.println("No running jobs.");
                } else {
                    Collections.sort(runningJobs, njec);

                    System.out.println("------------------------ Running Jobs ------------------------");
                    for (RecentJobEvent je : runningJobs) {
                        System.out.println(df.format(new Date(je.getTimestamp())) + " : " + je.getJobID().toString()
                                + " : " + je.getJobName());
                    }
                    System.out.println("--------------------------------------------------------------");
                }
            }
            if (scheduled) {
                if (scheduledJobs.size() == 0) {
                    System.out.println("No scheduled jobs.");
                } else {
                    Collections.sort(scheduledJobs, njec);

                    System.out.println("----------------------- Scheduled Jobs -----------------------");
                    for (RecentJobEvent je : scheduledJobs) {
                        System.out.println(df.format(new Date(je.getTimestamp())) + " : " + je.getJobID().toString()
                                + " : " + je.getJobName());
                    }
                    System.out.println("--------------------------------------------------------------");
                }
            }
            return 0;
        } catch (Throwable t) {
            return handleError(t);
        } finally {
            if (jmConn != null) {
                try {
                    RPC.stopProxy(jmConn);
                } catch (Throwable t) {
                    System.out.println("Could not cleanly shut down connection from compiler to job manager");
                }
            }
            jmConn = null;
        }

    }

    /**
     * Executes the cancel action.
     * 
     * @param args Command line arguments for the cancel action.
     */
    protected int cancel(String[] args) {
        // Parse command line options
        CommandLine line;
        try {
            line = parser.parse(CANCEL_OPTIONS, args, false);
        } catch (MissingOptionException e) {
            System.out.println(e.getMessage());
            printHelpForCancel();
            return 1;
        } catch (UnrecognizedOptionException e) {
            System.out.println(e.getMessage());
            printHelpForCancel();
            return 2;
        } catch (Exception e) {
            return handleError(e);
        }

        if (printHelp) {
            printHelpForCancel();
            return 0;
        }

        JobID jobId;

        if (line.hasOption(ID_OPTION.getOpt())) {
            String jobIdString = line.getOptionValue(ID_OPTION.getOpt());
            try {
                jobId = new JobID(StringUtils.hexStringToByte(jobIdString));
            } catch (Exception e) {
                System.out.println("Error: The value for the Job ID is not a valid ID.");
                printHelpForCancel();
                return 1;
            }
        } else {
            System.out.println("Error: Specify a Job ID to cancel a job.");
            printHelpForCancel();
            return 1;
        }

        ExtendedManagementProtocol jmConn = null;
        try {
            jmConn = getJobManagerConnection(line);
            if (jmConn == null) {
                printHelpForCancel();
                return 1;
            }

            jmConn.cancelJob(jobId);
            return 0;
        } catch (Throwable t) {
            return handleError(t);
        } finally {
            if (jmConn != null) {
                try {
                    RPC.stopProxy(jmConn);
                } catch (Throwable t) {
                    System.out.println("Warning: Could not cleanly shut down connection to the JobManager.");
                }
            }
            jmConn = null;
        }
    }

    /**
     * @param line
     * 
     * @return Either a PackagedProgram (upon success), or null;
     */
    protected PackagedProgram buildProgram(CommandLine line) {
        String[] programArgs = line.hasOption(ARGS_OPTION.getOpt()) ? line.getOptionValues(ARGS_OPTION.getOpt())
                : line.getArgs();

        // take the jar file from the option, or as the first trailing parameter (if available)
        String jarFilePath = null;
        if (line.hasOption(JAR_OPTION.getOpt())) {
            jarFilePath = line.getOptionValue(JAR_OPTION.getOpt());
        } else if (programArgs.length > 0) {
            jarFilePath = programArgs[0];
            programArgs = Arrays.copyOfRange(programArgs, 1, programArgs.length);
        } else {
            System.out.println("Error: Jar file is not set.");
            return null;
        }

        File jarFile = new File(jarFilePath);

        // Check if JAR file exists
        if (!jarFile.exists()) {
            System.out.println("Error: Jar file does not exist.");
            return null;
        } else if (!jarFile.isFile()) {
            System.out.println("Error: Jar file is not a file.");
            return null;
        }

        // Get assembler class
        String entryPointClass = line.hasOption(CLASS_OPTION.getOpt()) ? line.getOptionValue(CLASS_OPTION.getOpt())
                : null;

        try {
            return entryPointClass == null ? new PackagedProgram(jarFile, programArgs)
                    : new PackagedProgram(jarFile, entryPointClass, programArgs);
        } catch (ProgramInvocationException e) {
            handleError(e);
            return null;
        }
    }

    protected InetSocketAddress getJobManagerAddress(CommandLine line) throws IOException {
        Configuration configuration = getGlobalConfiguration();

        // first, check if the address comes from the command line option
        if (line.hasOption(ADDRESS_OPTION.getOpt())) {
            try {
                String address = line.getOptionValue(ADDRESS_OPTION.getOpt());
                return RemoteExecutor.getInetFromHostport(address);
            } catch (Exception e) {
                System.out.println("Error: The JobManager address has an invalid format. " + e.getMessage());
                return null;
            }
        } else {
            // second, search for a .yarn-jobmanager file
            String loc = getConfigurationDirectory();
            File jmAddressFile = new File(loc + '/' + JOBMANAGER_ADDRESS_FILE);

            if (jmAddressFile.exists()) {
                try {
                    String address = FileUtils.readFileToString(jmAddressFile).trim();
                    System.out.println("Found a " + JOBMANAGER_ADDRESS_FILE + " file, using \"" + address
                            + "\" to connect to the JobManager");

                    return RemoteExecutor.getInetFromHostport(address);
                } catch (Exception e) {
                    System.out.println("Found a " + JOBMANAGER_ADDRESS_FILE
                            + " file, but could not read the JobManager address from the file. " + e.getMessage());
                    return null;
                }
            } else {
                // regular config file gives the address
                String jobManagerAddress = configuration.getString(ConfigConstants.JOB_MANAGER_IPC_ADDRESS_KEY,
                        null);

                // verify that there is a jobmanager address and port in the configuration
                if (jobManagerAddress == null) {
                    System.out.println("Error: Found no configuration in the config directory '"
                            + getConfigurationDirectory() + "' that specifies the JobManager address.");
                    return null;
                }

                int jobManagerPort;
                try {
                    jobManagerPort = configuration.getInteger(ConfigConstants.JOB_MANAGER_IPC_PORT_KEY, -1);
                } catch (NumberFormatException e) {
                    System.out.println("Invalid value for the JobManager IPC port ("
                            + ConfigConstants.JOB_MANAGER_IPC_PORT_KEY + ") in the configuration.");
                    return null;
                }

                if (jobManagerPort == -1) {
                    System.out.println("Error: Found no configuration in the config directory '"
                            + getConfigurationDirectory() + "' that specifies the JobManager port.");
                    return null;
                }

                return new InetSocketAddress(jobManagerAddress, jobManagerPort);
            }
        }
    }

    protected ExtendedManagementProtocol getJobManagerConnection(CommandLine line) throws IOException {
        InetSocketAddress jobManagerAddress = getJobManagerAddress(line);
        if (jobManagerAddress == null) {
            return null;
        }

        String address = jobManagerAddress.getAddress().getHostAddress();
        int port = jobManagerAddress.getPort();

        return RPC.getProxy(ExtendedManagementProtocol.class, new InetSocketAddress(address, port),
                NetUtils.getSocketFactory());
    }

    protected String getConfigurationDirectory() {
        String location = null;
        if (System.getenv(ENV_CONFIG_DIRECTORY) != null) {
            location = System.getenv(ENV_CONFIG_DIRECTORY);
        } else if (new File(CONFIG_DIRECTORY_FALLBACK_1).exists()) {
            location = CONFIG_DIRECTORY_FALLBACK_1;
        } else if (new File(CONFIG_DIRECTORY_FALLBACK_2).exists()) {
            location = CONFIG_DIRECTORY_FALLBACK_2;
        } else {
            throw new RuntimeException("The configuration directory was not found. Please configure the '"
                    + ENV_CONFIG_DIRECTORY + "' environment variable properly.");
        }
        return location;
    }

    /**
     * Reads configuration settings. The default path can be overridden
     * by setting the ENV variable "STRATOSPHERE_CONF_DIR".
     * 
     * @return Stratosphere's global configuration
     */
    protected Configuration getGlobalConfiguration() {
        if (!globalConfigurationLoaded) {
            String location = getConfigurationDirectory();
            GlobalConfiguration.loadConfiguration(location);
            globalConfigurationLoaded = true;
        }
        return GlobalConfiguration.getConfiguration();
    }

    protected Client getClient(CommandLine line) throws IOException {
        return new Client(getJobManagerAddress(line), getGlobalConfiguration());
    }

    /**
     * Prints the help for the client.
     * 
     * @param options A map with options for actions. 
     */
    private void printHelp() {
        System.out.println("./stratosphere <ACTION> [GENERAL_OPTIONS] [ARGUMENTS]");

        HelpFormatter formatter = new HelpFormatter();
        formatter.setWidth(80);
        formatter.setLeftPadding(5);
        formatter.setSyntaxPrefix("  general options:");
        formatter.printHelp(" ", GENRAL_OPTIONS);

        printHelpForRun();
        printHelpForInfo();
        printHelpForList();
        printHelpForCancel();
    }

    private void printHelpForRun() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.setLeftPadding(5);
        formatter.setWidth(80);

        System.out.println("\nAction \"run\" compiles and runs a program.");
        System.out.println("\n  Syntax: run [OPTIONS] <jar-file> <arguments>");
        formatter.setSyntaxPrefix("  \"run\" action arguments:");
        formatter.printHelp(" ", getRunOptionsWithoutDeprecatedOptions(new Options()));
    }

    private void printHelpForInfo() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.setLeftPadding(5);
        formatter.setWidth(80);

        System.out.println("\nAction \"info\" displays information about a program.");
        formatter.setSyntaxPrefix("  \"info\" action arguments:");
        formatter.printHelp(" ", getInfoOptionsWithoutDeprecatedOptions(new Options()));
    }

    private void printHelpForList() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.setLeftPadding(5);
        formatter.setWidth(80);

        System.out.println("\nAction \"list\" lists running and finished programs.");
        formatter.setSyntaxPrefix("  \"list\" action arguments:");
        formatter.printHelp(" ", getListOptions(new Options()));
    }

    private void printHelpForCancel() {
        HelpFormatter formatter = new HelpFormatter();
        formatter.setLeftPadding(5);
        formatter.setWidth(80);

        System.out.println("\nAction \"cancel\" cancels a running program.");
        formatter.setSyntaxPrefix("  \"cancel\" action arguments:");
        formatter.printHelp(" ", getCancelOptions(new Options()));
    }

    /**
     * Displays exceptions.
     * 
     * @param e the exception to display.
     */
    private int handleError(Throwable t) {
        System.out.println("Error: " + t.getMessage());
        if (this.verbose) {
            t.printStackTrace();
        } else {
            System.out.println("For a more detailed error message use the vebose output option '-v'.");
        }
        return 1;
    }

    private void evaluateGeneralOptions(CommandLine line) {
        // check help flag
        this.printHelp = line.hasOption(HELP_OPTION.getOpt());

        // check verbosity flag
        this.verbose = line.hasOption(VERBOSE_OPTION.getOpt());
    }

    /**
     * Parses the command line arguments and starts the requested action.
     * 
     * @param args command line arguments of the client.
     * @return The return code of the program
     */
    public int parseParameters(String[] args) {

        // check for action
        if (args.length < 1) {
            System.out.println("Please specify an action.");
            printHelp();
            return 1;
        }

        // get action
        String action = args[0];

        // remove action from parameters
        String[] params = Arrays.copyOfRange(args, 1, args.length);

        // do action
        if (action.equals(ACTION_RUN)) {
            return run(params);
        } else if (action.equals(ACTION_LIST)) {
            return list(params);
        } else if (action.equals(ACTION_INFO)) {
            return info(params);
        } else if (action.equals(ACTION_CANCEL)) {
            return cancel(params);
        } else if (action.equals("-h") || action.equals("--help")) {
            printHelp();
            return 0;
        } else {
            System.out.println("Invalid action!");
            printHelp();
            return 1;
        }
    }

    /**
     * Submits the job based on the arguments
     */
    public static void main(String[] args) throws ParseException {
        CliFrontend cli = new CliFrontend();
        int retCode = cli.parseParameters(args);
        System.exit(retCode);
    }
}