/******************************************************************************* * Copyright 2006 - 2014 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.myexperiment; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.Future; import javax.ejb.AsyncResult; import javax.ejb.Asynchronous; import javax.ejb.Stateful; import javax.inject.Inject; import org.slf4j.Logger; import eu.scape_project.planning.exception.PlanningException; import eu.scape_project.planning.model.Alternative; import eu.scape_project.planning.model.ByteStream; import eu.scape_project.planning.model.DigitalObject; import eu.scape_project.planning.model.Parameter; import eu.scape_project.planning.model.PreservationActionDefinition; import eu.scape_project.planning.model.User; import eu.scape_project.planning.services.IServiceInfo; import eu.scape_project.planning.services.PlanningServiceException; 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.taverna.generator.T2FlowExecutablePlanGenerator; import eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGenerator.InputSource; import eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGenerator.RelatedObject; import eu.scape_project.planning.utils.FileUtils; /** * Async builder for executable plans based on components from myExperiment. */ @Stateful public class MyExperimentAsyncBuilder { @Inject private Logger log; @Inject private User user; private MyExperimentSearch myExperimentSearch; /** * Constructs a new async builder. */ public MyExperimentAsyncBuilder() { myExperimentSearch = new MyExperimentSearch(); } /** * Generates an executable plan. * * @param planName * the plan name * @param alternative * the alternative to base the workflow on * @param measures * the measures to include in the workflow * @param sourceMimetype * the source mimetype of the workflow * @param targetMimetype * the target mimetype of the workflow * @return an executable plan * @throws PlanningException * if an error occurred during generation */ @Asynchronous public Future<DigitalObject> generateExecutablePlan(final String planName, final Alternative alternative, final List<String> measures, final String sourceMimetype, final String targetMimetype) throws PlanningException { DigitalObject workflow = new DigitalObject(); String name = planName + " - " + alternative.getName(); T2FlowExecutablePlanGenerator generator = new T2FlowExecutablePlanGenerator(name, user.getFullName()); // Add ports generator.addSourcePort(); generator.addTargetPort(); // Migration action PreservationActionDefinition action = alternative.getAction(); if (action != null) { try { WorkflowDescription wf = MyExperimentRESTClient.getWorkflow(action.getDescriptor()); if (wf != null) { HashMap<String, String> parameters = new HashMap<String, String>(); for (Parameter p : action.getParams()) { parameters.put(p.getName(), p.getValue()); } wf.readMetadata(); String workflowContent = MyExperimentRESTClient.getWorkflowContent(wf); generator.setMigrationComponent(wf, workflowContent, parameters); } else { log.warn(String.format("The workflow [%s] could not be found.", action.getDescriptor())); } } catch (Exception e) { throw new PlanningException("An error occured querying myExperiment migration component", e); } } // Add QA components addQaComponents(generator, measures, sourceMimetype, targetMimetype); // Create digital object ByteArrayOutputStream out = new ByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(out); try { generator.generate(writer); } catch (IOException e) { log.warn("An error occured generating the executable plan.", e.getMessage()); throw new PlanningException("An error occured generating the executable plan.", e); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { log.warn("An error occured closing the executable plan generator writer.", e.getMessage()); throw new PlanningException("An error occured closing the executable plan generator writer.", e); } } } byte[] data = out.toByteArray(); ByteStream bsData = new ByteStream(); bsData.setData(data); workflow.setContentType("application/vnd.taverna.t2flow+xml"); workflow.setData(bsData); workflow.setFullname(FileUtils.makeFilename(name + ".t2flow")); return new AsyncResult<DigitalObject>(workflow); } /** * Adds QA components to the generator for the provided measures. * * @param generator * the generate used to add components * @param measures * measures to look for components * @param sourceMimetype * source mimetype for the generator * @param targetMimetype * target mimetype for the generator */ private void addQaComponents(final T2FlowExecutablePlanGenerator generator, final List<String> measures, final String sourceMimetype, final String targetMimetype) { List<RecommendedComponent> recommendedComponents = recommendComponents(measures, sourceMimetype, targetMimetype); for (RecommendedComponent recommendedComponent : recommendedComponents) { String workflowContent = MyExperimentRESTClient.getWorkflowContent(recommendedComponent.workflow); if (ComponentConstants.PROFILE_OBJECT_QA.equals(recommendedComponent.workflow.getProfile())) { generator.addQaComponent(recommendedComponent.workflow, workflowContent, recommendedComponent.leftSource, recommendedComponent.rightSource, new HashMap<String, String>(0), recommendedComponent.measures, recommendedComponent.relatedObject); } else if (ComponentConstants.PROFILE_CC.equals(recommendedComponent.workflow.getProfile())) { generator.addCcComponent(recommendedComponent.workflow, workflowContent, new HashMap<String, String>(0), recommendedComponent.measures); } else { log.debug("Component search returned component with invalid or no profile."); } } } /** * Finds QA components for the provided measures. * * @param measures * measures to find components for * @param sourceMimetype * source mimetype of the workflow * @param targetMimetype * target mimetype of the workflow * @return a list of workflow description */ public List<RecommendedComponent> recommendComponents(final List<String> measures, final String sourceMimetype, final String targetMimetype) { List<RecommendedComponent> recommendedComponents = new ArrayList<RecommendedComponent>(); Set<String> processedMeasures = new HashSet<String>(); try { for (String measure : measures) { if (!processedMeasures.contains(measure)) { List<IServiceInfo> qaWfs = queryQaComponents(measure, sourceMimetype, targetMimetype); Iterator<IServiceInfo> qaIt = qaWfs.iterator(); if (qaIt.hasNext()) { IServiceInfo wfi = qaIt.next(); WorkflowDescription wfd = MyExperimentRESTClient.getWorkflow(wfi.getDescriptor()); if (wfd != null) { wfd.readMetadata(); List<Port> outputPorts = wfd.getOutputPorts(); List<String> leftMeasures = new ArrayList<String>(); List<String> rightMeasures = new ArrayList<String>(); for (Port port : outputPorts) { if (measures.contains(port.getValue())) { if (port.getRelatedObject() == null) { leftMeasures.add(port.getValue()); rightMeasures.add(port.getValue()); } else if (ComponentConstants.VALUE_LEFT_OBJECT.equals(port.getRelatedObject())) { leftMeasures.add(port.getValue()); } else if (ComponentConstants.VALUE_RIGHT_OBJECT.equals(port.getRelatedObject())) { rightMeasures.add(port.getValue()); } } } boolean acceptsLeftMimetype = wfd.acceptsLeftMimetype(targetMimetype); boolean acceptsRightMimetype = wfd.acceptsRightMimetype(targetMimetype); if (acceptsLeftMimetype && acceptsRightMimetype) { if (leftMeasures.size() > rightMeasures.size()) { processedMeasures.addAll(leftMeasures); recommendedComponents.add(new RecommendedComponent(wfd, leftMeasures, InputSource.TARGET_OBJECT, InputSource.SOURCE_OBJECT, RelatedObject.LEFT_OBJECT)); } else { processedMeasures.addAll(rightMeasures); recommendedComponents.add(new RecommendedComponent(wfd, rightMeasures, InputSource.SOURCE_OBJECT, InputSource.TARGET_OBJECT, RelatedObject.RIGHT_OBJECT)); } } else if (acceptsLeftMimetype) { processedMeasures.addAll(leftMeasures); recommendedComponents.add(new RecommendedComponent(wfd, leftMeasures, InputSource.TARGET_OBJECT, InputSource.SOURCE_OBJECT, RelatedObject.LEFT_OBJECT)); } else if (acceptsRightMimetype) { processedMeasures.addAll(rightMeasures); recommendedComponents.add(new RecommendedComponent(wfd, rightMeasures, InputSource.SOURCE_OBJECT, InputSource.TARGET_OBJECT, RelatedObject.RIGHT_OBJECT)); } } } else { recommendCcComponents(recommendedComponents, processedMeasures, measures, measure, targetMimetype); } } } } catch (PlanningServiceException e) { // If querying myExperiment fails, it is probably not available, so skip all measures. e.printStackTrace(); } return recommendedComponents; } /** * Recommends CC components and updates {@code recommendetComponents} and * {@code processMeasures}. * * @param recommendedComponents * the recommended measures * @param processedMeasures * the process measures * @param measures * measures to consider * @param measure * measure to find * @param targetMimetype * the target mimetype * @throws PlanningServiceException */ private void recommendCcComponents(List<RecommendedComponent> recommendedComponents, Set<String> processedMeasures, final List<String> measures, final String measure, final String targetMimetype) throws PlanningServiceException { List<IServiceInfo> ccWfs = queryCcComponents(measure, targetMimetype); Iterator<IServiceInfo> ccIt = ccWfs.iterator(); if (ccIt.hasNext()) { IServiceInfo wfi = ccIt.next(); WorkflowDescription wfd = MyExperimentRESTClient.getWorkflow(wfi.getDescriptor()); if (wfd != null) { wfd.readMetadata(); List<Port> outputPorts = wfd.getOutputPorts(); List<String> ccMeasures = new ArrayList<String>(); for (Port port : outputPorts) { if (measures.contains(port.getValue())) { ccMeasures.add(port.getValue()); } } processedMeasures.addAll(ccMeasures); recommendedComponents.add(new RecommendedComponent(wfd, ccMeasures, null, null, null)); } } } /** * Queries myExperiment for workflows that provide the required measure for * the digital objects. * * @param measure * the required measure * @param sourceMimetype * the source mimetype * @param targetMimetype * the target mimetype * @return a list of workflows * @throws PlanningServiceException */ private List<IServiceInfo> queryQaComponents(String measure, String sourceMimetype, String targetMimetype) throws PlanningServiceException { myExperimentSearch.setMeasure(measure); myExperimentSearch.setSourceMimetype(sourceMimetype); myExperimentSearch.setTargetMimetype(targetMimetype); return myExperimentSearch.searchObjectQa(); } /** * Queries myExperiment for workflows that provide the required measure for * the digital objects. * * @param measure * the required measure * @param targetMimetype * the target mimetype * @return a list of workflows * @throws PlanningServiceException */ private List<IServiceInfo> queryCcComponents(String measure, String targetMimetype) throws PlanningServiceException { myExperimentSearch.setMeasure(measure); myExperimentSearch.setTargetMimetype(targetMimetype); return myExperimentSearch.searchCc(); } }