/******************************************************************************* * Copyright 2006 - 2012 Vienna University of Technology, * Department of Software Technology and Interactive Systems, IFS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. ******************************************************************************/ package eu.scape_project.planning.services.evaluation.taverna; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.scape_project.planning.evaluation.EvaluatorException; import eu.scape_project.planning.evaluation.IObjectEvaluator; import eu.scape_project.planning.evaluation.IStatusListener; import eu.scape_project.planning.manager.CriteriaManager; import eu.scape_project.planning.model.Alternative; import eu.scape_project.planning.model.DigitalObject; import eu.scape_project.planning.model.SampleObject; import eu.scape_project.planning.model.measurement.Measure; import eu.scape_project.planning.model.values.Value; import eu.scape_project.planning.services.PlanningServiceException; import eu.scape_project.planning.services.myexperiment.MyExperimentRESTClient; import eu.scape_project.planning.services.myexperiment.MyExperimentRESTClient.ComponentQuery; import eu.scape_project.planning.services.myexperiment.domain.ComponentConstants; import eu.scape_project.planning.services.myexperiment.domain.Port; import eu.scape_project.planning.services.myexperiment.domain.WorkflowDescription; import eu.scape_project.planning.services.myexperiment.domain.WorkflowInfo; import eu.scape_project.planning.services.taverna.executor.SSHTavernaExecutor; import eu.scape_project.planning.services.taverna.executor.TavernaExecutorException; import eu.scape_project.planning.utils.FileUtils; /** * Evaluation service for executing evaluations using taverna on a remote server * via SSH. */ public class SSHTavernaEvaluationService implements IObjectEvaluator { private static Logger LOG = LoggerFactory.getLogger(SSHTavernaEvaluationService.class); private final CriteriaManager cm = new CriteriaManager(); private MyExperimentRESTClient myExperiment = new MyExperimentRESTClient(); /** * Creates a new SSH taverna evaluation service. */ public SSHTavernaEvaluationService() { cm.init(); } @Override public HashMap<String, Value> evaluate(Alternative alternative, SampleObject sample, DigitalObject result, List<String> measureUris, IStatusListener listener) throws EvaluatorException { HashMap<String, Value> results = new HashMap<String, Value>(); if (result == null) { return results; } Set<String> processedMeasures = new HashSet<String>(measureUris.size()); try { for (String measure : measureUris) { if (!processedMeasures.contains(measure)) { List<WorkflowInfo> wfs = queryMyExperiment(sample, result, measure); for (WorkflowInfo wf : wfs) { Map<String, Value> wfResults = evaluate(sample, result, wf, measureUris, listener); results.putAll(wfResults); processedMeasures.addAll(results.keySet()); } } } } catch (PlanningServiceException e) { // if the query fails for one, most likely myExperiment is not available, so skip all measures LOG.error("Failed to evaluate measures.", e); } return results; } /** * Evaluates the provided digital objects using the service. * * @param sample * the sample object * @param result * the result object * @param service * the service used for evaluation * @param measureUris * measures to evaluate * @param listener * status listener * @return a map of measure URIs and values * @throws EvaluatorException * if an error occurred during evaluation */ private HashMap<String, Value> evaluate(DigitalObject sample, DigitalObject result, WorkflowInfo service, List<String> measureUris, IStatusListener listener) throws EvaluatorException { SSHTavernaExecutor tavernaExecutor = new SSHTavernaExecutor(); tavernaExecutor.init(); HashMap<String, Value> results = new HashMap<String, Value>(); // Get description WorkflowDescription workflowDescription = MyExperimentRESTClient.getWorkflow(service.getDescriptor()); if (workflowDescription == null) { throw new EvaluatorException(String.format("The workflow [%s] could not be found.", service.getDescriptor())); } workflowDescription.readMetadata(); if (!workflowDescription.getProfile().equals("http://purl.org/DP/components#Characterisation") && !workflowDescription.getProfile().equals("http://purl.org/DP/components#QAObjectComparison")) { LOG.warn("The workflow {} is no CC or QA component.", service.getDescriptor()); throw new EvaluatorException("The workflow " + service.getDescriptor() + " is no CC or QA component."); } // Input if (workflowDescription.getProfile().equals("http://purl.org/DP/components#Characterisation")) { setCCInputData(workflowDescription, result, tavernaExecutor); } else { setQAInputData(workflowDescription, sample, result, tavernaExecutor); } // Workflow tavernaExecutor.setWorkflow(service.getContentUri()); // Output ports to receive List<Port> outputPorts = workflowDescription.getOutputPorts(); Set<String> outputPortNames = new HashSet<String>(outputPorts.size()); for (Port p : outputPorts) { outputPortNames.add(p.getName()); } tavernaExecutor.setOutputPorts(outputPortNames); // Execute try { tavernaExecutor.execute(); } catch (IOException e) { LOG.error("Error connecting to execution server", e); throw new EvaluatorException("Error connecting to execution server", e); } catch (TavernaExecutorException e) { LOG.error("Error executing taverna workflow", e); throw new EvaluatorException("Error executing taverna workflow", e); } Map<String, ?> outputData = tavernaExecutor.getOutputData(); for (Port p : outputPorts) { String measure = p.getValue(); // Ignore non-measures, measures for sample object if (measure != null && !measure.isEmpty() && measureUris.contains(measure)) { Object value = outputData.get(p.getName()); Measure m = cm.getMeasure(measure); Value v = m.getScale().createValue(); v.setComment("Evaluated by " + service.getDescriptor()); v.parse(value.toString()); results.put(measure, v); } } return results; } /** * Sets the input data of the taverna executor according for CC components. * * @param workflowDescription * the workflow description * @param digitalObject * the digital object * @param tavernaExecutor * taverna executor * @throws EvaluatorException * if the input could not be set */ private void setCCInputData(WorkflowDescription workflowDescription, DigitalObject digitalObject, SSHTavernaExecutor tavernaExecutor) throws EvaluatorException { HashMap<String, Object> inputData = new HashMap<String, Object>(); List<Port> inputPorts = workflowDescription.getInputPorts(); for (Port p : inputPorts) { if (ComponentConstants.VALUE_SOURCE_OBJECT.equals(p.getValue())) { inputData.put(p.getName(), tavernaExecutor.new ByteArraySourceFile(FileUtils.makeFilename(digitalObject.getFullname()), digitalObject.getData().getData())); } else { LOG.warn("The workflow has an unsupported port {} of type {}", p.getName(), p.getValue()); throw new EvaluatorException("The workflow has an unsupported port " + p.getName() + " that accepts " + p.getValue()); } } tavernaExecutor.setInputData(inputData); } /** * Sets the input data of the taverna executor according for CC components. * * @param workflowDescription * the workflow description * @param digitalObject1 * the first digital object * @param digitalObject2 * the second digital object * @param tavernaExecutor * taverna executor * @throws EvaluatorException * if the input could not be set */ private void setQAInputData(WorkflowDescription workflowDescription, DigitalObject digitalObject1, DigitalObject digitalObject2, SSHTavernaExecutor tavernaExecutor) throws EvaluatorException { HashMap<String, Object> inputData = new HashMap<String, Object>(); List<Port> inputPorts = workflowDescription.getInputPorts(); for (Port p : inputPorts) { if (ComponentConstants.VALUE_LEFT_OBJECT.equals(p.getValue())) { inputData.put(p.getName(), tavernaExecutor.new ByteArraySourceFile(FileUtils.makeFilename(digitalObject1.getFullname()), digitalObject1.getData().getData())); } else { if (ComponentConstants.VALUE_RIGHT_OBJECT.equals(p.getValue())) { inputData.put(p.getName(), tavernaExecutor.new ByteArraySourceFile(FileUtils.makeFilename(digitalObject2.getFullname()), digitalObject2.getData().getData())); } else { LOG.warn("The workflow has an unsupported port {} of type {}", p.getName(), p.getValue()); throw new EvaluatorException("The workflow has an unsupported port " + p.getName() + " that accepts " + p.getValue()); } } } tavernaExecutor.setInputData(inputData); } /** * Queries myExperiment for workflows that provide the required measure for * the digital objects. * * @param sample * the sample object * @param result * the result object * @param measure * the required measure * @return a list of workflows * @throws PlanningServiceException */ private List<WorkflowInfo> queryMyExperiment(DigitalObject sample, DigitalObject result, String measure) throws PlanningServiceException { ComponentQuery q = myExperiment.createComponentQuery(); String sampleMimetype = sample.getFormatInfo().getMimeType(); String resultMimetype = result.getFormatInfo().getMimeType(); q.addHandlesMimetype(sampleMimetype, resultMimetype).addHandlesMimetypeWildcard(sampleMimetype, resultMimetype) .addHandlesMimetypes(sampleMimetype, resultMimetype) .addHandlesMimetypesWildcard(sampleMimetype, resultMimetype) .addInputPort(ComponentConstants.VALUE_LEFT_OBJECT).addInputPort(ComponentConstants.VALUE_RIGHT_OBJECT) .addMeasureOutputPort(measure).finishQuery(); return myExperiment.searchComponents(q); } }