blob: b87d35af09b2f7ae490a01b92e9743e33a6b8c1e [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007-2010 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.workbench.run.actions;
import static java.awt.Frame.ICONIFIED;
import static java.awt.Frame.NORMAL;
import static java.awt.Toolkit.getDefaultToolkit;
import static java.awt.event.KeyEvent.VK_R;
import static javax.swing.KeyStroke.getKeyStroke;
import static javax.swing.SwingUtilities.invokeLater;
import static net.sf.taverna.t2.reference.ui.InvalidDataflowReport.showErrorDialog;
import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.runIcon;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.swing.AbstractAction;
import net.sf.taverna.t2.lang.observer.Observable;
import net.sf.taverna.t2.lang.observer.Observer;
import net.sf.taverna.t2.reference.ui.CopyWorkflowInProgressDialog;
import net.sf.taverna.t2.reference.ui.CopyWorkflowSwingWorker;
import net.sf.taverna.t2.reference.ui.WorkflowLaunchWindow;
import net.sf.taverna.t2.reference.ui.referenceactions.ReferenceActionSPI;
import net.sf.taverna.t2.workbench.edits.EditManager;
import net.sf.taverna.t2.workbench.file.FileManager;
import net.sf.taverna.t2.workbench.file.events.ClosedDataflowEvent;
import net.sf.taverna.t2.workbench.file.events.FileManagerEvent;
import net.sf.taverna.t2.workbench.report.ReportManager;
import net.sf.taverna.t2.workbench.selection.SelectionManager;
import net.sf.taverna.t2.workbench.ui.SwingWorkerCompletionWaiter;
import net.sf.taverna.t2.workbench.ui.Workbench;
import org.apache.log4j.Logger;
import org.purl.wf4ever.robundle.Bundle;
import uk.org.taverna.databundle.DataBundles;
import uk.org.taverna.platform.execution.api.ExecutionEnvironment;
import uk.org.taverna.platform.execution.api.InvalidExecutionIdException;
import uk.org.taverna.platform.execution.api.InvalidWorkflowException;
import uk.org.taverna.platform.run.api.InvalidRunIdException;
import uk.org.taverna.platform.run.api.RunProfile;
import uk.org.taverna.platform.run.api.RunProfileException;
import uk.org.taverna.platform.run.api.RunService;
import uk.org.taverna.platform.run.api.RunStateException;
import uk.org.taverna.scufl2.api.container.WorkflowBundle;
import uk.org.taverna.scufl2.api.profiles.Profile;
/**
* Run the current workflow (with workflow input dialogue if needed) and add it
* to the list of runs.
* <p>
* Note that running a workflow will force a clone of the WorkflowBundle, allowing further edits to
* the current WorkflowBundle without obstructing the run.
*/
@SuppressWarnings("serial")
public class RunWorkflowAction extends AbstractAction {
private static Logger logger = Logger.getLogger(RunWorkflowAction.class);
/**
* A map of workflows and their corresponding {@link WorkflowLaunchWindow}s.
* We only create one window per workflow and then update its content if the
* workflow gets updated
*/
private static HashMap<WorkflowBundle, WorkflowLaunchWindow> workflowLaunchWindowMap = new HashMap<>();
private final EditManager editManager;
private final FileManager fileManager;
private final ReportManager reportManager;
private final Workbench workbench;
private final RunService runService;
private final SelectionManager selectionManager;
public RunWorkflowAction(EditManager editManager, FileManager fileManager,
ReportManager reportManager, Workbench workbench, RunService runService,
SelectionManager selectionManager) {
this.editManager = editManager;
this.fileManager = fileManager;
this.reportManager = reportManager;
this.workbench = workbench;
this.runService = runService;
this.selectionManager = selectionManager;
putValue(SMALL_ICON, runIcon);
putValue(NAME, "Run workflow...");
putValue(SHORT_DESCRIPTION, "Run the current workflow");
putValue(MNEMONIC_KEY, VK_R);
putValue(
ACCELERATOR_KEY,
getKeyStroke(VK_R, getDefaultToolkit().getMenuShortcutKeyMask()));
fileManager.addObserver(new Observer<FileManagerEvent>() {
@Override
public void notify(Observable<FileManagerEvent> sender, FileManagerEvent message)
throws Exception {
if (message instanceof ClosedDataflowEvent)
workflowLaunchWindowMap
.remove(((ClosedDataflowEvent) message)
.getDataflow());
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
final WorkflowBundle workflowBundle = selectionManager.getSelectedWorkflowBundle();
final Profile profile = selectionManager.getSelectedProfile();
Set<ExecutionEnvironment> executionEnvironments = runService
.getExecutionEnvironments(profile);
if (executionEnvironments.isEmpty()) {
showErrorDialog(
"There are no execution environments capable of running this workflow",
"Can't run workflow");
return;
}
// TODO ask user to choose execution environment
final ExecutionEnvironment executionEnvironment = executionEnvironments.iterator().next();
try {
if (validate(workflowBundle, profile)) {
if (workflowBundle.getMainWorkflow().getInputPorts().isEmpty()) {
final Bundle bundle = DataBundles.createBundle();
invokeLater(new Runnable() {
@Override
public void run() {
runWorkflow(workflowBundle, profile,
executionEnvironment, bundle);
}
});
} else // workflow had inputs - show the input dialog
invokeLater(new Runnable() {
@Override
public void run() {
showInputDialog(workflowBundle, profile,
executionEnvironment);
}
});
}
} catch (Exception ex) {
String message = "Could not run workflow " + workflowBundle.getName();
logger.warn(message);
showErrorDialog(ex.getMessage(), message);
}
}
// TODO update to use Scufl2 validation
private boolean validate(WorkflowBundle workflowBundle, Profile selectedProfile) {
//CheckWorkflowStatus.checkWorkflow(selectedProfile, workbench, editManager,
// fileManager,reportManager);
return true;
}
private void runWorkflow(WorkflowBundle workflowBundle, Profile profile,
ExecutionEnvironment executionEnvironment, Bundle workflowInputs) {
try {
RunProfile runProfile = createRunProfile(workflowBundle, profile,
executionEnvironment, workflowInputs);
if (runProfile != null) {
String runId = runService.createRun(runProfile);
runService.start(runId);
}
} catch (InvalidWorkflowException | RunProfileException | InvalidRunIdException
| RunStateException | InvalidExecutionIdException e) {
String message = "Could not run workflow " + workflowBundle.getName();
logger.warn(message, e);
showErrorDialog(e.getMessage(), message);
}
}
private RunProfile createRunProfile(WorkflowBundle workflowBundle, Profile profile,
ExecutionEnvironment executionEnvironment, Bundle inputDataBundle) {
/*
* Make a copy of the workflow to run so user can still modify the
* original workflow
*/
WorkflowBundle workflowBundleCopy = null;
/*
* CopyWorkflowSwingWorker will make a copy of the workflow and pop up a
* modal dialog that will block the GUI while CopyWorkflowSwingWorker is
* doing it to let the user know that something is being done. Blocking
* of the GUI is needed here so that the user cannot modify the original
* workflow while it is being copied.
*/
CopyWorkflowSwingWorker copyWorkflowSwingWorker = new CopyWorkflowSwingWorker(
workflowBundle);
CopyWorkflowInProgressDialog dialog = new CopyWorkflowInProgressDialog();
copyWorkflowSwingWorker.addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog));
copyWorkflowSwingWorker.execute();
/*
* Give a chance to the SwingWorker to finish so we do not have to
* display the dialog if copying of the workflow is quick (so it won't
* flicker on the screen)
*/
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// do nothing
}
if (!copyWorkflowSwingWorker.isDone())
dialog.setVisible(true); // this will block the GUI
// see if user cancelled the dialog
boolean userCancelled = dialog.hasUserCancelled();
if (userCancelled) {
// Stop the CopyWorkflowSwingWorker if it is still working
copyWorkflowSwingWorker.cancel(true);
return null;
}
// Get the workflow copy from the copyWorkflowSwingWorker
try {
workflowBundleCopy = copyWorkflowSwingWorker.get();
} catch (InterruptedException | ExecutionException e) {
logger.error("Failed to get the workflow copy", e);
}
if (workflowBundleCopy == null) {
showErrorDialog("Unable to make a copy of the workflow to run",
"Workflow copy failed");
return null;
}
return new RunProfile(executionEnvironment, workflowBundleCopy,
workflowBundleCopy.getMainWorkflow().getName(),
profile.getName(), inputDataBundle);
}
private void showInputDialog(final WorkflowBundle workflowBundle,
final Profile profile,
final ExecutionEnvironment executionEnvironment) {
// Get the WorkflowLauchWindow
WorkflowLaunchWindow launchWindow = null;
synchronized (workflowLaunchWindowMap) {
WorkflowLaunchWindow savedLaunchWindow = workflowLaunchWindowMap
.get(workflowBundle);
if (savedLaunchWindow == null) {
launchWindow = new WorkflowLaunchWindow(
workflowBundle.getMainWorkflow(), editManager,
fileManager, reportManager, workbench,
new ArrayList<ReferenceActionSPI>(), null) {
@Override
public void handleLaunch(Bundle workflowInputs) {
runWorkflow(workflowBundle, profile,
executionEnvironment, workflowInputs);
//TODO T2 now makes the launch window vanish
setState(ICONIFIED); // minimise the window
}
@Override
public void handleCancel() {
// Keep the window so we do not have to rebuild it again
setVisible(false);
}
};
/*
* Add this window to the map of the workflow input/launch
* windows
*/
workflowLaunchWindowMap.put(workflowBundle, launchWindow);
launchWindow.setLocationRelativeTo(null);
} else
launchWindow = savedLaunchWindow;
// Display the window
launchWindow.setVisible(true);
/*
* On Win XP setting the window visible seems not to be enough to
* bring the window up if it was minimised previously so we restore
* it here
*/
if (launchWindow.getState() == ICONIFIED)
launchWindow.setState(NORMAL); // restore the window
}
}
}