Merging in TAVUTILS-25
git-svn-id: https://taverna.googlecode.com/svn/taverna/engine/net.sf.taverna.t2.taverna-commandline/branches/taverna-commandline-1.3@13568 bf327186-88b3-11dd-a302-d386e5130c1c
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineInvocationContext.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineInvocationContext.java
new file mode 100644
index 0000000..5c60ddf
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineInvocationContext.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.provenance.reporter.ProvenanceReporter;
+import net.sf.taverna.t2.reference.ReferenceService;
+/**
+ * An InvocationContext used by the command line tool.
+ *
+ * @author Stuart Owen
+ *
+ */
+public class CommandLineInvocationContext implements InvocationContext {
+
+ private final ReferenceService referenceService;
+
+ private final ProvenanceReporter provenanceReporter;
+
+ private List<Object> entities = Collections
+ .synchronizedList(new ArrayList<Object>());
+
+ public CommandLineInvocationContext(ReferenceService referenceService,
+ ProvenanceReporter provenanceReporter) {
+ this.referenceService = referenceService;
+ this.provenanceReporter = provenanceReporter;
+ }
+
+ public void addEntity(Object entity) {
+ entities.add(entity);
+ }
+
+ public <T extends Object> List<T> getEntities(Class<T> entityType) {
+ List<T> entitiesOfType = new ArrayList<T>();
+ synchronized (entities) {
+ for (Object entity : entities) {
+ if (entityType.isInstance(entity)) {
+ entitiesOfType.add(entityType.cast(entity));
+ }
+ }
+ }
+ return entitiesOfType;
+ }
+
+ public ProvenanceReporter getProvenanceReporter() {
+ return provenanceReporter;
+ }
+
+ public ReferenceService getReferenceService() {
+ return referenceService;
+ }
+
+ public void removeEntity(Object entity) {
+ entities.remove(entity);
+ }
+}
\ No newline at end of file
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineLauncher.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineLauncher.java
new file mode 100644
index 0000000..e8fc4ba
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineLauncher.java
@@ -0,0 +1,586 @@
+/*******************************************************************************
+ * 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;
+
+import java.io.BufferedReader;
+import java.io.Console;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.NamingException;
+
+import net.sf.taverna.platform.spring.RavenAwareClassPathXmlApplicationContext;
+import net.sf.taverna.raven.launcher.Launchable;
+import net.sf.taverna.t2.commandline.data.DatabaseConfigurationHandler;
+import net.sf.taverna.t2.commandline.data.InputsHandler;
+import net.sf.taverna.t2.commandline.data.SaveResultsHandler;
+import net.sf.taverna.t2.commandline.exceptions.DatabaseConfigurationException;
+import net.sf.taverna.t2.commandline.exceptions.InvalidOptionException;
+import net.sf.taverna.t2.commandline.exceptions.OpenDataflowException;
+import net.sf.taverna.t2.commandline.exceptions.ReadInputException;
+import net.sf.taverna.t2.commandline.options.CommandLineOptions;
+import net.sf.taverna.t2.facade.WorkflowInstanceFacade;
+import net.sf.taverna.t2.facade.WorkflowInstanceFacade.State;
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.invocation.TokenOrderException;
+import net.sf.taverna.t2.invocation.WorkflowDataToken;
+import net.sf.taverna.t2.provenance.ProvenanceConnectorFactory;
+import net.sf.taverna.t2.provenance.ProvenanceConnectorFactoryRegistry;
+import net.sf.taverna.t2.provenance.connector.ProvenanceConnector;
+import net.sf.taverna.t2.reference.ReferenceService;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workbench.reference.config.DataManagementConfiguration;
+import net.sf.taverna.t2.workflowmodel.Dataflow;
+import net.sf.taverna.t2.workflowmodel.DataflowInputPort;
+import net.sf.taverna.t2.workflowmodel.DataflowOutputPort;
+import net.sf.taverna.t2.workflowmodel.DataflowValidationReport;
+import net.sf.taverna.t2.workflowmodel.EditException;
+import net.sf.taverna.t2.workflowmodel.Edits;
+import net.sf.taverna.t2.workflowmodel.EditsRegistry;
+import net.sf.taverna.t2.workflowmodel.InvalidDataflowException;
+import net.sf.taverna.t2.workflowmodel.serialization.DeserializationException;
+import net.sf.taverna.t2.workflowmodel.serialization.xml.XMLDeserializer;
+import net.sf.taverna.t2.workflowmodel.serialization.xml.XMLDeserializerRegistry;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.RollingFileAppender;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.input.SAXBuilder;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * A utility class that wraps the process of executing a workflow, allowing
+ * workflows to be easily executed independently of the GUI.
+ *
+ * @author Stuart Owen
+ */
+
+public class CommandLineLauncher implements Launchable {
+
+ private static Logger logger = Logger.getLogger(CommandLineLauncher.class);
+
+ /**
+ * Main method, purely for development and debugging purposes. Full
+ * execution of workflows will not work through this method.
+ *
+ * @param args
+ * @throws Exception
+ */
+ public static void main(String[] args) {
+ new CommandLineLauncher().launch(args);
+ }
+
+ public int launch(String[] args) {
+
+ try {
+ CommandLineOptions options = new CommandLineOptions(args);
+ initialiseLogging(options);
+ int result = setupAndExecute(args,options);
+ System.exit(result);
+ return result;
+ } catch (EditException e) {
+ error("There was an error opening the workflow: " + e.getMessage());
+ } catch (DeserializationException e) {
+ error("There was an error opening the workflow: " + e.getMessage());
+ } catch (InvalidDataflowException e) {
+ error("There was an error validating the workflow: "
+ + e.getMessage());
+ } catch (TokenOrderException e) {
+ error("There was an error starting the workflow execution: "
+ + e.getMessage());
+ } catch (InvalidOptionException e) {
+ error(e.getMessage());
+ } catch (ReadInputException e) {
+ error(e.getMessage());
+ } catch (OpenDataflowException e) {
+ error(e.getMessage());
+ } catch (DatabaseConfigurationException e) {
+ error(e.getMessage());
+ }
+ catch (CMException e) {
+ error("There was an error instantiating Credential Manager: " + e.getMessage());
+ }
+ // Should be unreachable
+ System.exit(-1);
+ return -1;
+ }
+
+ private void initialiseLogging(CommandLineOptions options) {
+ LogManager.resetConfiguration();
+
+ if (System.getProperty("log4j.configuration") == null) {
+ try {
+ PropertyConfigurator.configure(CommandLineLauncher.class
+ .getClassLoader().getResource("cl-log4j.properties")
+ .toURI().toURL());
+ } catch (MalformedURLException e) {
+ logger
+ .error(
+ "There was a serious error reading the default logging configuration",
+ e);
+ } catch (URISyntaxException e) {
+ logger
+ .error(
+ "There was a serious error reading the default logging configuration",
+ e);
+ }
+
+ } else {
+ PropertyConfigurator.configure(System
+ .getProperty("log4j.configuration"));
+ }
+
+ if (options.hasLogFile()) {
+ RollingFileAppender appender;
+ try {
+
+ PatternLayout layout = new PatternLayout(
+ "%-5p %d{ISO8601} (%c:%L) - %m%n");
+ appender = new RollingFileAppender(layout, options.getLogFile());
+ appender.setMaxFileSize("1MB");
+ appender.setEncoding("UTF-8");
+ appender.setMaxBackupIndex(4);
+ // Let root logger decide level
+ appender.setThreshold(Level.ALL);
+ LogManager.getRootLogger().addAppender(appender);
+ } catch (IOException e) {
+ System.err.println("Could not log to " + options.getLogFile());
+ }
+ }
+ }
+
+ public int setupAndExecute(String[] args,CommandLineOptions options) throws InvalidOptionException,
+ EditException, DeserializationException, InvalidDataflowException,
+ TokenOrderException, ReadInputException, OpenDataflowException,
+ DatabaseConfigurationException, CMException {
+
+
+ if (!options.askedForHelp()) {
+ setupDatabase(options);
+
+ if (options.getWorkflow() != null) {
+
+ // Initialise Credential Manager and SSL stuff quite early as parsing and
+ // validating the workflow might require its services
+ String credentialManagerDirPath = options.getCredentialManagerDir();
+ String credentialManagerPassword = null;
+ if (options.hasOption(CommandLineOptions.CREDENTIAL_MANAGER_PASSWORD_OPTION)){ // if this parameter was used when launching the command line tool
+ // Try to read the password from stdin (terminal or pipe)
+ credentialManagerPassword = getCredentialManagerPasswordFromStdin();
+ }
+ else{
+ // Try to read the password from a special file located in
+ // Credential Manager directory (if the dir was not null)
+ credentialManagerPassword = getCredentialManagerPasswordFromFile(options.getCredentialManagerDir());
+ }
+ if (credentialManagerPassword != null){
+ // Initialise Credential Manager (Taverna's Keystore and Truststore) and
+ // SSL stuff - set the SSLSocketFactory to use Taverna's Keystore and Truststore.
+
+ // If credentialManagerDirPath is null - initialize from the default location in <TAVERNA_HOME>/security somewhere
+ // inside user's home directory. This should not be used when running command
+ // line tool on a server and the Credential Manager dir path should always be
+ // passed in as we do not want to store the security files in user's home directory
+ // on the server (we do not even know which user the command line tool will be running as).
+
+ //if (credentialManagerDirPath != null){
+ CredentialManager.initialiseSSL(credentialManagerDirPath, credentialManagerPassword); // this can now handle situations when credentialManagerDirPath is null
+ //}
+ //else{
+ // Initialize from the default location in <TAVERNA_HOME>/security somewhere
+ // inside user's home directory. This should not be used when running command
+ // line tool on a server and the Credential Manager dir path should always be
+ // passed in as we do not want to store the security files in user's home directory
+ // on the server (we do not even know which user the command line tool will be running as).
+ // CredentialManager.initialiseSSL(credentialManagerPassword);
+ //}
+ }
+ else{
+ logger.warn("No master password provided for Credential Manager.");
+ }
+
+ URL workflowURL = readWorkflowURL(options.getWorkflow());
+
+ Dataflow dataflow = openDataflow(workflowURL);
+ validateDataflow(dataflow);
+
+ InvocationContext context = createInvocationContext();
+
+ WorkflowInstanceFacade facade = compileFacade(dataflow, context);
+ InputsHandler inputsHandler = new InputsHandler();
+ Map<String, DataflowInputPort> portMap = new HashMap<String, DataflowInputPort>();
+
+ for (DataflowInputPort port : dataflow.getInputPorts()) {
+ portMap.put(port.getName(), port);
+ }
+ inputsHandler.checkProvidedInputs(portMap, options);
+ Map<String, WorkflowDataToken> inputs = inputsHandler
+ .registerInputs(portMap, options, context);
+
+ CommandLineResultListener resultListener = addResultListener(
+ facade, context, dataflow, options);
+
+ executeWorkflow(facade, inputs, resultListener);
+ }
+ } else {
+ options.displayHelp();
+ }
+
+ // wait until user hits CTRL-C before exiting
+ if (options.getStartDatabaseOnly()) {
+ // FIXME: need to do this more gracefully.
+ while (true) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ protected void validateDataflow(Dataflow dataflow)
+ throws InvalidDataflowException {
+ // FIXME: this needs expanding upon to give more details info back to
+ // the user
+ // FIXME: added a getMessage to InvalidDataflowException may be good
+ // place to do this.
+ DataflowValidationReport report = dataflow.checkValidity();
+ if (!report.isValid()) {
+ throw new InvalidDataflowException(dataflow, report);
+ }
+ }
+
+ protected void executeWorkflow(WorkflowInstanceFacade facade,
+ Map<String, WorkflowDataToken> inputs,
+ CommandLineResultListener resultListener)
+ throws TokenOrderException {
+ facade.fire();
+ for (String inputName : inputs.keySet()) {
+ WorkflowDataToken token = inputs.get(inputName);
+ facade.pushData(token, inputName);
+ }
+ while (facade.getState().compareTo(State.completed) < 0) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ logger
+ .warn(
+ "Thread Interuption Exception whilst waiting for dataflow completion",
+ e);
+ }
+ }
+ resultListener.saveProvenance();
+ resultListener.saveOutputDocument();
+ }
+
+ private void setupDatabase(CommandLineOptions options)
+ throws DatabaseConfigurationException {
+ DatabaseConfigurationHandler dbHandler = new DatabaseConfigurationHandler(
+ options);
+ dbHandler.configureDatabase();
+ if (!options.isInMemory()) {
+ try {
+ dbHandler.testDatabaseConnection();
+ } catch (NamingException e) {
+ throw new DatabaseConfigurationException(
+ "There was an error trying to setup the database datasource: "
+ + e.getMessage(), e);
+ } catch (SQLException e) {
+ if (options.isClientServer()) {
+ throw new DatabaseConfigurationException(
+ "There was an error whilst making a test database connection. If running with -clientserver you should check that a server is running (check -startdb or -dbproperties)",
+ e);
+ }
+ if (options.isEmbedded()) {
+ throw new DatabaseConfigurationException(
+ "There was an error whilst making a test database connection. If running with -embedded you should make sure that another process isn't using the database, or a server running through -startdb",
+ e);
+ }
+ }
+ }
+
+ }
+
+ private InvocationContext createInvocationContext() {
+ ReferenceService referenceService = createReferenceServiceBean();
+ ProvenanceConnector connector = null;
+ DataManagementConfiguration dbConfig = DataManagementConfiguration
+ .getInstance();
+ if (dbConfig.isProvenanceEnabled()) {
+ String connectorType = dbConfig.getConnectorType();
+
+ for (ProvenanceConnectorFactory factory : ProvenanceConnectorFactoryRegistry
+ .getInstance().getInstances()) {
+ if (connectorType.equalsIgnoreCase(factory.getConnectorType())) {
+ connector = factory.getProvenanceConnector();
+ }
+
+ }
+ if (connector != null) {
+ connector.init();
+ } else {
+ error("Unable to initialise the provenance - the ProvenanceConnector cannot be found.");
+ }
+ }
+ InvocationContext context = new CommandLineInvocationContext(
+ referenceService, connector);
+ if (connector != null){
+ connector.setInvocationContext(context);
+ }
+ return context;
+ }
+
+ private File determineOutputDir(CommandLineOptions options,
+ String dataflowName) {
+ File result = null;
+ if (options.getOutputDirectory() != null) {
+ result = new File(options.getOutputDirectory());
+ if (result.exists()) {
+ error("The specified output directory '"
+ + options.getOutputDirectory() + "' already exists");
+ }
+ } else if (options.getOutputDocument() == null) {
+ result = new File(dataflowName + "_output");
+ int x = 1;
+ while (result.exists()) {
+ result = new File(dataflowName + "_output_" + x);
+ x++;
+ }
+ }
+ if (result != null) {
+ System.out.println("Outputs will be saved to the directory: "
+ + result.getAbsolutePath());
+ }
+ return result;
+ }
+
+ protected void error(String msg) {
+ System.err.println(msg);
+ System.exit(-1);
+ }
+
+ private URL readWorkflowURL(String workflowOption)
+ throws OpenDataflowException {
+ URL url;
+ try {
+ url = new URL("file:");
+ return new URL(url, workflowOption);
+ } catch (MalformedURLException e) {
+ throw new OpenDataflowException(
+ "The was an error processing the URL to the workflow: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * @param cmDir
+ * @return Password for Credential Manager.
+ * @throws CMException
+ */
+ private String getCredentialManagerPasswordFromFile(String cmDir) throws CMException{
+
+ if (cmDir == null){
+ return null;
+ }
+ File passwordFile = new File(cmDir, "password.txt");
+ String password = null;
+ BufferedReader buffReader = null;
+ try {
+ buffReader = new BufferedReader(new FileReader(passwordFile));
+ password = buffReader.readLine();
+ } catch (IOException ioe) {
+ // For some reason the error of the exception thrown
+ // does not get printed from the Launcher so print it here as
+ // well as it gives more clue as to what is going wrong.
+ logger.error("There was an error reading the Credential Manager password from "
+ + passwordFile.toString() + ": " + ioe.getMessage(), ioe);
+ throw new CMException(
+ "There was an error reading the Credential Manager password from "
+ + passwordFile.toString() + ": " + ioe.getMessage(), ioe);
+ } finally {
+ try {
+ buffReader.close();
+ } catch (Exception ioe1) {
+ // Ignore
+ }
+ }
+ return password;
+ }
+
+ private String getCredentialManagerPasswordFromStdin() throws CMException{
+
+ String password = null;
+
+ Console console = System.console();
+
+ if (console == null) { // password is being piped in, not entered in the terminal by user
+ BufferedReader buffReader = null;
+ try {
+ buffReader = new BufferedReader(new InputStreamReader(System.in));
+ password = buffReader.readLine();
+ }
+ catch (IOException ex) {
+ // For some reason the error of the exception thrown
+ // does not get printed from the Launcher so print it here as
+ // well as it gives more clue as to what is going wrong.
+ logger.error("An error occured while trying to read Credential Manager's password the user piped in: "
+ + ex.getMessage(), ex);
+ throw new CMException(
+ "An error occured while trying to read Credential Manager's password the user piped in: "
+ + ex.getMessage(), ex);
+ }
+ finally {
+ try {
+ buffReader.close();
+ } catch (Exception ioe1) {
+ // Ignore
+ }
+ }
+ }
+ else{ // read the password from the terminal as entered by the user
+ try {
+ // Block until user enters password
+ char passwordArray[] = console
+ .readPassword("Password for Credential Manager: ");
+ if (passwordArray != null) { // user did not abort input
+ password = new String(passwordArray);
+ } // else password will be null
+
+ } catch (Exception ex) {
+ // For some reason the error of the exception thrown
+ // does not get printed from the Launcher so print it here as
+ // well as it gives more clue as to what is going wrong.
+ logger.error("An error occured while trying to read Credential Manager's password from the terminal: "
+ + ex.getMessage(), ex);
+ throw new CMException(
+ "An error occured while trying to read Credential Manager's password from the terminal: "
+ + ex.getMessage(), ex);
+ }
+ }
+ return password;
+ }
+
+ private CommandLineResultListener addResultListener(
+ WorkflowInstanceFacade facade, InvocationContext context,
+ Dataflow dataflow, CommandLineOptions options) {
+ File outputDir = null;
+ File baclavaDoc = null;
+ File janus = null;
+ File janusDir = null;
+ File opm = null;
+
+ if (options.saveResultsToDirectory()) {
+ outputDir = determineOutputDir(options, dataflow.getLocalName());
+ janusDir = outputDir;
+ }
+ if (options.getOutputDocument() != null) {
+ baclavaDoc = new File(options.getOutputDocument());
+ }
+ if (options.isJanus()) {
+ if (options.getJanus() == null) {
+ if (janusDir == null) {
+ janusDir = determineOutputDir(options, dataflow.getLocalName());
+ }
+ janus = new File(janusDir, "provenance-janus.rdf");
+ } else {
+ janus = new File(options.getJanus());
+ }
+ }
+ if (options.isOPM()) {
+ if (options.getOPM() == null) {
+ if (janusDir == null) {
+ janusDir = determineOutputDir(options, dataflow.getLocalName());
+ }
+ opm = new File(janusDir, "provenance-opm.rdf");
+ } else {
+ opm = new File(options.getOPM());
+ }
+ }
+
+ Map<String, Integer> outputPortNamesAndDepth = new HashMap<String, Integer>();
+ for (DataflowOutputPort port : dataflow.getOutputPorts()) {
+ outputPortNamesAndDepth.put(port.getName(), port.getDepth());
+ }
+ SaveResultsHandler resultsHandler = new SaveResultsHandler(
+ outputPortNamesAndDepth, outputDir, baclavaDoc, janus, opm);
+ CommandLineResultListener listener = new CommandLineResultListener(
+ outputPortNamesAndDepth.size(), resultsHandler,
+ outputDir != null, baclavaDoc != null, opm != null, janus != null, facade.getWorkflowRunId());
+ facade.addResultListener(listener);
+ return listener;
+
+ }
+
+ protected ReferenceService createReferenceServiceBean() {
+ ApplicationContext appContext = new RavenAwareClassPathXmlApplicationContext(
+ DataManagementConfiguration.getInstance().getDatabaseContext());
+ return (ReferenceService) appContext
+ .getBean("t2reference.service.referenceService");
+ }
+
+ protected WorkflowInstanceFacade compileFacade(Dataflow dataflow,
+ InvocationContext context) throws InvalidDataflowException {
+ Edits edits = EditsRegistry.getEdits();
+ return edits.createWorkflowInstanceFacade(dataflow, context, "");
+ }
+
+ protected Dataflow openDataflow(URL workflowURL)
+ throws DeserializationException, EditException,
+ OpenDataflowException {
+ XMLDeserializer deserializer = XMLDeserializerRegistry.getInstance()
+ .getDeserializer();
+ SAXBuilder builder = new SAXBuilder();
+ Element el;
+ try {
+ InputStream stream = workflowURL.openStream();
+ el = builder.build(stream).detachRootElement();
+ } catch (JDOMException e) {
+ throw new OpenDataflowException(
+ "There was a problem processing the workflow XML: "
+ + e.getMessage(), e);
+ } catch (IOException e) {
+ throw new OpenDataflowException(
+ "There was a problem reading the workflow file: "
+ + e.getMessage(), e);
+ }
+ return deserializer.deserializeDataflow(el);
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineResultListener.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineResultListener.java
new file mode 100644
index 0000000..aadae0f
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/CommandLineResultListener.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * 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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.commandline.data.SaveResultsHandler;
+import net.sf.taverna.t2.facade.ResultListener;
+import net.sf.taverna.t2.invocation.WorkflowDataToken;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A ResultListener that is using for collecting and storing results when running
+ * workflows from the commandline.
+ *
+ * @author Stuart Owen
+ */
+public class CommandLineResultListener implements ResultListener {
+
+ private static final Logger logger = Logger.getLogger(CommandLineResultListener.class);
+
+ private Map<String, WorkflowDataToken> outputMap = new HashMap<String, WorkflowDataToken>();
+ private Map<String,WorkflowDataToken> finalTokens = new HashMap<String, WorkflowDataToken>();
+ private final SaveResultsHandler saveResultsHandler;
+ private final int numberOfOutputs;
+ private final boolean saveIndividualResults;
+ private final boolean saveOutputDocument;
+
+ private boolean saveOpm;
+
+ private boolean saveJanus;
+
+ private final String workflowRunId;
+
+ public CommandLineResultListener(int numberOfOutputs,SaveResultsHandler saveResultsHandler,boolean saveIndividualResults,boolean saveOutputDocument, boolean saveOpm, boolean saveJanus, String workflowRunId) {
+ this.numberOfOutputs = numberOfOutputs;
+ this.saveResultsHandler = saveResultsHandler;
+ this.saveIndividualResults = saveIndividualResults;
+ this.saveOutputDocument = saveOutputDocument;
+ this.saveOpm = saveOpm;
+ this.saveJanus = saveJanus;
+ this.workflowRunId = workflowRunId;
+ }
+
+ public Map<String, WorkflowDataToken> getOutputMap() {
+ return outputMap;
+ }
+
+ public void resultTokenProduced(WorkflowDataToken token, String portName) {
+ if (saveIndividualResults) {
+ saveResultsHandler.tokenReceived(token, portName);
+ }
+
+ if (token.isFinal()) {
+ finalTokens.put(portName, token);
+ }
+ }
+
+ public void saveOutputDocument() {
+ if (saveOutputDocument) {
+ try {
+ saveResultsHandler.saveOutputDocument(finalTokens);
+ } catch (Exception e) {
+ logger.error("An error occurred saving the final results to -outputdoc",e);
+ }
+ }
+ }
+
+ public void saveProvenance() {
+ if (saveOpm) {
+ saveResultsHandler.saveOpm(workflowRunId);
+ }
+ if (saveJanus) {
+ saveResultsHandler.saveJanus(workflowRunId);
+ }
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/DatabaseConfigurationHandler.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/DatabaseConfigurationHandler.java
new file mode 100644
index 0000000..7769a3c
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/DatabaseConfigurationHandler.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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.data;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+
+import net.sf.taverna.t2.commandline.exceptions.DatabaseConfigurationException;
+import net.sf.taverna.t2.commandline.options.CommandLineOptions;
+import net.sf.taverna.t2.workbench.reference.config.DataManagementConfiguration;
+import net.sf.taverna.t2.workbench.reference.config.DataManagementHelper;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Handles the initialisation and configuration of the data source according to
+ * the command line arguments, or a properties file.
+ * This also handles starting a network based instance of a Derby server, if requested.
+ *
+ * @author Stuart Owen
+ *
+ */
+public class DatabaseConfigurationHandler {
+
+ private final CommandLineOptions options;
+ private DataManagementConfiguration dbConfig;
+ private static Logger logger = Logger.getLogger(DatabaseConfigurationHandler.class);
+
+ public DatabaseConfigurationHandler(CommandLineOptions options) {
+ this.options = options;
+ dbConfig = DataManagementConfiguration.getInstance();
+ dbConfig.disableAutoSave();
+ }
+
+ public void configureDatabase() throws DatabaseConfigurationException {
+ overrideDefaults();
+ useOptions();
+ if (dbConfig.getStartInternalDerbyServer()) {
+ DataManagementHelper.startDerbyNetworkServer();
+ System.out.println("Started Derby Server on Port: "
+ + dbConfig.getCurrentPort());
+ }
+ DataManagementHelper.setupDataSource();
+ }
+
+ public DataManagementConfiguration getDBConfig() {
+ return dbConfig;
+ }
+
+ private void importConfigurationFromStream(InputStream inStr)
+ throws IOException {
+ Properties p = new Properties();
+ p.load(inStr);
+ for (Object key : p.keySet()) {
+ dbConfig.setProperty((String)key, p.getProperty((String)key).trim());
+ }
+ }
+
+ protected void overrideDefaults() throws DatabaseConfigurationException {
+
+ InputStream inStr = DatabaseConfigurationHandler.class.getClassLoader().getResourceAsStream("database-defaults.properties");
+ try {
+ importConfigurationFromStream(inStr);
+ } catch (IOException e) {
+ throw new DatabaseConfigurationException("There was an error reading the default database configuration settings: "+e.getMessage(),e);
+ }
+ }
+
+ protected void readConfigirationFromFile(String filename) throws IOException {
+ FileInputStream fileInputStream = new FileInputStream(filename);
+ importConfigurationFromStream(fileInputStream);
+ fileInputStream.close();
+ }
+
+ public void testDatabaseConnection()
+ throws DatabaseConfigurationException, NamingException, SQLException {
+ //try and get a connection
+ Connection con = null;
+ try {
+ con = DataManagementHelper.openConnection();
+ } finally {
+ if (con!=null)
+ try {
+ con.close();
+ } catch (SQLException e) {
+ logger.warn("There was an SQL error whilst closing the test connection: "+e.getMessage(),e);
+ }
+ }
+ }
+
+ public void useOptions() throws DatabaseConfigurationException {
+
+ if (options.hasOption("port")) {
+ dbConfig.setPort(options.getDatabasePort());
+ }
+
+ if (options.hasOption("startdb")) {
+ dbConfig.setStartInternalDerbyServer(true);
+ }
+
+ if (options.hasOption("inmemory")) {
+ dbConfig.setInMemory(true);
+ }
+
+ if (options.hasOption("embedded")) {
+ dbConfig.setInMemory(false);
+ dbConfig.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
+ }
+
+ if (options.isProvenanceEnabled()) {
+ dbConfig.setProvenanceEnabled(true);
+ }
+
+ if (options.hasOption("clientserver")) {
+ dbConfig.setInMemory(false);
+ dbConfig.setDriverClassName("org.apache.derby.jdbc.ClientDriver");
+ dbConfig.setJDBCUri("jdbc:derby://localhost:" + dbConfig.getPort() + "/t2-database;create=true;upgrade=true");
+ }
+
+ if (options.hasOption("dbproperties")) {
+ try {
+ readConfigirationFromFile(options.getDatabaseProperties());
+ } catch (IOException e) {
+ throw new DatabaseConfigurationException("There was an error reading the database configuration options at "+options.getDatabaseProperties()+" : "+e.getMessage(),e);
+ }
+ }
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/ErrorDocumentHandler.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/ErrorDocumentHandler.java
new file mode 100644
index 0000000..50b909c
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/ErrorDocumentHandler.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * 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.data;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.reference.ErrorDocument;
+import net.sf.taverna.t2.reference.ErrorDocumentService;
+import net.sf.taverna.t2.reference.IdentifiedList;
+import net.sf.taverna.t2.reference.ListService;
+import net.sf.taverna.t2.reference.StackTraceElementBean;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.reference.T2ReferenceType;
+
+/**
+ * Handles Error documents and transforming them into String representations
+ * that can be stored as a file, or within a Baclava document.
+ *
+ * @author Stuart Owen
+ *
+ */
+public class ErrorDocumentHandler {
+
+ /**
+ * Creates a string representation of the ErrorDocument.
+ */
+ public static String buildErrorDocumentString(ErrorDocument errDocument,
+ InvocationContext context) {
+
+ String errDocumentString = "";
+
+ String exceptionMessage = errDocument.getExceptionMessage();
+ if (exceptionMessage != null && !exceptionMessage.equals("")) {
+ DefaultMutableTreeNode exceptionMessageNode = new DefaultMutableTreeNode(
+ exceptionMessage);
+ errDocumentString += exceptionMessageNode + "\n";
+ List<StackTraceElementBean> stackTrace = errDocument
+ .getStackTraceStrings();
+ if (stackTrace.size() > 0) {
+ for (StackTraceElementBean stackTraceElement : stackTrace) {
+ errDocumentString += getStackTraceElementString(stackTraceElement)
+ + "\n";
+ }
+ }
+ }
+
+ Set<T2Reference> errorReferences = errDocument.getErrorReferences();
+ if (!errorReferences.isEmpty()) {
+ errDocumentString += "Set of ErrorDocumentS to follow." + "\n";
+ }
+ int errorCounter = 1;
+ int listCounter = 0;
+ for (T2Reference reference : errorReferences) {
+ if (reference.getReferenceType().equals(
+ T2ReferenceType.ErrorDocument)) {
+ ErrorDocumentService errorDocumentService = context
+ .getReferenceService().getErrorDocumentService();
+ ErrorDocument causeErrorDocument = errorDocumentService
+ .getError(reference);
+ if (listCounter == 0) {
+ errDocumentString += "ErrorDocument " + (errorCounter++)
+ + "\n";
+ } else {
+ errDocumentString += "ErrorDocument " + listCounter + "."
+ + (errorCounter++) + "\n";
+ }
+ errDocumentString += buildErrorDocumentString(
+ causeErrorDocument, context)
+ + "\n";
+ } else if (reference.getReferenceType().equals(
+ T2ReferenceType.IdentifiedList)) {
+ List<ErrorDocument> errorDocuments = getErrorDocuments(
+ reference, context);
+ errDocumentString += "ErrorDocument list " + (++listCounter)
+ + "\n";
+ for (ErrorDocument causeErrorDocument : errorDocuments) {
+ errDocumentString += buildErrorDocumentString(
+ causeErrorDocument, context)
+ + "\n";
+ }
+ }
+ }
+
+ return errDocumentString;
+ }
+
+ public static void buildErrorDocumentTree(DefaultMutableTreeNode node,
+ ErrorDocument errorDocument, InvocationContext context) {
+ DefaultMutableTreeNode child = new DefaultMutableTreeNode(errorDocument);
+ String exceptionMessage = errorDocument.getExceptionMessage();
+ if (exceptionMessage != null && !exceptionMessage.equals("")) {
+ DefaultMutableTreeNode exceptionMessageNode = new DefaultMutableTreeNode(
+ exceptionMessage);
+ child.add(exceptionMessageNode);
+ List<StackTraceElementBean> stackTrace = errorDocument
+ .getStackTraceStrings();
+ if (stackTrace.size() > 0) {
+ for (StackTraceElementBean stackTraceElement : stackTrace) {
+ exceptionMessageNode.add(new DefaultMutableTreeNode(
+ getStackTraceElementString(stackTraceElement)));
+ }
+ }
+
+ }
+ node.add(child);
+
+ Set<T2Reference> errorReferences = errorDocument.getErrorReferences();
+ for (T2Reference reference : errorReferences) {
+ if (reference.getReferenceType().equals(
+ T2ReferenceType.ErrorDocument)) {
+ ErrorDocumentService errorDocumentService = context
+ .getReferenceService().getErrorDocumentService();
+ ErrorDocument causeErrorDocument = errorDocumentService
+ .getError(reference);
+ if (errorReferences.size() == 1) {
+ buildErrorDocumentTree(node, causeErrorDocument, context);
+ } else {
+ buildErrorDocumentTree(child, causeErrorDocument, context);
+ }
+ } else if (reference.getReferenceType().equals(
+ T2ReferenceType.IdentifiedList)) {
+ List<ErrorDocument> errorDocuments = getErrorDocuments(
+ reference, context);
+ if (errorDocuments.size() == 1) {
+ buildErrorDocumentTree(node, errorDocuments.get(0), context);
+ } else {
+ for (ErrorDocument errorDocument2 : errorDocuments) {
+ buildErrorDocumentTree(child, errorDocument2, context);
+ }
+ }
+ }
+ }
+ }
+
+ private static String getStackTraceElementString(
+ StackTraceElementBean stackTraceElement) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(stackTraceElement.getClassName());
+ sb.append('.');
+ sb.append(stackTraceElement.getMethodName());
+ if (stackTraceElement.getFileName() == null) {
+ sb.append("(unknown file)");
+ } else {
+ sb.append('(');
+ sb.append(stackTraceElement.getFileName());
+ sb.append(':');
+ sb.append(stackTraceElement.getLineNumber());
+ sb.append(')');
+ }
+ return sb.toString();
+ }
+
+ public static List<ErrorDocument> getErrorDocuments(T2Reference reference,
+ InvocationContext context) {
+ List<ErrorDocument> errorDocuments = new ArrayList<ErrorDocument>();
+ if (reference.getReferenceType().equals(T2ReferenceType.ErrorDocument)) {
+ ErrorDocumentService errorDocumentService = context
+ .getReferenceService().getErrorDocumentService();
+ errorDocuments.add(errorDocumentService.getError(reference));
+ } else if (reference.getReferenceType().equals(
+ T2ReferenceType.IdentifiedList)) {
+ ListService listService = context.getReferenceService()
+ .getListService();
+ IdentifiedList<T2Reference> list = listService.getList(reference);
+ for (T2Reference listReference : list) {
+ errorDocuments
+ .addAll(getErrorDocuments(listReference, context));
+ }
+ }
+ return errorDocuments;
+ }
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/InputsHandler.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/InputsHandler.java
new file mode 100644
index 0000000..0b41bea
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/InputsHandler.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * 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.data;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.commandline.exceptions.InputMismatchException;
+import net.sf.taverna.t2.commandline.exceptions.InvalidOptionException;
+import net.sf.taverna.t2.commandline.exceptions.ReadInputException;
+import net.sf.taverna.t2.commandline.options.CommandLineOptions;
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.invocation.WorkflowDataToken;
+import net.sf.taverna.t2.lang.baclava.BaclavaDocumentHandler;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.workflowmodel.DataflowInputPort;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+import org.embl.ebi.escience.baclava.DataThing;
+import org.jdom.JDOMException;
+
+/**
+ *
+ * Handles the reading, or processing, or input values according to arguments provided to the commandline.
+ * The may be either as direct values, from a file, or from a Baclava document.
+ *
+ * Also handles registering the input values with the Reference Service, ready to initiate
+ * the workflow run.
+ *
+ * @author Stuart Owen
+ *
+ */
+public class InputsHandler {
+
+ private static Logger logger = Logger.getLogger(InputsHandler.class);
+
+
+ public void checkProvidedInputs(Map<String, DataflowInputPort> portMap, CommandLineOptions options) throws InputMismatchException {
+ //we dont check for the document
+ if (options.getInputDocument()==null) {
+ Set<String> providedInputNames = new HashSet<String>();
+ for (int i=0;i<options.getInputFiles().length;i+=2) {
+ providedInputNames.add(options.getInputFiles()[i]);
+ }
+
+ for (int i=0;i<options.getInputValues().length;i+=2) {
+ providedInputNames.add(options.getInputValues()[i]);
+ }
+
+ if (portMap.size()*2 != (options.getInputFiles().length + options.getInputValues().length)) {
+ throw new InputMismatchException("The number of inputs provided does not match the number of input ports.",portMap.keySet(),providedInputNames);
+ }
+
+ for (String portName : portMap.keySet()) {
+ if (!providedInputNames.contains(portName)) {
+ throw new InputMismatchException("The provided inputs does not contain an input for the port '"+portName+"'",portMap.keySet(),providedInputNames);
+ }
+ }
+ }
+ }
+
+
+ public Map<String, WorkflowDataToken> registerInputs(Map<String, DataflowInputPort> portMap, CommandLineOptions options,
+ InvocationContext context) throws InvalidOptionException, ReadInputException {
+ Map<String,WorkflowDataToken> inputs = new HashMap<String, WorkflowDataToken>();
+ URL url;
+ try {
+ url = new URL("file:");
+ } catch (MalformedURLException e1) {
+ //Should never happen, but just incase:
+ throw new ReadInputException("The was an internal error setting up the URL to open the inputs. You should contact Taverna support.",e1);
+ }
+
+ if (options.hasInputFiles()) {
+ regesterInputsFromFiles(portMap, options, context, inputs, url);
+ }
+
+ if (options.hasInputValues()) {
+ registerInputsFromValues(portMap, options, context, inputs);
+
+ }
+
+ if (options.getInputDocument()!=null) {
+ registerInputsFromBaclava(options, context, inputs, url);
+ }
+
+ return inputs;
+ }
+
+
+ private void registerInputsFromBaclava(CommandLineOptions options,
+ InvocationContext context, Map<String, WorkflowDataToken> inputs,
+ URL url) throws ReadInputException {
+ String inputDocPath = options.getInputDocument();
+
+ URL inputDocURL;
+ try {
+ inputDocURL = new URL(url, inputDocPath);
+ } catch (MalformedURLException e1) {
+ throw new ReadInputException(
+ "The a error reading the input document from : "
+ + inputDocPath + ", " + e1.getMessage(), e1);
+ }
+ Map<String, DataThing> things;
+ try {
+ things = new BaclavaDocumentHandler().readData(inputDocURL.openStream());
+ } catch (IOException e) {
+ throw new ReadInputException(
+ "There was an error reading the input document file: "
+ + e.getMessage(), e);
+ } catch (JDOMException e) {
+ throw new ReadInputException(
+ "There was a error processing the input document XML: "
+ + e.getMessage(), e);
+ }
+ for (String inputName : things.keySet()) {
+ DataThing thing = things.get(inputName);
+ Object object = thing.getDataObject();
+ T2Reference entityId=context.getReferenceService().register(object,getObjectDepth(object), true, context);
+ WorkflowDataToken token = new WorkflowDataToken("",new int[]{}, entityId, context);
+ inputs.put(inputName, token);
+ }
+ }
+
+
+ private void registerInputsFromValues(
+ Map<String, DataflowInputPort> portMap, CommandLineOptions options,
+ InvocationContext context, Map<String, WorkflowDataToken> inputs)
+ throws InvalidOptionException {
+ String[] inputParams = options.getInputValues();
+ for (int i = 0; i < inputParams.length; i = i + 2) {
+ String inputName = inputParams[i];
+ try {
+ String inputValue = inputParams[i + 1];
+ DataflowInputPort port = portMap.get(inputName);
+
+ if (port==null) {
+ throw new InvalidOptionException("Cannot find an input port named '"+inputName+"'");
+ }
+
+ T2Reference entityId=null;
+ if (options.hasDelimiterFor(inputName)) {
+ String delimiter=options.inputDelimiter(inputName);
+ Object value=checkForDepthMismatch(1, port.getDepth(), inputName, inputValue.split(delimiter));
+ entityId=context.getReferenceService().register(value, port.getDepth(), true, context);
+ }
+ else
+ {
+ Object value=checkForDepthMismatch(0, port.getDepth(), inputName, inputValue);
+ entityId=context.getReferenceService().register(value, port.getDepth(), true, context);
+ }
+
+ WorkflowDataToken token = new WorkflowDataToken("",new int[]{}, entityId, context);
+ inputs.put(inputName, token);
+
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidOptionException("Missing input value for input "+ inputName);
+ }
+ }
+ }
+
+
+ private void regesterInputsFromFiles(
+ Map<String, DataflowInputPort> portMap, CommandLineOptions options,
+ InvocationContext context, Map<String, WorkflowDataToken> inputs,
+ URL url) throws InvalidOptionException {
+ String[] inputParams = options.getInputFiles();
+ for (int i = 0; i < inputParams.length; i = i + 2) {
+ String inputName = inputParams[i];
+ try {
+ URL inputURL = new URL(url, inputParams[i + 1]);
+ DataflowInputPort port = portMap.get(inputName);
+
+ if (port==null) {
+ throw new InvalidOptionException("Cannot find an input port named '"+inputName+"'");
+ }
+
+ T2Reference entityId=null;
+
+ if (options.hasDelimiterFor(inputName)) {
+ String delimiter=options.inputDelimiter(inputName);
+ Object value = IOUtils.toString(inputURL.openStream()).split(delimiter);
+
+ value=checkForDepthMismatch(1, port.getDepth(), inputName, value);
+ entityId=context.getReferenceService().register(value, port.getDepth(), true, context);
+ }
+ else
+ {
+ Object value = IOUtils.toByteArray(inputURL.openStream());
+ value=checkForDepthMismatch(0, port.getDepth(), inputName, value);
+ entityId=context.getReferenceService().register(value, port.getDepth(), true, context);
+ }
+
+ WorkflowDataToken token = new WorkflowDataToken("",new int[]{}, entityId, context);
+ inputs.put(inputName, token);
+
+ } catch (IndexOutOfBoundsException e) {
+ throw new InvalidOptionException("Missing input filename for input "+ inputName);
+ } catch (IOException e) {
+ throw new InvalidOptionException("Could not read input " + inputName + ": " + e.getMessage());
+ }
+ }
+ }
+
+ private Object checkForDepthMismatch(int inputDepth,int portDepth,String inputName,Object inputValue) throws InvalidOptionException {
+ if (inputDepth!=portDepth) {
+ if (inputDepth<portDepth) {
+ logger.warn("Wrapping input for '" + inputName + "' from a depth of "+inputDepth+" to the required depth of "+portDepth);
+ while (inputDepth<portDepth) {
+ List<Object> l=new ArrayList<Object>();
+ l.add(inputValue);
+ inputValue=l;
+ inputDepth++;
+ }
+ }
+ else {
+ String msg="There is a mismatch between depth of the list for the input port '"+inputName+"' and the data presented. The input port requires a "+depthToString(portDepth)+" and the data presented is a "+depthToString(inputDepth);
+ throw new InvalidOptionException(msg);
+ }
+ }
+
+ return inputValue;
+ }
+
+ private String depthToString(int depth) {
+ switch (depth) {
+ case 0:
+ return "single item";
+ case 1:
+ return "list";
+ case 2:
+ return "list of lists";
+ default:
+ return "list of depth "+depth;
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private int getObjectDepth(Object o) {
+ int result = 0;
+ if (o instanceof Iterable) {
+ result++;
+ Iterator i = ((Iterable) o).iterator();
+
+ if (i.hasNext()) {
+ Object child = i.next();
+ result = result + getObjectDepth(child);
+ }
+ }
+ return result;
+ }
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/MimeTypeHandler.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/MimeTypeHandler.java
new file mode 100644
index 0000000..2e026d6
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/MimeTypeHandler.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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.data;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.reference.ExternalReferenceSPI;
+import net.sf.taverna.t2.reference.ReferenceSet;
+import net.sf.taverna.t2.reference.ReferencedDataNature;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.reference.T2ReferenceType;
+
+import org.apache.log4j.Logger;
+
+import eu.medsea.mimeutil.MimeType;
+import eu.medsea.mimeutil.MimeUtil2;
+
+/**
+ * Handles identifying mime-types for a given data stream, or T2Reference
+ *
+ * @author Stuart Owen
+ */
+public class MimeTypeHandler {
+
+ private static Logger logger = Logger.getLogger(MimeTypeHandler.class);
+
+ @SuppressWarnings("unchecked")
+ public static List<MimeType> getMimeTypes(InputStream inputStream,InvocationContext context) throws IOException {
+ List<MimeType> mimeList = new ArrayList<MimeType>();
+ MimeUtil2 mimeUtil = new MimeUtil2();
+ mimeUtil
+ .registerMimeDetector("eu.medsea.mimeutil.detector.ExtensionMimeDetector");
+ mimeUtil
+ .registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
+ mimeUtil
+ .registerMimeDetector("eu.medsea.mimeutil.detector.WindowsRegistryMimeDetector");
+ mimeUtil
+ .registerMimeDetector("eu.medsea.mimeutil.detector.ExtraMimeTypes");
+
+ try {
+ byte[] bytes = new byte[2048];
+ inputStream.read(bytes);
+ Collection mimeTypes2 = mimeUtil.getMimeTypes(bytes);
+ mimeList.addAll(mimeTypes2);
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ logger.error(
+ "Failed to close stream after determining mimetype", e);
+ }
+ }
+ return mimeList;
+ }
+
+ public static List<String> determineMimeTypes(T2Reference reference,
+ InvocationContext context) throws IOException {
+ List<String> mimeTypeList = new ArrayList<String>();
+
+ if (reference.getReferenceType() == T2ReferenceType.ErrorDocument) {
+ mimeTypeList.add("text/plain");
+ } else {
+ ReferenceSet referenceSet = (ReferenceSet) context
+ .getReferenceService().resolveIdentifier(reference,
+ null, context);
+ if (!referenceSet.getExternalReferences().isEmpty()) {
+
+ ExternalReferenceSPI externalReference = referenceSet
+ .getExternalReferences().iterator().next();
+
+ List<MimeType> mimeTypes = getMimeTypes(
+ externalReference.openStream(context), context);
+
+ for (MimeType type : mimeTypes) {
+ if (!type.toString().equals("text/plain")
+ && !type.toString().equals(
+ "application/octet-stream")) {
+ mimeTypeList.add(type.toString());
+ }
+ }
+ if (externalReference.getDataNature() == ReferencedDataNature.TEXT) {
+ mimeTypeList.add("text/plain");
+ } else {
+ mimeTypeList.add("application/octet-stream");
+ }
+ }
+
+ }
+
+ return mimeTypeList;
+ }
+
+ public static List<MimeType> getMimeTypes(
+ ExternalReferenceSPI externalReference, InvocationContext context) throws IOException {
+
+ InputStream inputStream = externalReference.openStream(context);
+ return getMimeTypes(inputStream, context);
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/SaveResultsHandler.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/SaveResultsHandler.java
new file mode 100644
index 0000000..b2f8ea4
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/data/SaveResultsHandler.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * 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.data;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.sf.taverna.t2.commandline.CommandLineResultListener;
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.invocation.WorkflowDataToken;
+import net.sf.taverna.t2.lang.baclava.BaclavaDocumentHandler;
+import net.sf.taverna.t2.provenance.api.ProvenanceAccess;
+import net.sf.taverna.t2.provenance.client.ProvenanceExporter;
+import net.sf.taverna.t2.reference.ErrorDocument;
+import net.sf.taverna.t2.reference.ExternalReferenceSPI;
+import net.sf.taverna.t2.reference.Identified;
+import net.sf.taverna.t2.reference.IdentifiedList;
+import net.sf.taverna.t2.reference.ReferenceService;
+import net.sf.taverna.t2.reference.ReferenceSet;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.reference.T2ReferenceType;
+import net.sf.taverna.t2.workbench.reference.config.DataManagementConfiguration;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * Handles all recording of results as they are received by the {@link CommandLineResultListener}
+ * or when the workflow enactment has completed.
+ * This includes saving as a Baclava Document, or storing individual results.
+ *
+ * @author Stuart Owen
+ *
+ * @see BaclavaHandler
+ * @see CommandLineResultListener
+ *
+ */
+public class SaveResultsHandler {
+
+ private final Map<String, Integer> portsAndDepth;
+ private HashMap<String, Integer> depthSeen;
+ private final File rootDirectory;
+ private static Logger logger = Logger
+ .getLogger(CommandLineResultListener.class);
+ private final File outputDocumentFile;
+ private final File janus;
+ private final File opm;
+ private ProvenanceExporter provExport;
+
+ public SaveResultsHandler(Map<String, Integer> portsAndDepth,
+ File rootDirectory, File outputDocumentFile, File janus, File opm) {
+
+ this.portsAndDepth = portsAndDepth;
+ this.rootDirectory = rootDirectory;
+ this.outputDocumentFile = outputDocumentFile;
+ this.janus = janus;
+ this.opm = opm;
+
+ depthSeen = new HashMap<String, Integer>();
+ for (String portName : portsAndDepth.keySet()) {
+ depthSeen.put(portName, -1);
+ }
+ }
+
+ public void tokenReceived(WorkflowDataToken token, String portName) {
+ if (rootDirectory != null) { //only save individual results if a directory is specified
+ if (portsAndDepth.containsKey(portName)) {
+ int[] index = token.getIndex();
+ if (depthSeen.get(portName) == -1)
+ depthSeen.put(portName, index.length);
+ if (index.length >= depthSeen.get(portName)) {
+ storeToken(token, portName);
+ }
+ } else {
+ logger
+ .error("Result recieved for unexpected Port: "
+ + portName);
+ }
+ }
+ }
+
+ public void saveOutputDocument(Map<String,WorkflowDataToken> allResults) throws Exception {
+ if (outputDocumentFile!=null) {
+ BaclavaDocumentHandler handler = new BaclavaDocumentHandler();
+ InvocationContext context = null;
+ Map<String,T2Reference> references = new HashMap<String, T2Reference>();
+ //fetch the references from the tokens, and pick up the context on the way
+ for (String portname : allResults.keySet()) {
+ WorkflowDataToken token = allResults.get(portname);
+ if (context==null) {
+ context=token.getContext();
+ }
+ references.put(portname, token.getData());
+ }
+ handler.setChosenReferences(references);
+
+ handler.setInvocationContext(context);
+ ReferenceService referenceService=null;
+ if (context!=null) referenceService = context.getReferenceService();
+ handler.setReferenceService(referenceService);
+
+ handler.saveData(outputDocumentFile);
+ }
+ }
+
+ protected void storeToken(WorkflowDataToken token, String portName) {
+
+ if (token.getData().getReferenceType() == T2ReferenceType.IdentifiedList) {
+ saveList(token, portName);
+ } else {
+ File dataDirectory = rootDirectory;
+ File dataFile = null;
+
+ if (token.getIndex().length > 0) {
+ dataDirectory = new File(rootDirectory, portName);
+ for (int i = 0; i < token.getIndex().length - 1; i++) {
+ dataDirectory = new File(dataDirectory, String
+ .valueOf(token.getIndex()[i] + 1));
+ }
+ dataFile = new File(dataDirectory, String.valueOf(token
+ .getIndex()[token.getIndex().length - 1] + 1));
+ } else {
+ dataFile = new File(dataDirectory, portName);
+ }
+
+ if (!dataDirectory.exists()) {
+ dataDirectory.mkdirs();
+ }
+
+ if (dataFile.exists()) {
+ System.err.println("There is already data saved to: "
+ + dataFile.getAbsolutePath());
+ System.exit(-1);
+ }
+
+ saveIndividualDataFile(token.getData(), dataFile, token
+ .getContext());
+ }
+ }
+
+ private void saveList(WorkflowDataToken token, String portName) {
+ File dataDirectory = null;
+ int[] index = token.getIndex();
+
+ if (index.length > 0) {
+ dataDirectory = new File(rootDirectory, portName);
+ for (int i = 0; i < index.length - 1; i++) {
+ dataDirectory = new File(dataDirectory, String.valueOf(token
+ .getIndex()[i] + 1));
+ }
+ dataDirectory = new File(dataDirectory, String.valueOf(token
+ .getIndex()[index.length - 1]+ 1));
+ } else {
+ dataDirectory = new File(rootDirectory, portName);
+ }
+
+ T2Reference reference = token.getData();
+ IdentifiedList<T2Reference> list = token.getContext()
+ .getReferenceService().getListService().getList(reference);
+ saveListItems(token.getContext(), dataDirectory, list);
+ }
+
+ private void saveListItems(InvocationContext context, File dataDirectory,IdentifiedList<T2Reference> list) {
+ int c = 0;
+ if (!dataDirectory.exists()) {
+ dataDirectory.mkdirs();
+ }
+ for (T2Reference id : list) {
+ File dataFile = new File(dataDirectory, String.valueOf(c+1));
+ if (id.getReferenceType() == T2ReferenceType.IdentifiedList) {
+ IdentifiedList<T2Reference> innerList = context
+ .getReferenceService().getListService().getList(id);
+ saveListItems(context, dataFile, innerList);
+ }
+ else {
+ saveIndividualDataFile(id, dataFile, context);
+ }
+ c++;
+ }
+ }
+
+ protected void saveIndividualDataFile(T2Reference reference, File dataFile,
+ InvocationContext context) {
+
+ if (dataFile.exists()) {
+ System.err.println("There is already data saved to: "
+ + dataFile.getAbsolutePath());
+ System.exit(-1);
+ }
+
+ Object data = null;
+ InputStream stream = null;
+ try {
+ if (reference.containsErrors()) {
+ ErrorDocument errorDoc = context.getReferenceService()
+ .getErrorDocumentService().getError(reference);
+ data = ErrorDocumentHandler.buildErrorDocumentString(errorDoc, context);
+ dataFile = new File(dataFile.getAbsolutePath()+".error");
+ } else {
+ // FIXME: this really should be done using a stream rather
+ // than an instance of the object in memory
+ Identified identified = context.getReferenceService().resolveIdentifier(reference, null, context);
+ ReferenceSet referenceSet = (ReferenceSet) identified;
+ ExternalReferenceSPI externalReference = referenceSet.getExternalReferences().iterator().next();
+ stream = externalReference.openStream(context);
+ data = stream;
+ }
+
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(dataFile);
+ if (data instanceof InputStream) {
+ IOUtils.copyLarge(stream, fos);
+ }
+ else {
+ PrintWriter out = new PrintWriter(new OutputStreamWriter(fos));
+ out.print(data.toString());
+ out.flush();
+ out.close();
+ }
+ } catch (FileNotFoundException e) {
+ logger.error("Unable to find the file: '"
+ + dataFile.getAbsolutePath() + "' for writing results", e);
+ } catch (IOException e) {
+ logger.error("IO Error writing resuts to: '"
+ + dataFile.getAbsolutePath(), e);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ logger.error("Cannot close file output stream", e);
+ }
+ }
+ }
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ logger.error("Cannot close stream from reference", e);
+ }
+ }
+ }
+ }
+
+ public void saveOpm(String workflowRunId) {
+ if (opm.getParentFile() != null) {
+ opm.getParentFile().mkdirs();
+ }
+ BufferedOutputStream outStream;
+ try {
+ outStream = new BufferedOutputStream(new FileOutputStream(opm));
+ } catch (FileNotFoundException e1) {
+ logger.error("Can't find directory for writing OPM to " + opm, e1);
+ return;
+ }
+ try {
+ getProvenanceExporter().exportAsOPMRDF(workflowRunId, outStream);
+ } catch (Exception e) {
+ logger.error("Can't write OPM to " + opm, e);
+ } finally {
+ try {
+ outStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ protected synchronized ProvenanceExporter getProvenanceExporter() {
+ if (provExport == null) {
+ DataManagementConfiguration dbConfig = DataManagementConfiguration.getInstance();
+ String connectorType = dbConfig.getConnectorType();
+ ProvenanceAccess provAccess = new ProvenanceAccess(connectorType);
+ provExport = new ProvenanceExporter(provAccess);
+ }
+ return provExport;
+ }
+
+ public void saveJanus(String workflowRunId) {
+ if (janus.getParentFile() != null) {
+ janus.getParentFile().mkdirs();
+ }
+ BufferedOutputStream outStream;
+ try {
+ outStream = new BufferedOutputStream(new FileOutputStream(janus));
+ } catch (FileNotFoundException e1) {
+ logger.error("Can't find directory for writing Janus to " + janus, e1);
+ return;
+ }
+ try {
+ getProvenanceExporter().exportAsJanusRDF(workflowRunId, outStream);
+ } catch (Exception e) {
+ logger.error("Can't write Janus to " + janus, e);
+ } finally {
+ try {
+ outStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/DatabaseConfigurationException.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/DatabaseConfigurationException.java
new file mode 100644
index 0000000..616fb70
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/DatabaseConfigurationException.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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.exceptions;
+
+public class DatabaseConfigurationException extends Exception {
+
+ private static final long serialVersionUID = -4128248547532355697L;
+
+ public DatabaseConfigurationException() {
+
+ }
+
+ public DatabaseConfigurationException(String message) {
+ super(message);
+ }
+
+ public DatabaseConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ public DatabaseConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/InputMismatchException.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/InputMismatchException.java
new file mode 100644
index 0000000..eb4a289
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/InputMismatchException.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.exceptions;
+
+import java.util.Set;
+
+public class InputMismatchException extends InvalidOptionException {
+
+ private static final long serialVersionUID = -5368068332397293706L;
+ private final Set<String> expectedInputNames;
+ private final Set<String> providedInputNames;
+
+ public InputMismatchException(String msg, Set<String> expectedInputNames, Set<String> providedInputNames) {
+ super(msg);
+ this.expectedInputNames = expectedInputNames;
+ this.providedInputNames = providedInputNames;
+ }
+
+ public String getMessage() {
+ String result = super.getMessage();
+ result += "\n" + expectedInputNames.size() + " inputs were expected";
+ if (expectedInputNames.size()>0) result += " which are:\n";
+ for (String name : expectedInputNames) {
+ result += "'"+name+"' ";
+ }
+
+ result += "\n" + providedInputNames.size() + " inputs were provided";
+ if (providedInputNames.size()>0) result += " which are:\n";
+ for (String name : providedInputNames) {
+ result += "'"+name+"' ";
+ }
+ return result;
+ }
+
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/InvalidOptionException.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/InvalidOptionException.java
new file mode 100644
index 0000000..c07a19f
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/InvalidOptionException.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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.exceptions;
+
+public class InvalidOptionException extends Exception {
+
+ private static final long serialVersionUID = 2467409785164223258L;
+
+ public InvalidOptionException(String message) {
+ super(message);
+ }
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/OpenDataflowException.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/OpenDataflowException.java
new file mode 100644
index 0000000..664ab71
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/OpenDataflowException.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.exceptions;
+
+public class OpenDataflowException extends Exception {
+
+
+ private static final long serialVersionUID = 4778578311101082197L;
+
+ public OpenDataflowException() {
+
+ }
+
+ public OpenDataflowException(String message) {
+ super(message);
+ }
+
+ public OpenDataflowException(Throwable cause) {
+ super(cause);
+ }
+
+ public OpenDataflowException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/ReadInputException.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/ReadInputException.java
new file mode 100644
index 0000000..226d6ea
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/exceptions/ReadInputException.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.exceptions;
+
+public class ReadInputException extends Exception {
+
+ private static final long serialVersionUID = -3494432791254643055L;
+
+ public ReadInputException(String msg) {
+ super(msg);
+ }
+
+ public ReadInputException(String msg, Throwable e) {
+ super(msg,e);
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/options/CommandLineOptions.java b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/options/CommandLineOptions.java
new file mode 100644
index 0000000..ccbd861
--- /dev/null
+++ b/taverna-commandline-common/src/main/java/net/sf/taverna/t2/commandline/options/CommandLineOptions.java
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ * 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 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 JANUS = "janus";
+ private static final String OPM = "opm";
+ public static final String CREDENTIAL_MANAGER_DIR_OPTION = "cmdir";
+ public static final String CREDENTIAL_MANAGER_PASSWORD_OPTION = "cmpassword";
+
+ public CommandLineOptions(String[] args) throws InvalidOptionException {
+ this.options = intitialiseOptions();
+ this.commandLine = processArgs(args);
+ checkForInvalid();
+ }
+
+ public boolean askedForHelp() {
+ return hasOption("help") || (getArgs().length==0 && getOptions().length==0);
+ }
+
+ public boolean isProvenanceEnabled() {
+ return hasOption("provenance") || hasOption(OPM) || hasOption(JANUS);
+ }
+
+ protected void checkForInvalid() throws InvalidOptionException {
+ if (askedForHelp()) return;
+ if (isProvenanceEnabled()
+ && !(hasOption("embedded") || hasOption("clientserver") || hasOption("dbproperties")))
+ throw new InvalidOptionException(
+ "You should be running with a database to use provenance");
+ if (isProvenanceEnabled() && hasOption("inmemory"))
+ throw new InvalidOptionException(
+ "You should be running with a database to use provenance");
+ if ((hasOption("inputfile") || hasOption("inputvalue"))
+ && hasOption("inputdoc"))
+ throw new InvalidOptionException(
+ "You can't provide both -input and -inputdoc arguments");
+
+ if (hasOption("inputdelimiter") && hasOption("inputdoc"))
+ throw new InvalidOptionException("You cannot combine the -inputdelimiter and -inputdoc arguments");
+
+ if (getArgs().length == 0
+ && !(hasOption("help") || hasOption("startdb")))
+ throw new InvalidOptionException("You must specify a workflow");
+
+ if (hasOption("inmemory") && hasOption("embedded"))
+ throw new InvalidOptionException(
+ "The options -embedded, -clientserver and -inmemory cannot be used together");
+ if (hasOption("inmemory") && hasOption("clientserver"))
+ throw new InvalidOptionException(
+ "The options -embedded, -clientserver and -inmemory cannot be used together");
+ if (hasOption("embedded") && hasOption("clientserver"))
+ throw new InvalidOptionException(
+ "The options -embedded, -clientserver and -inmemory cannot be used together");
+ }
+
+ public void displayHelp() {
+ boolean full = false;
+ if (hasOption("help")) full=true;
+ displayHelp(full);
+ }
+
+ public void displayHelp(boolean showFullText) {
+
+ HelpFormatter formatter = new HelpFormatter();
+ try {
+ formatter
+ .printHelp("executeworkflow [options] [workflow]", options);
+ if (showFullText) {
+ InputStream helpStream = CommandLineOptions.class
+ .getClassLoader().getResourceAsStream("help.txt");
+ String helpText = IOUtils.toString(helpStream);
+ System.out.println(helpText);
+ }
+
+ } 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("port");
+ }
+
+ /**
+ *
+ * @return a path to a properties file that contains database configuration
+ * settings
+ */
+ public String getDatabaseProperties() {
+ return getOptionValue("dbproperties");
+ }
+
+ /**
+ *
+ * @return the path to the input document
+ */
+ public String getInputDocument() {
+ return getOptionValue("inputdoc");
+ }
+
+ /**
+ * 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("inputfile");
+ } else {
+ return new String[] {};
+ }
+ }
+
+ public String[] getInputValues() {
+ if (hasInputValues()) {
+ return getOptionValues("inputvalue");
+ } else {
+ return new String[] {};
+ }
+ }
+
+ public String getLogFile() {
+ return getOptionValue("logfile");
+ }
+
+ 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("outputdir");
+ }
+
+ /**
+ *
+ * @return the path to the output document
+ */
+ public String getOutputDocument() {
+ return getOptionValue("outputdoc");
+ }
+
+ public boolean getStartDatabase() {
+ return hasOption("startdb");
+ }
+
+ /**
+ * @return the directory with Credential Manager's files
+ */
+ public String getCredentialManagerDir() {
+ return 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) {
+ boolean result = false;
+ if (hasOption("inputdelimiter")) {
+ String [] values = getOptionValues("inputdelimiter");
+ for (int i=0;i<values.length;i+=2) {
+ if (values[i].equals(inputName))
+ {
+ result=true;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ public boolean hasInputFiles() {
+ return hasOption("inputfile");
+ }
+
+ public boolean hasInputValues() {
+ return hasOption("inputvalue");
+ }
+
+ public boolean hasLogFile() {
+ return hasOption("logfile");
+ }
+
+ public boolean hasOption(String option) {
+ return commandLine.hasOption(option);
+ }
+
+ public String inputDelimiter(String inputName) {
+ String result = null;
+ if (hasOption("inputdelimiter")) {
+ String [] values = getOptionValues("inputdelimiter");
+ for (int i=0;i<values.length;i+=2) {
+ if (values[i].equals(inputName))
+ {
+ result=values[i+1];
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ @SuppressWarnings("static-access")
+ private Options intitialiseOptions() {
+ Option helpOption = new Option("help", "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("outputdir");
+
+ Option outputdocOption = OptionBuilder.withArgName("document").hasArg()
+ .withDescription("Save outputs to a new Baclava document.")
+ .create("outputdoc");
+
+ Option logFileOption = OptionBuilder
+ .withArgName("filename")
+ .hasArg()
+ .withDescription(
+ "The logfile to which more verbose logging will be written to.")
+ .create("logfile");
+
+ Option inputdocOption = OptionBuilder.withArgName("document").hasArg()
+ .withDescription("Load inputs from a Baclava document.").create(
+ "inputdoc");
+
+ Option inputFileOption = OptionBuilder
+ .withArgName("inputname filename").hasArgs(2)
+ .withValueSeparator(' ').withDescription(
+ "Load the named input from file or URL.").create(
+ "inputfile");
+
+ Option inputValueOption = OptionBuilder.withArgName("inputname value")
+ .hasArgs(2).withValueSeparator(' ').withDescription(
+ "Directly use the value for the named input.").create(
+ "inputvalue");
+
+ 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("inputdelimiter");
+
+ Option dbProperties = OptionBuilder.withArgName("filename").hasArg()
+ .withDescription(
+ "Load a properties file to configure the database.")
+ .create("dbproperties");
+
+ 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("port");
+
+ Option embedded = new Option("embedded",
+ "Connect to an embedded Derby database. This can prevent mulitple invocations.");
+ Option clientserver = new Option("clientserver",
+ "Connect as a client to a derby server instance.");
+ Option inMemOption = new Option(
+ "inmemory",
+ "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("startdb",
+ "Automatically start an internal Derby database server.");
+ Option provenance = new Option("provenance",
+ "Generate provenance information and store it in the database.");
+
+
+ Option opm = OptionBuilder
+ .withArgName("file")
+ .hasOptionalArg()
+ .withDescription(
+ "Save Open Provenance Model (OPM) RDF/XML trace of execution to FILE or 'provenance-opm.rdf'.")
+ .create(OPM);
+
+ Option janus = OptionBuilder
+ .withArgName("file")
+ .hasOptionalArg()
+ .withDescription(
+ "Save Janus RDF/XML trace of execution to FILE or 'provenance-janus.rdf'.")
+ .create(JANUS);
+
+ 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 password option, to be read from standard input
+
+ Options options = new Options();
+ options.addOption(helpOption);
+ options.addOption(inputFileOption);
+ options.addOption(inputValueOption);
+ options.addOption(inputDelimiterOption);
+ 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(opm);
+ options.addOption(janus);
+ options.addOption(logFileOption);
+ options.addOption(credentialManagerDirectory);
+ options.addOption(credentialManagerPassword);
+
+ return options;
+ }
+
+ public boolean isClientServer() {
+ return hasOption("clientserver");
+ }
+
+ public boolean isEmbedded() {
+ return hasOption("embedded");
+ }
+
+ public boolean isInMemory() {
+ return hasOption("inmemory");
+ }
+
+ 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 -output has been explicitly defined,
+ * and/or if -outputdoc hasn't been defined
+ *
+ * @return boolean
+ */
+ public boolean saveResultsToDirectory() {
+ return (options.hasOption("outputdir") || !options
+ .hasOption("outputdoc"));
+ }
+
+ public String getJanus() {
+ return getOptionValue(JANUS);
+ }
+ public String getOPM() {
+ return getOptionValue(OPM);
+ }
+
+ public boolean isOPM() {
+ return hasOption(OPM);
+ }
+ public boolean isJanus() {
+ return hasOption(JANUS);
+ }
+
+}
diff --git a/taverna-commandline-common/src/main/resources/META-INF/services/net.sf.taverna.raven.launcher.Launchable b/taverna-commandline-common/src/main/resources/META-INF/services/net.sf.taverna.raven.launcher.Launchable
new file mode 100644
index 0000000..1314920
--- /dev/null
+++ b/taverna-commandline-common/src/main/resources/META-INF/services/net.sf.taverna.raven.launcher.Launchable
@@ -0,0 +1 @@
+net.sf.taverna.t2.commandline.CommandLineLauncher
\ No newline at end of file
diff --git a/taverna-commandline-common/src/main/resources/cl-log4j.properties b/taverna-commandline-common/src/main/resources/cl-log4j.properties
new file mode 100644
index 0000000..8c17461
--- /dev/null
+++ b/taverna-commandline-common/src/main/resources/cl-log4j.properties
@@ -0,0 +1,27 @@
+# By default, WARN (our external libraries)
+log4j.rootLogger=INFO, CONSOLE
+
+# Log INFO from anything Taverna
+log4j.logger.net.sf.taverna.t2=INFO
+
+
+
+# Restrict logging from classes that like to complain a lot
+log4j.logger.net.sf.taverna.raven=WARN
+log4j.logger.net.sf.taverna.t2.workbench.configuration.ConfigurationManager=WARN
+log4j.logger.net.sf.taverna.t2.workbench.views.graph.GraphViewComponent=WARN
+log4j.logger.net.sf.taverna.t2.workbench.ui.impl.WorkbenchPerspectives=WARN
+log4j.logger.net.sf.taverna.t2.workbench.ui.impl.DataflowEditsListener=WARN
+log4j.logger.net.sf.taverna.t2.workbench.ui.views.contextualviews.annotated.AnnotatedContextualView=WARN
+log4j.logger.net.sf.taverna.t2.workbench.ui.workflowview.WorkflowView=WARN
+log4j.logger.net.sf.taverna.t2.ui.menu.impl.MenuManagerImpl=ERROR
+
+log4j.logger.org.apache.commons.httpclient=ERROR
+
+
+# Default output to console is restrictive
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%-5p %d{ISO8601} (%c:%L) - %m%n
+# Output WARN and above to console -
+log4j.appender.CONSOLE.Threshold=ERROR
\ No newline at end of file
diff --git a/taverna-commandline-common/src/main/resources/database-defaults.properties b/taverna-commandline-common/src/main/resources/database-defaults.properties
new file mode 100644
index 0000000..1080856
--- /dev/null
+++ b/taverna-commandline-common/src/main/resources/database-defaults.properties
@@ -0,0 +1,11 @@
+in_memory = true
+provenance = false
+connector = derby
+port = 1527
+dialect = org.hibernate.dialect.DerbyDialect
+start_derby = false
+pool_max_active = 50
+pool_min_idle = 50
+pool_max_idle = 10
+driver = org.apache.derby.jdbc.EmbeddedDriver
+jdbcuri = jdbc:derby:t2-database;create=true;upgrade=true
\ No newline at end of file
diff --git a/taverna-commandline-common/src/main/resources/help.txt b/taverna-commandline-common/src/main/resources/help.txt
new file mode 100644
index 0000000..e2103a1
--- /dev/null
+++ b/taverna-commandline-common/src/main/resources/help.txt
@@ -0,0 +1,87 @@
+By default, the workflow is executed using the -inmemory option, and the
+results are written out to a directory named after the workflow name.
+
+If this directory already exists then a new directory is created, and
+appended with _<n>, where n is incremented to the next available index.
+
+Results are written out to files named after the output port for that result.
+If a result is composed of lists, then a directory is created for the output
+port and individual list items are named after the list element index (with 1
+being the first index). The the output is the result of an error, the filename
+is appended with '.error'.
+
+You can provide your own output directory with the -outputdir option. There
+will be an error if the directory already exists.
+
+You can also record your results to a Baclava document using -outputdoc
+option. The document will be overwritten if it already exists.
+
+Inputs can be provided in three ways. Both -inputfile and -inputvalue options
+can be used together; -inputdoc option must be used on its own. -inputfile and
+-inputvalue options both take two additional arguments, the name of the port
+for the input, and either a file containing the input data, or the input value
+itself respectively.
+
+If one of more of your workflow inputs is a list, you can create a list
+input by using the -inputdelimiter option, which may be used with either
+-inputfile or -inputvalue. This option takes two parameters - an input name
+and the delimiter by which to split the input into a list.
+
+The delimiter may be a simple character, such as a comma or a new-line
+character, or a regular expression. The input string, or file, will then be
+converted into a list being split by the delimiter specified. Make sure to
+put the delimiter character in quotes as it may be interpreted by the shell
+as a special character, e.g. ;.
+
+If a list of greater depth (i.e. a list or lists or deeper) is required then
+you will need to use the -inputdoc option. However, if you provide an input
+of lower depth to that required, then it will automatically be wrapped in one
+or more lists up to the required depth. Providing an input of greater depth
+than that required will result in an error.
+
+If a workflow has a high memory requirement, then it may be better to run it
+using a database to store data rather than storing it in memory, which is the
+default option. There are three options for using a database:
+
+-embedded option, runs with an embedded database. This is slightly faster than
+the -clientserver option (below), but has the limitation that only one
+executeworkflow script may be executed simultaneously.
+
+-clientserver option allows the workflow to be executed backed by the database
+running as a server. By default a database is not started for you, but may be
+started using -startdb option.
+
+-startdb option starts a database. It may be used without providing a workflow
+to allow a database to be started separately, allowing multiple simultaneous
+executeworkflow script runs.
+
+More advanced database configurations can be specified using -dbproperties
+option, allowing you to take full control over the database used. This takes a
+second argument, the filename of the propeties file, for which the following
+example contains the default settings used:
+
+in_memory = true
+provenance = false
+connector = derby
+port = 1527
+dialect = org.hibernate.dialect.DerbyDialect
+start_derby = false
+driver = org.apache.derby.jdbc.EmbeddedDriver
+jdbcuri = jdbc:derby:t2-database;create=true;upgrade=true
+
+Note that when using -dbproperties together with other options, the other
+options take precedence.
+
+-cmdir option lets you specify an absolute path to a directory where
+Credential Manager's files (keystore and truststore - containing user's
+credentials and trusted certificates for accessing secure services) are stored.
+If not specified and the workflow requires access to these files, Taverna will
+try to find them in the default location in <TAVERNA_HOME>/security somewhere
+inside user's home directory (depending on the platform).
+
+-cmpassword option can be used to tell Taverna to expect the password for the
+Credential Manager on standard input. If the password is not piped in, Taverna
+will prompt you for it in the terminal and block until it is entered. Do not
+enter your password in the command line! If -cmpassword option is not specified
+and -cmdir option is used, Taverna will try to find the password in a special
+file password.txt in the directory specified with -cmdir option.
diff --git a/taverna-commandline-common/src/test/java/net/sf/taverna/t2/commandline/TestCommandLineOptionsHandler.java b/taverna-commandline-common/src/test/java/net/sf/taverna/t2/commandline/TestCommandLineOptionsHandler.java
new file mode 100644
index 0000000..dddeba6
--- /dev/null
+++ b/taverna-commandline-common/src/test/java/net/sf/taverna/t2/commandline/TestCommandLineOptionsHandler.java
@@ -0,0 +1,266 @@
+package net.sf.taverna.t2.commandline;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import net.sf.taverna.t2.commandline.exceptions.InvalidOptionException;
+import net.sf.taverna.t2.commandline.options.CommandLineOptions;
+
+import org.junit.Test;
+
+public class TestCommandLineOptionsHandler {
+
+ @Test
+ public void testWorkflowName() throws Exception {
+ CommandLineOptions handler = new CommandLineOptions(
+ new String[] { "myworkflow.t2flow" });
+ assertEquals("myworkflow.t2flow", handler.getWorkflow());
+ }
+
+ @Test
+ public void shouldShowHelp() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(
+ new String[] { "-help" });
+ assertTrue(options.askedForHelp());
+ options = new CommandLineOptions(
+ new String[] {});
+ assertTrue(options.askedForHelp());
+ options = new CommandLineOptions(new String[] { "myworkflow.t2flow" });
+ assertFalse(options.askedForHelp());
+ }
+
+ @Test
+ public void getWorkflow() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(
+ new String[] { "-help" });
+ assertNull(options.getWorkflow());
+ options = new CommandLineOptions(new String[] { "myworkflow.t2flow" });
+ assertEquals("myworkflow.t2flow", options.getWorkflow());
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void cannotProvideInputFileAndInputDoc() throws Exception {
+ new CommandLineOptions(new String[] { "-inputfile", "fred", "fred.txt",
+ "-inputdoc", "myworkflow.t2flow" });
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void cannotProvideInputValueAndInputDoc() throws Exception {
+ new CommandLineOptions(new String[] { "-inputvalue", "fred", "fred.txt",
+ "-inputdoc", "myworkflow.t2flow" });
+ }
+
+ @Test
+ public void canProvideInputValueAndFileTogether() throws Exception {
+ //should not be an error
+ new CommandLineOptions(new String[] { "-inputvalue", "fred", "abc",
+ "-inputfile","fred2","fred2.txt","myworkflow.t2flow" });
+ }
+
+ @Test
+ public void getInputs() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inputfile", "fred", "fred.txt", "myworkflow.t2flow" });
+ assertEquals(2, options.getInputFiles().length);
+ assertEquals("fred", options.getInputFiles()[0]);
+ assertEquals("fred.txt", options.getInputFiles()[1]);
+
+ options = new CommandLineOptions(new String[] { "-inputfile", "fred",
+ "fred.txt", "-inputfile", "fred2", "fred2.txt",
+ "myworkflow.t2flow" });
+ assertEquals(4, options.getInputFiles().length);
+ assertEquals("fred", options.getInputFiles()[0]);
+ assertEquals("fred.txt", options.getInputFiles()[1]);
+ assertEquals("fred2", options.getInputFiles()[2]);
+ assertEquals("fred2.txt", options.getInputFiles()[3]);
+
+ options = new CommandLineOptions(new String[] { "myworkflow.t2flow" });
+ assertNotNull(options.getInputFiles());
+ assertEquals(0, options.getInputFiles().length);
+
+ }
+
+ @Test
+ public void hasInputValue() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inputvalue", "fred", "abc", "myworkflow.t2flow" });
+ assertTrue(options.hasInputValues());
+
+ options = new CommandLineOptions(new String[] { "myworkflow.t2flow" });
+ assertFalse(options.hasInputValues());
+ }
+
+ @Test
+ public void getInputValues() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inputvalue", "fred", "abc", "myworkflow.t2flow" });
+ assertEquals(2, options.getInputValues().length);
+
+ options = new CommandLineOptions(new String[] { "myworkflow.t2flow" });
+ assertNotNull(options.getInputValues());
+ assertEquals(0,options.getInputValues().length);
+ }
+
+ @Test
+ public void hasInputs() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inputfile", "fred", "fred.txt", "myworkflow.t2flow" });
+ assertTrue(options.hasInputFiles());
+
+ options = new CommandLineOptions(new String[] { "myworkflow.t2flow" });
+ assertFalse(options.hasInputFiles());
+ }
+
+ @Test
+ public void noWorkflowNameButStartDB() throws Exception {
+ // should not throw an error
+ CommandLineOptions options = new CommandLineOptions(
+ new String[] { "-startdb" });
+ assertTrue(options.getStartDatabase());
+ assertTrue(options.getStartDatabaseOnly());
+ }
+
+ @Test
+ public void workflowNameAndStartDB() throws Exception {
+ // should not throw an error
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-startdb", "myworkflow.t2flow" });
+ assertTrue(options.getStartDatabase());
+ assertFalse(options.getStartDatabaseOnly());
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void provenanceButNoDatabase() throws Exception {
+ new CommandLineOptions(new String[] { "-provenance",
+ "myworkflow.t2flow" });
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void provenanceButNoDatabase2() throws Exception {
+ new CommandLineOptions(new String[] { "-provenance", "-inmemory",
+ "myworkflow.t2flow" });
+ }
+
+ @Test
+ public void provenanceDatabase() throws Exception {
+ // should be no errors
+ new CommandLineOptions(new String[] { "-provenance", "-embedded",
+ "myworkflow.t2flow" });
+ new CommandLineOptions(new String[] { "-provenance", "-clientserver",
+ "myworkflow.t2flow" });
+ new CommandLineOptions(new String[] { "-provenance", "-dbproperties",
+ "dbproperties.properties", "myworkflow.t2flow" });
+ }
+
+ @Test
+ public void testHasInputDelimiter() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inputvalue","in1","1,2,3","-inputdelimiter","in1",",","-inputdelimiter","in2",",","myworkflow.t2flow" });
+ assertTrue(options.hasDelimiterFor("in1"));
+ assertTrue(options.hasDelimiterFor("in2"));
+ assertFalse(options.hasDelimiterFor("in3"));
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void testInputDelimiterInvalidWithInputDoc() throws Exception {
+ new CommandLineOptions(new String[] {
+ "-inputdoc","doc.xml","-inputdelimiter","in1",",","myworkflow.t2flow" });
+ }
+
+
+ @Test
+ public void testInputDelimiter() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inputvalue","in1","1,2,3","-inputdelimiter","in1",",","-inputdelimiter","in2","!","myworkflow.t2flow" });
+ assertEquals(",",options.inputDelimiter("in1"));
+ assertEquals("!",options.inputDelimiter("in2"));
+ assertNull(options.inputDelimiter("in3"));
+ }
+
+ @Test
+ public void testInMemory() throws Exception {
+ CommandLineOptions handler = new CommandLineOptions(new String[] {
+ "-inmemory", "myworkflow.t2flow" });
+ assertTrue(handler.hasOption("inmemory"));
+ }
+
+ @Test
+ public void testEmbedded() throws Exception {
+ CommandLineOptions handler = new CommandLineOptions(new String[] {
+ "-embedded", "myworkflow.t2flow" });
+ assertTrue(handler.hasOption("embedded"));
+ }
+
+ @Test
+ public void testClientServer() throws Exception {
+ CommandLineOptions handler = new CommandLineOptions(new String[] {
+ "-clientserver", "myworkflow.t2flow" });
+ assertTrue(handler.hasOption("clientserver"));
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void testInvalidEmbeddedAndClientServer() throws Exception {
+ new CommandLineOptions(new String[] { "-clientserver", "-embedded",
+ "myworkflow.t2flow" });
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void testInvalidEmbeddedAndMemory() throws Exception {
+ new CommandLineOptions(new String[] { "-embedded", "-inmemory",
+ "myworkflow.t2flow" });
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void testInvalidClientServerAndInMemory() throws Exception {
+ new CommandLineOptions(new String[] { "-clientserver", "-inmemory",
+ "myworkflow.t2flow" });
+ }
+
+ @Test
+ public void isInMemory() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-inmemory", "myworkflow.t2flow" });
+
+ assertTrue(options.isInMemory());
+ assertFalse(options.isClientServer());
+ assertFalse(options.isEmbedded());
+ }
+
+ @Test
+ public void isClientServer() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-clientserver", "myworkflow.t2flow" });
+
+ assertTrue(options.isClientServer());
+ assertFalse(options.isInMemory());
+ assertFalse(options.isEmbedded());
+ }
+
+ @Test
+ public void hasLogFile() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-logfile","/tmp/logging", "myworkflow.t2flow" });
+
+ assertTrue(options.hasLogFile());
+ assertEquals("/tmp/logging", options.getLogFile());
+ }
+
+ @Test(expected = InvalidOptionException.class)
+ public void hasLogFileNotValidWithoutWorkflow() throws Exception{
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-logfile","/tmp/logging"});
+ }
+
+ @Test
+ public void isEmbedded() throws Exception {
+ CommandLineOptions options = new CommandLineOptions(new String[] {
+ "-embedded", "myworkflow.t2flow" });
+
+ assertTrue(options.isEmbedded());
+ assertFalse(options.isInMemory());
+ assertFalse(options.isClientServer());
+ }
+
+}
diff --git a/taverna-commandline-common/src/test/java/net/sf/taverna/t2/commandline/TestDatabaseConfigurationHandler.java b/taverna-commandline-common/src/test/java/net/sf/taverna/t2/commandline/TestDatabaseConfigurationHandler.java
new file mode 100644
index 0000000..555a1a6
--- /dev/null
+++ b/taverna-commandline-common/src/test/java/net/sf/taverna/t2/commandline/TestDatabaseConfigurationHandler.java
@@ -0,0 +1,20 @@
+package net.sf.taverna.t2.commandline;
+
+import static org.junit.Assert.assertEquals;
+import net.sf.taverna.t2.commandline.data.DatabaseConfigurationHandler;
+import net.sf.taverna.t2.commandline.options.CommandLineOptions;
+import net.sf.taverna.t2.workbench.reference.config.DataManagementConfiguration;
+
+import org.junit.Test;
+
+public class TestDatabaseConfigurationHandler {
+
+ @Test
+ public void testDefaults() throws Exception {
+ CommandLineOptions opts = new CommandLineOptions(new String[]{"myworkflow.t2flow"});
+ DatabaseConfigurationHandler handler = new DatabaseConfigurationHandler(opts);
+ handler.configureDatabase();
+ assertEquals("org.apache.derby.jdbc.EmbeddedDriver", DataManagementConfiguration.getInstance().getDriverClassName());
+ assertEquals(false, DataManagementConfiguration.getInstance().getStartInternalDerbyServer());
+ }
+}