blob: 768068a9644e08e66322013b3c02464b2a460759 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.commandline.options;
import static org.apache.commons.io.FileUtils.readFileToString;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import net.sf.taverna.t2.commandline.exceptions.InvalidOptionException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
/**
* Handles the processing of command line arguments for enacting a workflow.
* This class encapsulates all command line options, and exposes them through
* higher-level accessors. Upon creation it checks the validity of the command
* line options and raises an {@link InvalidOptionException} if they are
* invalid.
*
* @author Stuart Owen
*
*/
public class CommandLineOptions {
private static final Logger logger = Logger
.getLogger(CommandLineOptions.class);
private Options options;
private CommandLine commandLine;
private static final String PROV_OPTION = "provenance";
private static final String PROV_BUNDLE = "provbundle";
public static final String CREDENTIAL_MANAGER_DIR_OPTION = "cmdir";
public static final String CREDENTIAL_MANAGER_PASSWORD_OPTION = "cmpassword";
private static final String INPUT_DELIMITER_LITERAL_OPTION = "inputdelimiter";
private static final String INPUT_DELIMITER_FILE_OPTION = "inputdelimfile";
private static final String INPUT_BACLAVA_OPTION = "inputdoc";
private static final String INPUT_FILE_OPTION = "inputfile";
private static final String INPUT_LITERAL_OPTION = "inputvalue";
private static final String OUTPUT_DIRECTORY_OPTION = "outputdir";
private static final String OUTPUT_BACLAVA_OPTION = "outputdoc";
private static final String DB_EMBEDDED_OPTION = "embedded";
private static final String DB_CLIENT_OPTION = "clientserver";
private static final String DB_MEMORY_OPTION = "inmemory";
private static final String DB_PROPS_OPTION = "dbproperties";
private static final String DB_START_OPTION = "startdb";
private static final String DB_PORT_OPTION = "port";
private static final String LOG_OPTION = "logfile";
private static final String HELP_OPTION = "help";
public CommandLineOptions(String[] args) throws InvalidOptionException {
this.options = intitialiseOptions();
this.commandLine = processArgs(args);
checkForInvalid();
}
public boolean askedForHelp() {
return hasOption(HELP_OPTION)
|| (getArgs().length == 0 && getOptions().length == 0);
}
public boolean isProvenanceEnabled() {
return hasOption(PROV_OPTION) || hasOption(PROV_BUNDLE);
}
protected void checkForInvalid() throws InvalidOptionException {
if (askedForHelp())
return;
if (isProvenanceEnabled()
&& !(hasOption(DB_EMBEDDED_OPTION)
|| hasOption(DB_CLIENT_OPTION) || hasOption(DB_PROPS_OPTION)))
throw new InvalidOptionException(
"You must be running with a disk-based database to use provenance");
if (isProvenanceEnabled() && hasOption(DB_MEMORY_OPTION))
throw new InvalidOptionException(
"You must be running with a disk-based database to use provenance");
if (hasOption(INPUT_FILE_OPTION) && hasOption(INPUT_BACLAVA_OPTION))
throw new InvalidOptionException("You can't provide both -"
+ INPUT_FILE_OPTION + " and -" + INPUT_BACLAVA_OPTION
+ " arguments");
if (hasOption(INPUT_LITERAL_OPTION) && hasOption(INPUT_BACLAVA_OPTION))
throw new InvalidOptionException("You can't provide both -"
+ INPUT_LITERAL_OPTION + " and -" + INPUT_BACLAVA_OPTION
+ " arguments");
if (hasOption(INPUT_DELIMITER_FILE_OPTION)
&& hasOption(INPUT_BACLAVA_OPTION))
throw new InvalidOptionException("You cannot combine the -"
+ INPUT_DELIMITER_FILE_OPTION + " and -"
+ INPUT_BACLAVA_OPTION + " arguments");
if (hasOption(INPUT_DELIMITER_LITERAL_OPTION)
&& hasOption(INPUT_BACLAVA_OPTION))
throw new InvalidOptionException("You cannot combine the -"
+ INPUT_DELIMITER_LITERAL_OPTION + " and -"
+ INPUT_BACLAVA_OPTION + " arguments");
if (getArgs().length == 0
&& !(hasOption(HELP_OPTION) || hasOption(DB_START_OPTION)))
throw new InvalidOptionException("You must specify a workflow");
if (hasOption(DB_MEMORY_OPTION) && hasOption(DB_EMBEDDED_OPTION)
|| hasOption(DB_MEMORY_OPTION) && hasOption(DB_CLIENT_OPTION)
|| hasOption(DB_EMBEDDED_OPTION) && hasOption(DB_CLIENT_OPTION))
throw new InvalidOptionException("The options -"
+ DB_EMBEDDED_OPTION + ", -" + DB_CLIENT_OPTION + " and -"
+ DB_MEMORY_OPTION + " cannot be used together");
}
public void displayHelp() {
displayHelp(hasOption(HELP_OPTION));
}
public void displayHelp(boolean showFullText) {
try {
HelpFormatter formatter = new HelpFormatter();
formatter
.printHelp("executeworkflow [options] [workflow]", options);
if (showFullText)
try (InputStream helpStream = CommandLineOptions.class
.getClassLoader().getResourceAsStream("help.txt")) {
System.out.println(IOUtils.toString(helpStream));
}
} catch (IOException e) {
logger.error("Error reading the help document", e);
System.exit(-1);
}
}
public String[] getArgs() {
return commandLine.getArgs();
}
/**
*
* @return the port that the database should run on
*/
public String getDatabasePort() {
return getOptionValue(DB_PORT_OPTION);
}
/**
*
* @return a path to a properties file that contains database configuration
* settings
*/
public String getDatabaseProperties() {
return getOptionValue(DB_PROPS_OPTION);
}
/**
*
* @return the path to the input document
*/
public String getInputDocument() {
return getOptionValue(INPUT_BACLAVA_OPTION);
}
/**
* Returns an array that alternates between a portname and path to a file
* containing the input values. Therefore the array will always contain an
* even number of elements
*
* @return an array of portname and path to files containing individual
* inputs.
*/
public String[] getInputFiles() {
if (hasInputFiles())
return getOptionValues(INPUT_FILE_OPTION);
return new String[] {};
}
public String[] getInputValues() {
if (hasInputValues())
return getOptionValues(INPUT_LITERAL_OPTION);
return new String[] {};
}
public String getLogFile() {
return getOptionValue(LOG_OPTION);
}
public Option[] getOptions() {
return commandLine.getOptions();
}
private String getOptionValue(String opt) {
return commandLine.getOptionValue(opt);
}
private String[] getOptionValues(String arg0) {
return commandLine.getOptionValues(arg0);
}
/**
*
* @return the directory to write the results to
*/
public String getOutputDirectory() {
return getOptionValue(OUTPUT_DIRECTORY_OPTION);
}
/**
*
* @return the path to the output document
*/
public String getOutputDocument() {
return getOptionValue(OUTPUT_BACLAVA_OPTION);
}
public boolean getStartDatabase() {
return hasOption(DB_START_OPTION);
}
/**
* @return the directory with Credential Manager's files
*/
public File getCredentialManagerDir() {
return getOptionValue(CREDENTIAL_MANAGER_DIR_OPTION) == null ? null
: new File(getOptionValue(CREDENTIAL_MANAGER_DIR_OPTION));
}
public boolean getStartDatabaseOnly() throws InvalidOptionException {
return (getStartDatabase() && (getWorkflow() == null));
}
public String getWorkflow() throws InvalidOptionException {
if (getArgs().length == 0)
return null;
else if (getArgs().length != 1)
throw new InvalidOptionException(
"You should only specify one workflow file");
else
return getArgs()[0];
}
public boolean hasDelimiterFor(String inputName) {
if (hasOption(INPUT_DELIMITER_FILE_OPTION)) {
String[] values = getOptionValues(INPUT_DELIMITER_FILE_OPTION);
for (int i = 0; i < values.length - 1; i += 2) {
if (!values[i].equals(inputName))
continue;
File f = new File(values[i + 1]);
if (f.exists() && f.isFile() && f.canRead() && f.length() > 0)
return true;
}
}
if (hasOption(INPUT_DELIMITER_LITERAL_OPTION)) {
String[] values = getOptionValues(INPUT_DELIMITER_LITERAL_OPTION);
for (int i = 0; i < values.length; i += 2)
if (values[i].equals(inputName))
return true;
}
return false;
}
public boolean hasInputFiles() {
return hasOption(INPUT_FILE_OPTION);
}
public boolean hasInputValues() {
return hasOption(INPUT_LITERAL_OPTION);
}
public boolean hasLogFile() {
return hasOption(LOG_OPTION);
}
public boolean hasOption(String option) {
return commandLine.hasOption(option);
}
public String inputDelimiter(String inputName) {
if (hasOption(INPUT_DELIMITER_FILE_OPTION)) {
String[] values = getOptionValues(INPUT_DELIMITER_FILE_OPTION);
for (int i = 0; i < values.length - 1; i += 2) {
if (!values[i].equals(inputName))
continue;
File f = new File(values[i + 1]);
try {
if (f.exists() && f.isFile() && f.canRead()) {
String result = readFileToString(f);
if (result != null && !result.isEmpty())
return result;
}
} catch (IOException e) {
// Ignore! Not much we can do
}
}
}
if (hasOption(INPUT_DELIMITER_LITERAL_OPTION)) {
String[] values = getOptionValues(INPUT_DELIMITER_LITERAL_OPTION);
for (int i = 0; i < values.length; i += 2)
if (values[i].equals(inputName))
return values[i + 1];
}
return null;
}
@SuppressWarnings("static-access")
private Options intitialiseOptions() {
Option helpOption = new Option(HELP_OPTION,
"Display comprehensive help information.");
Option outputOption = OptionBuilder
.withArgName("directory")
.hasArg()
.withDescription(
"Save outputs as files in directory, default "
+ "is to make a new directory workflowName_output.")
.create(OUTPUT_DIRECTORY_OPTION);
Option outputdocOption = OptionBuilder.withArgName("document").hasArg()
.withDescription("Save outputs to a new Baclava document.")
.create(OUTPUT_BACLAVA_OPTION);
Option logFileOption = OptionBuilder
.withArgName("filename")
.hasArg()
.withDescription(
"The logfile to which more verbose logging will be written to.")
.create(LOG_OPTION);
Option inputdocOption = OptionBuilder.withArgName("document").hasArg()
.withDescription("Load inputs from a Baclava document.")
.create(INPUT_BACLAVA_OPTION);
Option inputFileOption = OptionBuilder
.withArgName("inputname filename").hasArgs(2)
.withValueSeparator(' ')
.withDescription("Load the named input from file or URL.")
.create(INPUT_FILE_OPTION);
Option inputValueOption = OptionBuilder.withArgName("inputname value")
.hasArgs(2).withValueSeparator(' ')
.withDescription("Directly use the value for the named input.")
.create(INPUT_LITERAL_OPTION);
Option inputDelimiterOption = OptionBuilder
.withArgName("inputname delimiter")
.hasArgs(2)
.withValueSeparator(' ')
.withDescription(
"Cause an inputvalue or inputfile to be split into a "
+ "list according to the delimiter. The associated "
+ "workflow input must be expected to receive a list.")
.create(INPUT_DELIMITER_LITERAL_OPTION);
Option inputDelimiterFileOption = OptionBuilder
.withArgName("inputname file")
.hasArgs(2)
.withValueSeparator(' ')
.withDescription(
"Cause an inputvalue or inputfile to be split into a "
+ "list according to the delimiter character, "
+ "which is the first character read from the "
+ "given file. The associated workflow input "
+ "must be expected to receive a list.")
.create(INPUT_DELIMITER_FILE_OPTION);
Option dbProperties = OptionBuilder
.withArgName("filename")
.hasArg()
.withDescription(
"Load a properties file to configure the database.")
.create(DB_PROPS_OPTION);
Option port = OptionBuilder
.withArgName("portnumber")
.hasArg()
.withDescription(
"The port that the database is running on. If set "
+ "requested to start its own internal server, "
+ "this is the start port that will be used.")
.create(DB_PORT_OPTION);
Option embedded = new Option(DB_EMBEDDED_OPTION,
"Connect to an embedded Derby database. This can prevent "
+ "mulitple invocations.");
Option clientserver = new Option(DB_CLIENT_OPTION,
"Connect as a client to a derby server instance.");
Option inMemOption = new Option(
DB_MEMORY_OPTION,
"Run the workflow with data stored in-memory rather than in a "
+ "database. This can give performance inprovements, "
+ "at the cost of overall memory usage.");
Option startDB = new Option(DB_START_OPTION,
"Automatically start an internal Derby database server.");
Option provenance = new Option(PROV_OPTION,
"Generate provenance information and store it in the database.");
Option provExport = OptionBuilder
.hasArg()
.withArgName("file")
.withDescription(
"Save provenance/trace of workflow execution as "
+ "Research Object zip bundle to <file> specified.")
.create(PROV_BUNDLE);
Option credentialManagerDirectory = OptionBuilder
.withArgName("directory path")
.hasArg()
.withDescription(
"Absolute path to a directory where Credential Manager's "
+ "files (keystore and truststore) are located. ")
.create(CREDENTIAL_MANAGER_DIR_OPTION);
Option credentialManagerPassword = new Option(
CREDENTIAL_MANAGER_PASSWORD_OPTION,
"Indicate that the master password for Credential Manager will be "
+ "provided on standard input."); // optional
Options options = new Options();
options.addOption(helpOption);
options.addOption(inputFileOption);
options.addOption(inputValueOption);
options.addOption(inputDelimiterOption);
options.addOption(inputDelimiterFileOption);
options.addOption(inputdocOption);
options.addOption(outputOption);
options.addOption(outputdocOption);
options.addOption(inMemOption);
options.addOption(embedded);
options.addOption(clientserver);
options.addOption(dbProperties);
options.addOption(port);
options.addOption(startDB);
options.addOption(provenance);
options.addOption(provExport);
options.addOption(logFileOption);
options.addOption(credentialManagerDirectory);
options.addOption(credentialManagerPassword);
return options;
}
public boolean isClientServer() {
return hasOption(DB_CLIENT_OPTION);
}
public boolean isEmbedded() {
return hasOption(DB_EMBEDDED_OPTION);
}
public boolean isInMemory() {
return hasOption(DB_MEMORY_OPTION);
}
private CommandLine processArgs(String[] args) {
CommandLineParser parser = new GnuParser();
CommandLine line = null;
try {
// parse the command line arguments
line = parser.parse(options, args);
} catch (ParseException exp) {
// oops, something went wrong
System.err.println("Parsing failed. Reason: " + exp.getMessage());
System.exit(1);
}
return line;
}
/**
* Save the results to a directory if -outputdir has been explicitly
* defined, or if -outputdoc has not been defined
*
* @return boolean
*/
public boolean saveResultsToDirectory() {
return options.hasOption(OUTPUT_DIRECTORY_OPTION)
|| !options.hasOption(OUTPUT_BACLAVA_OPTION)
|| !options.hasOption(PROV_BUNDLE);
}
public String getProvBundle() {
return getOptionValue(PROV_BUNDLE);
}
public boolean isProvBundle() {
return hasOption(PROV_BUNDLE);
}
}