/** * Copyright 2015 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.github.jmchilton.blend4j.galaxy.GalaxyResponseException; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import nl.vumc.biomedbridges.core.Constants; import nl.vumc.biomedbridges.core.DefaultWorkflowEngineFactory; import nl.vumc.biomedbridges.core.DefaultWorkflowFactory; import nl.vumc.biomedbridges.core.WorkflowEngineFactory; import nl.vumc.biomedbridges.core.WorkflowFactory; import org.apache.log4j.Appender; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.varia.LevelRangeFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class checks all of (working) examples on a number of servers and reports the results. * * You can run all examples using the following Maven command: * mvn compile exec:java -Dexec.mainClass="nl.vumc.biomedbridges.examples.AllExamplesCheck" * * @author <a href="mailto:f.debruijn@vumc.nl">Freek de Bruijn</a> */ public class AllExamplesCheck { /** * The list of Galaxy servers to check during Continuous Integration (CI). */ protected static final List<String> CI_GALAXY_SERVER_URLS = ImmutableList.of( Constants.CENTRAL_GALAXY_URL, Constants.THE_HYVE_GALAXY_URL ); /** * The list of all example classes to check. */ protected static final List<Class<? extends AbstractBaseExample>> ALL_EXAMPLE_CLASSES = AbstractBaseExample.getSingletonList(ConcatenateExample.class); // = ImmutableList.of( // ConcatenateExample.class // GrepExample.class // HistogramExample.class, // LineCountExample.class, //// RandomLinesExample.class, //// RemoveTopAndLeftExample.class, // RnaSeqDgeExample.class // ); /** * These example classes will be skipped on all Vancis servers, because the required tools are not available. * * todo: it would be better to make this field private, but it needs to be declared before SKIP_EXAMPLES... */ protected static final List<Class<? extends AbstractBaseExample>> SKIP_EXAMPLES_VANCIS = ImmutableList.of( ConcatenateExample.class, GrepExample.class, HistogramExample.class, RandomLinesExample.class, RemoveTopAndLeftExample.class ); /** * This map contains server URLs and a list of example classes to skip for a specific server. */ protected static final ImmutableMap<String, List<Class<? extends AbstractBaseExample>>> SKIP_EXAMPLES = ImmutableMap.of( Constants.VANCIS_PRO_GALAXY_URL, SKIP_EXAMPLES_VANCIS, Constants.VANCIS_ACC_GALAXY_URL, SKIP_EXAMPLES_VANCIS, // todo: the histogram tool does not work as expected on the central Galaxy server. Constants.CENTRAL_GALAXY_URL, Arrays.asList(HistogramExample.class, RnaSeqDgeExample.class), // The histogram tool is not available on the Galaxy server at The Hyve. Why is RNA-Seq failing? Constants.THE_HYVE_GALAXY_URL, Arrays.asList(HistogramExample.class, RemoveTopAndLeftExample.class /*RnaSeqDgeExample.class*/), Constants.LOCAL_HOST_GALAXY_INSTANCE_URL, Arrays.asList(HistogramExample.class, AbstractBaseExample.class) ); /** * The logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(AllExamplesCheck.class); /** * The list of all Galaxy servers to check. */ private static final List<String> ALL_GALAXY_SERVER_URLS = Arrays.asList( Constants.CENTRAL_GALAXY_URL, // Constants.VANCIS_PRO_GALAXY_URL // Constants.VANCIS_ACC_GALAXY_URL, Constants.THE_HYVE_GALAXY_URL // Constants.LOCAL_HOST_GALAXY_INSTANCE_URL ); /** * The maximum number of times to wait for the upload to finish. */ private int uploadMaxWaitCount; /** * The maximum number of times to wait for the workflow to finish. */ private int runWorkflowMaxWaitCount; /** * Hidden constructor (protected for testing). Only the main and checkExamples methods of this class are meant to * be used. */ protected AllExamplesCheck() { } /** * Main method. * * @param arguments unused command-line arguments. */ // CHECKSTYLE_OFF: UncommentedMain public static void main(final String[] arguments) { new AllExamplesCheck().checkExamples(ALL_GALAXY_SERVER_URLS, ALL_EXAMPLE_CLASSES, SKIP_EXAMPLES); } // CHECKSTYLE_ON: UncommentedMain /** * Run all examples on all Galaxy servers. * * @param galaxyServerUrls the list with Galaxy server URLs to check. * @param exampleClasses the list with examples to run. * @param skipExamples the map with examples to skip for specific servers. * @return a short report of the results: [server name]": "[success rate]" ["[failures]"]" separated by "; " for * each server. */ protected String checkExamples(final List<String> galaxyServerUrls, final List<Class<? extends AbstractBaseExample>> exampleClasses, final ImmutableMap<String, List<Class<? extends AbstractBaseExample>>> skipExamples) { return checkExamples(galaxyServerUrls, exampleClasses, skipExamples, -1, -1); } /** * Run all examples on all Galaxy servers. * * @param galaxyServerUrls the list with Galaxy server URLs to check. * @param exampleClasses the list with examples to run. * @param skipExamples the map with examples to skip for specific servers. * @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 a short report of the results: [server name]": "[success rate]" ["[failures]"]" separated by "; " for * each server. */ protected String checkExamples(final List<String> galaxyServerUrls, final List<Class<? extends AbstractBaseExample>> exampleClasses, final ImmutableMap<String, List<Class<? extends AbstractBaseExample>>> skipExamples, final int uploadMaxWaitCount, final int runWorkflowMaxWaitCount) { final long startTime = System.currentTimeMillis(); final StringBuilder report = new StringBuilder(); final Appender consoleAppender = LogManager.getRootLogger().getAppender("console-appender"); if (consoleAppender != null) consoleAppender.addFilter(createAppenderFilter()); this.uploadMaxWaitCount = uploadMaxWaitCount; this.runWorkflowMaxWaitCount = runWorkflowMaxWaitCount; final List<String> summary = new ArrayList<>(); for (final String serverUrl : galaxyServerUrls) { final boolean serverOnline = isGalaxyServerOnline(serverUrl); logger.warn("Galaxy server " + serverUrl + " is {}.", serverOnline ? "online" : "not available"); final String serverReport; if (serverOnline) serverReport = runExamplesOnServer(serverUrl, exampleClasses, skipExamples); else serverReport = createServerReport(serverUrl, new ArrayList<String>(), new ArrayList<String>(), "0/" + ALL_EXAMPLE_CLASSES.size()); summary.add(serverReport); report.append(report.length() == 0 ? "" : " | "); report.append(serverReport); logger.warn(""); logger.warn(""); } printSummary(summary); logger.warn(""); final long durationSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startTime); logger.warn("Checking all examples took {} seconds.", durationSeconds); return report.toString(); } /** * Create a filter to limit logging messages. * * @return the appender filter. */ protected LevelRangeFilter createAppenderFilter() { final LevelRangeFilter logAppenderFilter = new LevelRangeFilter(); logAppenderFilter.setAcceptOnMatch(true); logAppenderFilter.setLevelMin(Level.WARN); logAppenderFilter.setLevelMax(Level.FATAL); return logAppenderFilter; } /** * Determine whether a Galaxy server appears to be online. * * @param serverUrl the URL of the Galaxy server. * @return whether it appears to be online. */ private boolean isGalaxyServerOnline(final String serverUrl) { boolean serverOnline; final int minimumExpectedContentLength = 64; try { final StringBuilder content = new StringBuilder(); final InputStream inputStream = new URL(serverUrl).openStream(); int nextByte; while ((nextByte = inputStream.read()) != -1) content.append((char) nextByte); logger.debug("AllExamplesCheck.isGalaxyServerOnline - content length: {}", content.length()); serverOnline = content.length() > minimumExpectedContentLength; } catch (final IOException e) { serverOnline = false; } return serverOnline; } /** * Run all examples on a specific Galaxy server. * * @param serverUrl the URL of the Galaxy server. * @param exampleClasses the example classes to run. * @param skipExamples the examples to skip for specific servers. * @return the server report: [server URL]: [successful example count] '/' [attempt count] " [" failures "]". */ private String runExamplesOnServer(final String serverUrl, final List<Class<? extends AbstractBaseExample>> exampleClasses, final ImmutableMap<String, List<Class<? extends AbstractBaseExample>>> skipExamples) { logger.warn("Running examples:"); int skipCount = 0; final List<String> successes = new ArrayList<>(); final List<String> failures = new ArrayList<>(); for (final Class<? extends AbstractBaseExample> exampleClass : exampleClasses) { if (skipExamples == null || !skipExamples.containsKey(serverUrl) || !skipExamples.get(serverUrl).contains(exampleClass)) { if (runExampleOnServer(serverUrl, exampleClass)) successes.add(exampleClass.getSimpleName()); else failures.add(exampleClass.getSimpleName()); } else { logger.warn("- The example {} is skipped for server {}.", exampleClass.getSimpleName(), serverUrl); skipCount++; } } logger.warn(""); final String successRate = String.format("%d/%d", successes.size(), exampleClasses.size() - skipCount); logger.warn("Success rate: {}.", successRate); logger.warn(""); logger.warn(""); return createServerReport(serverUrl, successes, failures, successRate); } /** * Run an example on a specific Galaxy server. * * @param serverUrl the URL of the Galaxy server. * @param exampleClass the example class to run. * @return whether the example ran successfully. */ private boolean runExampleOnServer(final String serverUrl, final Class<? extends AbstractBaseExample> exampleClass) { boolean result = false; try { logger.warn(""); logger.warn("- The example {} is running...", exampleClass.getSimpleName()); final AbstractBaseExample example = createExample(exampleClass); result = example.runExample(serverUrl, uploadMaxWaitCount, runWorkflowMaxWaitCount); } catch (final ReflectiveOperationException | GalaxyResponseException e) { logger.error("Galaxy response exception while running an example.", e); } logger.warn("- The example {} ran " + (result ? "" : "un") + "successfully.", exampleClass.getSimpleName()); return result; } /** * Create an example instance from an example class. * * @param exampleClass the example class. * @return the example instance. * @throws ReflectiveOperationException if creating the example instance throws an reflection exception. */ private AbstractBaseExample createExample(final Class<? extends AbstractBaseExample> exampleClass) throws ReflectiveOperationException { AbstractBaseExample example; final DefaultWorkflowEngineFactory workflowEngineFactory = new DefaultWorkflowEngineFactory(); final DefaultWorkflowFactory workflowFactory = new DefaultWorkflowFactory(); try { example = exampleClass .getConstructor(WorkflowEngineFactory.class, WorkflowFactory.class) .newInstance(workflowEngineFactory, workflowFactory); } catch (final NoSuchMethodException e) { example = exampleClass.getConstructor(WorkflowFactory.class).newInstance(workflowFactory); } example.setHttpLogging(true); return example; } /** * Create the report of the examples that have run on a specific server. * * @param serverUrl the URL of the server. * @param successes the list of examples that ran successfully (class names). * @param failures the list of examples that failed (class names). * @param successRate the success rate (success count versus not skipped example count). * @return the server report. */ private String createServerReport(final String serverUrl, final List<String> successes, final List<String> failures, final String successRate) { final String exampleSeparator = ", "; final boolean successesAndFailures = successes.size() > 0 && failures.size() > 0; return serverUrl + ": " + successRate + " [" + (successes.size() > 0 ? ("successes: " + Joiner.on(exampleSeparator).join(successes)) : "") + (successesAndFailures ? "; " : "") + (failures.size() > 0 ? ("failures: " + Joiner.on(exampleSeparator).join(failures)) : "") + "]"; } /** * Print the summary of the checks. * * @param summary the summary with one line for each Galaxy server. */ private void printSummary(final List<String> summary) { logger.warn(""); logger.warn("Summary:"); for (final String line : summary) logger.warn("- " + line); } }