/** * Copyright 2014 VU University Medical Center. * Licensed under the Apache License version 2.0 (see http://www.apache.org/licenses/LICENSE-2.0.html). */ package nl.vumc.biomedbridges.examples; import com.google.common.base.Charsets; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; import java.io.File; import java.io.IOException; import java.util.List; import nl.vumc.biomedbridges.core.Constants; import nl.vumc.biomedbridges.core.Workflow; import nl.vumc.biomedbridges.core.WorkflowEngineFactory; import nl.vumc.biomedbridges.core.WorkflowFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class contains shared functionality for the workflow running examples. * * @author <a href="mailto:f.debruijn@vumc.nl">Freek de Bruijn</a> */ public abstract class AbstractBaseExample { /** * The logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(AbstractBaseExample.class); /** * The workflow engine factory to use. */ protected final WorkflowEngineFactory workflowEngineFactory; /** * The workflow factory to use. */ protected final WorkflowFactory workflowFactory; /** * Whether the HTTP messages should be logged or not. */ protected boolean httpLogging = true; /** * The start time of this example (in milliseconds). */ private long startTime; /** * The runtime duration of this workflow (in seconds). */ private double durationSeconds; /** * Construct a base example object. * * @param workflowFactory the workflow factory to use. */ public AbstractBaseExample(final WorkflowFactory workflowFactory) { this.workflowEngineFactory = null; this.workflowFactory = workflowFactory; } /** * Construct a base example object. * * @param workflowEngineFactory the workflow engine factory to use. * @param workflowFactory the workflow factory to use. */ public AbstractBaseExample(final WorkflowEngineFactory workflowEngineFactory, final WorkflowFactory workflowFactory) { this.workflowEngineFactory = workflowEngineFactory; this.workflowFactory = workflowFactory; } /** * Construct a base example object. * * @param workflowEngineFactory the workflow engine factory to use. * @param workflowFactory the workflow factory to use. * @param logger the logger to use for initialization. */ public AbstractBaseExample(final WorkflowEngineFactory workflowEngineFactory, final WorkflowFactory workflowFactory, final Logger logger) { this.workflowEngineFactory = workflowEngineFactory; this.workflowFactory = workflowFactory; if (logger != null) initializeExample(logger, null); } /** * Construct a base example object. * * @param workflowEngineFactory the workflow engine factory to use. */ // todo: remove this old constructor later. //@Deprecated public AbstractBaseExample(final WorkflowEngineFactory workflowEngineFactory) { this.workflowEngineFactory = workflowEngineFactory; this.workflowFactory = null; } /** * Select whether the HTTP messages should be logged or not. * * @param httpLogging whether the HTTP messages should be logged or not. */ public void setHttpLogging(final boolean httpLogging) { this.httpLogging = httpLogging; } /** * Initialize running an example by configuring the logging and storing the start time. * * @param logger the logger to use. * @param name the name of the example. */ public void initializeExample(final Logger logger, final String name) { logger.info("========================================"); logger.info(name != null ? name : getClass().getSimpleName() + " has started."); startTime = System.currentTimeMillis(); } /** * Run this example workflow and return the result. Can be overridden by sub classes. * * @param galaxyInstanceUrl the URL of the Galaxy instance to use. * @return whether the workflow ran successfully. */ public boolean runExample(final String galaxyInstanceUrl) { return runExample(galaxyInstanceUrl, -1, -1); } /** * Run this example workflow and return the result. Should be implemented by sub classes. * * @param galaxyInstanceUrl the URL of the Galaxy instance to use. * @param uploadMaxWaitCount the maximum number of times to wait for the upload to finish. * @param runWorkflowMaxWaitCount the maximum number of times to wait for the workflow to finish. * @return whether the workflow ran successfully. */ public boolean runExample(final String galaxyInstanceUrl, final int uploadMaxWaitCount, final int runWorkflowMaxWaitCount) { return false; } /** * Check the single output after running the workflow. * * @param workflow the workflow that has been executed. * @param outputName the name of the single output to check. * @param expectedLines the lines that are expected in the output file. * @throws IOException if reading the output file fails. */ public static void checkWorkflowSingleOutput(final Workflow workflow, final String outputName, final List<String> expectedLines) throws IOException { final boolean finalResult = checkWorkflowSingleOutput(workflow, workflow.getResult(), outputName, expectedLines); workflow.setResult(finalResult); } /** * Check the single output after running the workflow. * * @param workflow the workflow that has been executed. * @param runResult the result from running the workflow. * @param outputName the name of the single output to check. * @param expectedLines the lines that are expected in the output file. * @return whether the workflow output is correct (and running the workflow returned true). * @throws IOException if reading the output file fails. */ public static boolean checkWorkflowSingleOutput(final Workflow workflow, final boolean runResult, final String outputName, final List<String> expectedLines) throws IOException { boolean result = false; if (!runResult) logger.error("Error while running workflow {}.", workflow.getName()); final Object output = workflow.getOutput(outputName); if (output instanceof File) { final File outputFile = (File) output; final List<String> actualLines = Files.readLines(outputFile, Charsets.UTF_8); final String lineSeparator = " | "; final String partialMessage = "the line" + (expectedLines.size() > 1 ? "s" : "") + " we expected!"; if (expectedLines.equals(actualLines)) { result = true; logger.info("- The output file contains " + partialMessage + "!!"); logger.info(" actual: " + Joiner.on(lineSeparator).join(actualLines)); } else { logger.error("- The output file does not contain " + partialMessage); logger.error(" expected: " + Joiner.on(lineSeparator).join(expectedLines)); logger.error(" actual: " + Joiner.on(lineSeparator).join(actualLines)); } final boolean deleteResult = outputFile.delete(); result &= deleteResult; if (!deleteResult) logger.error("Deleting output file {} failed (after checking contents).", outputFile.getAbsolutePath()); } else logger.error("There is no output file named {}.", outputName); return runResult && result; } /** * Finish running an example by logging the duration. * * @param logger the logger to use. */ public void finishExample(final Logger logger) { durationSeconds = (System.currentTimeMillis() - startTime) / (float) Constants.MILLISECONDS_PER_SECOND; logger.info(""); logger.info(String.format("Running the workflow took %1.2f seconds.", durationSeconds)); } /** * Finish running an example by logging the duration. This method returns the workflow result as well. * * @param workflow the workflow that ran. * @return the result from running the workflow. */ public boolean finishExample(final Workflow workflow) { finishExample(logger); return workflow.getResult(); } /** * Get the runtime duration of this workflow (in seconds). * * @return the runtime duration of this workflow (in seconds). */ public double getDurationSeconds() { return durationSeconds; } /** * Return a list with a single example class. (For some reason both Collections.singletonList and ImmutableList.of * with a single item seem to return not exactly the right class.) * * @param exampleClass the example class to put in the list. * @return the list with the example class. */ public static List<Class<? extends AbstractBaseExample>> getSingletonList( final Class<? extends AbstractBaseExample> exampleClass) { return ImmutableList.of(exampleClass, ConcatenateExample.class).subList(0, 1); } }