blob: 1e6ef77f3696debfd00eb232a58c42365170cd50 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2009 Hajo Nils Krabbenhoeft, INB, University of Luebeck
*
* 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.activities.externaltool;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.taverna.t2.activities.externaltool.manager.InvocationGroup;
import net.sf.taverna.t2.activities.externaltool.manager.InvocationMechanism;
import net.sf.taverna.t2.annotation.Annotated;
import net.sf.taverna.t2.annotation.annotationbeans.MimeType;
import net.sf.taverna.t2.reference.ExternalReferenceSPI;
import net.sf.taverna.t2.reference.ReferenceService;
import net.sf.taverna.t2.reference.T2Reference;
import net.sf.taverna.t2.reference.WorkflowRunIdEntity;
import net.sf.taverna.t2.workflowmodel.EditException;
import net.sf.taverna.t2.workflowmodel.processor.activity.AbstractAsynchronousActivity;
import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityConfigurationException;
import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
import net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivityCallback;
import org.apache.log4j.Logger;
import de.uni_luebeck.inb.knowarc.usecases.ScriptInput;
import de.uni_luebeck.inb.knowarc.usecases.ScriptInputUser;
import de.uni_luebeck.inb.knowarc.usecases.ScriptOutput;
import de.uni_luebeck.inb.knowarc.usecases.UseCaseDescription;
import de.uni_luebeck.inb.knowarc.usecases.invocation.InvocationException;
import de.uni_luebeck.inb.knowarc.usecases.invocation.UseCaseInvocation;
/**
* This is the main class of the use case activity plugin. Here we store the
* configuration and the description of a use case activity, configure the input
* and output port and provide use case activity invocation
*
* @author Hajo Nils Krabbenhoeft
*/
public class ExternalToolActivity extends AbstractAsynchronousActivity<ExternalToolActivityConfigurationBean> {
public static final String URI = "http://ns.taverna.org.uk/2010/activity/tool";
private static final String STDERR = "STDERR";
private static final String STDOUT = "STDOUT";
private static final String STDIN = "STDIN";
private static Logger logger = Logger.getLogger(ExternalToolActivity.class);
private ExternalToolActivityConfigurationBean configurationBean;
private UseCaseDescription mydesc;
private List<InvocationCreator> invocationCreators;
/**
* Add the given MIME types to the given input/output port.
*
* @param annotated
* The port to which to add the MIME types.
* @param mimeTypes
* A list of Strings specifying the MIME types to add.
*/
private void addMimeTypes(Annotated<?> annotated, List<String> mimeTypes) {
for (String mimeType : mimeTypes) {
MimeType mimeTypeAnnotation = new MimeType();
mimeTypeAnnotation.setText(mimeType);
try {
getEdits().getAddAnnotationChainEdit(annotated, mimeTypeAnnotation).doEdit();
} catch (EditException e) {
Logger.getLogger(ExternalToolActivity.class).error(e);
}
}
}
/**
* Create a new input port with the given name, depth, element class and
* MIME types.
*
* @param portName
* Name of the new port
* @param portDepth
* Depth of the new port
* @param translatedElementClass
* Which class of elements would this port like?
* @param mimeTypes
* Accepted mime types for this port
*/
private void addInputWithMime(String portName, int portDepth, Class<?> translatedElementClass, List<String> mimeTypes) {
List<Class<? extends ExternalReferenceSPI>> handledReferenceSchemes = Collections.emptyList();
ActivityInputPort inputPort = getEdits().createActivityInputPort(portName, portDepth, true, handledReferenceSchemes,
translatedElementClass);
inputPorts.add(inputPort);
if (mimeTypes != null) {
addMimeTypes(inputPort, mimeTypes);
}
}
/**
* Create a new output port with the given MIME types
*
* @param portName
* Name of the new port
* @param portDepth
* Depth of the new port
* @param mimeTypes
* Accepted mime types for this port
*/
private void addOutputWithMime(String portName, int portDepth, List<String> mimeTypes) {
ActivityOutputPort outputPort = getEdits().createActivityOutputPort(portName, portDepth, portDepth);
outputPorts.add(outputPort);
addMimeTypes(outputPort, mimeTypes);
}
@Override
public void configure(ExternalToolActivityConfigurationBean bean) throws ActivityConfigurationException {
this.configurationBean = bean;
try {
mydesc = bean.getUseCaseDescription();
inputPorts.clear();
outputPorts.clear();
if (mydesc != null) {
// loop through all script inputs and add them as taverna activity
// input ports
for (Map.Entry<String, ScriptInput> cur : mydesc.getInputs().entrySet()) {
ScriptInputUser scriptInputUser = (ScriptInputUser) cur.getValue();
// if the input port is a list, depth is 1 otherwise it is a
// single element, therefore depth 0
// if the input port is binary, we would like byte arrays,
// otherwise we require strings
addInputWithMime(cur.getKey(), scriptInputUser.isList() ? 1 : 0, cur.getValue().isBinary() ? byte[].class : String.class, scriptInputUser.getMime());
}
// loop through all script outputs and add them to taverna
for (Map.Entry<String, ScriptOutput> cur : mydesc.getOutputs().entrySet()) {
addOutputWithMime(cur.getKey(), 0, cur.getValue().getMime());
}
}
if (mydesc.isIncludeStdIn()) {
addInputWithMime(STDIN, 0, byte[].class, null);
}
if (mydesc.isIncludeStdOut()) {
addOutput(STDOUT, 0);
}
if (mydesc.isIncludeStdErr()) {
addOutput(STDERR, 0);
}
} catch (Exception e) {
throw new ActivityConfigurationException("Couldn't create ExternalTool Activity", e);
}
}
@Override
public ExternalToolActivityConfigurationBean getConfiguration() {
if (configurationBean != null) {
InvocationGroup invocationGroup = configurationBean.getInvocationGroup();
if (invocationGroup == null) {
if (configurationBean.getMechanism() != null) {
configurationBean.convertMechanismToDetails();
}
} else {
if (invocationGroup.getMechanism() != null) {
invocationGroup.convertMechanismToDetails();
}
}
}
return configurationBean;
}
public ExternalToolActivityConfigurationBean getConfigurationNoConversion() {
return configurationBean;
}
public InvocationMechanism recreateMechanism() {
if (configurationBean.getInvocationGroup() != null) {
if (configurationBean.getInvocationGroup().getMechanism() == null) {
configurationBean.getInvocationGroup().convertDetailsToMechanism();
}
return configurationBean.getInvocationGroup().getMechanism();
} else {
if (configurationBean.getMechanism() == null) {
configurationBean.convertDetailsToMechanism();
}
return configurationBean.getMechanism();
}
}
@Override
public void executeAsynch(final Map<String, T2Reference> data, final AsynchronousActivityCallback callback) {
callback.requestRun(new Runnable() {
public void run() {
ReferenceService referenceService = callback.getContext().getReferenceService();
UseCaseInvocation invoke = null;
/**
* Note that retrying needs to be either done via Taverna's retry mechanism or as part of the specific invocation
*/
try {
invoke = getInvocation(recreateMechanism(),
configurationBean.getUseCaseDescription(), data, referenceService);
if (invoke == null) {
logger.error("Invoke is null");
callback.fail("No invocation mechanism found");
}
String runId = callback.getContext()
.getEntities(WorkflowRunIdEntity.class).get(0)
.getWorkflowRunId();
logger.info("Run id is " + runId);
invoke.rememberRun(runId);
invoke.setContext(callback.getContext());
// look at every use dynamic case input
for (String cur : invoke.getInputs()) {
if (!cur.equals(STDIN)) {
invoke.setInput(cur, referenceService,
data.get(cur));
}
}
if (mydesc.isIncludeStdIn() && (data.get(STDIN) != null)) {
invoke.setStdIn(referenceService, data.get(STDIN));
}
// submit the use case to its invocation mechanism
invoke.submit_generate_job(referenceService);
// retrieve the result.
Map<String, Object> downloads = invoke
.submit_wait_fetch_results(referenceService);
Map<String, T2Reference> result = new HashMap<String, T2Reference>();
for (Map.Entry<String, Object> cur : downloads.entrySet()) {
Object value = cur.getValue();
// register the result value with taverna
T2Reference reference = referenceService.register(
value, 0, true, callback.getContext());
// store the reference into the activity result
// set
result.put(cur.getKey(), reference);
}
callback.receiveResult(result, new int[0]);
} catch (InvocationException e) {
callback.fail(e.getMessage(), e);
}
}
});
}
public void setInvocationCreators(List<InvocationCreator> invocationCreators) {
this.invocationCreators = invocationCreators;
}
private UseCaseInvocation getInvocation(InvocationMechanism mechanism, UseCaseDescription description, Map<String, T2Reference> data, ReferenceService referenceService) {
UseCaseInvocation result = null;
InvocationCreator creator = null;
for (InvocationCreator c : invocationCreators) {
if (c.canHandle(mechanism.getType())) {
creator = c;
break;
}
}
if (creator != null) {
result = creator.convert(mechanism, description, data, referenceService);
}
return result;
}
}