org.perfcake.ScenarioExecution.java Source code

Java tutorial

Introduction

Here is the source code for org.perfcake.ScenarioExecution.java

Source

/*
 * -----------------------------------------------------------------------\
 * PerfCake
 * 
 * Copyright (C) 2010 - 2016 the original author or authors.
 * 
 * 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 org.perfcake;

import org.perfcake.scenario.Scenario;
import org.perfcake.scenario.ScenarioLoader;
import org.perfcake.util.TimerBenchmark;
import org.perfcake.util.Utils;
import org.perfcake.validation.ValidationException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * Parses command line parameters, loads the scenario from XML or DSL file and executes it.
 *
 * @author <a href="mailto:pavel.macik@gmail.com">Pavel Mack</a>
 * @author <a href="mailto:marvenec@gmail.com">Martin Ve?ea</a>
 */
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "DM_EXIT", justification = "This class is allowed to terminate the JVM.")
public class ScenarioExecution {

    private static final Logger log = LogManager.getLogger(ScenarioExecution.class);

    /**
     * Command line parameters.
     */
    private CommandLine commandLine;

    /**
     * The scenario created from the specified XML file.
     */
    private Scenario scenario;

    /**
     * Skips timer benchmark when set to true.
     */
    private boolean skipTimerBenchmark = false;

    /**
     * Parses command line arguments and creates this class to take care of the Scenario execution.
     *
     * @param args
     *       command line arguments.
     */
    private ScenarioExecution(final String[] args) {
        Utils.initTimeStamps();
        parseCommandLine(args);
        loadScenario();
    }

    /**
     * Creates an instance of {@link org.perfcake.ScenarioExecution} and executes the scenario.
     *
     * @param args
     *       Command line arguments.
     */
    public static void main(final String[] args) {
        final ScenarioExecution se = new ScenarioExecution(args);

        log.info(String.format("=== Welcome to PerfCake %s ===", PerfCakeConst.VERSION));

        // Print system properties
        se.printTraceInformation();

        se.executeScenario();

        log.info("=== Goodbye! ===");
    }

    /**
     * Parses a single command line parameter/option.
     *
     * @param option
     *       The parameter/option name.
     * @param property
     *       The system property to which the option value should be stored.
     * @param defaultValue
     *       The default value for the option when it is not present at the command line.
     */
    private void parseParameter(final String option, final String property, final String defaultValue) {
        if (commandLine.hasOption(option)) {
            System.setProperty(property, commandLine.getOptionValue(option));
        } else {
            if (defaultValue != null) {
                System.setProperty(property, defaultValue);
            }
        }
    }

    /**
     * Parses additional user properties specified in a property file and at the command line.
     */
    private void parseUserProperties() {
        final Properties props = new Properties();
        final String propsFile = System.getProperty(PerfCakeConst.PROPERTIES_FILE_PROPERTY);
        if (propsFile != null) {
            try (final FileInputStream propsInputStream = new FileInputStream(propsFile)) {
                props.load(propsInputStream);
            } catch (final IOException e) {
                // we can still continue without reading file
                log.warn(String.format("Unable to read the properties file '%s': ", propsFile), e);
            }
        }

        props.putAll(commandLine.getOptionProperties("D"));

        for (final Entry<Object, Object> entry : props.entrySet()) {
            System.setProperty(entry.getKey().toString(), entry.getValue().toString());
        }

    }

    /**
     * Parses the command line.
     *
     * @param args
     *       Command line arguments.
     */
    @SuppressWarnings("static-access")
    private void parseCommandLine(final String[] args) {
        final HelpFormatter formatter = new HelpFormatter();
        final Options options = new Options();

        options.addOption(Option.builder("s").longOpt(PerfCakeConst.SCENARIO_OPT).desc("scenario to be executed")
                .hasArg().argName("SCENARIO").build());
        options.addOption(Option.builder("sd").longOpt(PerfCakeConst.SCENARIOS_DIR_OPT)
                .desc("directory for scenarios").hasArg().argName("SCENARIOS_DIR").build());
        options.addOption(Option.builder("md").longOpt(PerfCakeConst.MESSAGES_DIR_OPT)
                .desc("directory for messages").hasArg().argName("MESSAGES_DIR").build());
        options.addOption(Option.builder("pd").longOpt(PerfCakeConst.PLUGINS_DIR_OPT).desc("directory for plugins")
                .hasArg().argName("PLUGINS_DIR").build());
        options.addOption(Option.builder("pf").longOpt(PerfCakeConst.PROPERTIES_FILE_OPT)
                .desc("custom system properties file").hasArg().argName("PROPERTIES_FILE").build());
        options.addOption(Option.builder("log").longOpt(PerfCakeConst.LOGGING_LEVEL_OPT).desc("logging level")
                .hasArg().argName("LOG_LEVEL").build());
        options.addOption(Option.builder("skip").longOpt(PerfCakeConst.SKIP_TIMER_BENCHMARK_OPT)
                .desc("skip system timer benchmark").build());
        options.addOption(Option.builder("D").argName("property=value").numberOfArgs(2).valueSeparator()
                .desc("system properties").build());

        final CommandLineParser commandLineParser = new DefaultParser();
        try {
            commandLine = commandLineParser.parse(options, args);
        } catch (final ParseException pe) {
            log.fatal("Cannot parse application parameters: ", pe);
            formatter.printHelp(PerfCakeConst.USAGE_HELP, options);
            System.exit(PerfCakeConst.ERR_PARAMETERS);
            return;
        }

        if (commandLine.hasOption(PerfCakeConst.SCENARIO_OPT)) {
            System.setProperty(PerfCakeConst.SCENARIO_PROPERTY,
                    commandLine.getOptionValue(PerfCakeConst.SCENARIO_OPT));
        } else {
            formatter.printHelp(PerfCakeConst.USAGE_HELP, options);
            System.exit(PerfCakeConst.ERR_NO_SCENARIO);
            return;
        }

        if (commandLine.hasOption(PerfCakeConst.SKIP_TIMER_BENCHMARK_OPT)) {
            skipTimerBenchmark = true;
        }

        parseParameter(PerfCakeConst.SCENARIOS_DIR_OPT, PerfCakeConst.SCENARIOS_DIR_PROPERTY,
                Utils.determineDefaultLocation("scenarios"));
        parseParameter(PerfCakeConst.MESSAGES_DIR_OPT, PerfCakeConst.MESSAGES_DIR_PROPERTY,
                Utils.determineDefaultLocation("messages"));
        parseParameter(PerfCakeConst.PLUGINS_DIR_OPT, PerfCakeConst.PLUGINS_DIR_PROPERTY,
                Utils.DEFAULT_PLUGINS_DIR.getAbsolutePath());
        parseParameter(PerfCakeConst.PROPERTIES_FILE_OPT, PerfCakeConst.PROPERTIES_FILE_PROPERTY, null);
        parseParameter(PerfCakeConst.LOGGING_LEVEL_OPT, PerfCakeConst.LOGGING_LEVEL_PROPERTY, null);
        if (Utils.getProperty(PerfCakeConst.LOGGING_LEVEL_PROPERTY, null) != null) {
            Utils.setLoggingLevel(
                    Level.toLevel(Utils.getProperty(PerfCakeConst.LOGGING_LEVEL_PROPERTY), Level.INFO));
        }

        parseUserProperties();
    }

    /**
     * Prints trace information for test debugging purposes.
     */
    private void printTraceInformation() {
        if (log.isTraceEnabled()) {
            log.trace("System properties:");
            final List<String> p = System.getProperties().entrySet().stream()
                    .map(entry -> "\t" + entry.getKey() + "=" + entry.getValue())
                    .collect(Collectors.toCollection(() -> new LinkedList<>()));

            Collections.sort(p);

            for (final String s : p) {
                log.trace(s);
            }

            // Print classpath
            log.trace("Classpath:");
            final ClassLoader currentCL = ScenarioExecution.class.getClassLoader();
            final URL[] curls = ((URLClassLoader) currentCL).getURLs();

            for (final URL curl : curls) {
                log.trace("\t" + curl);
            }
        }
    }

    /**
     * Loads the scenario from the XML file specified at the command line.
     */
    private void loadScenario() {
        final String scenarioFile = Utils.getProperty(PerfCakeConst.SCENARIO_PROPERTY);

        try {
            scenario = ScenarioLoader.load(scenarioFile);
        } catch (final Exception e) {
            log.fatal(String.format("Cannot load scenario '%s': ", scenarioFile), e);
            System.exit(PerfCakeConst.ERR_SCENARIO_LOADING);
        }
    }

    /**
     * Executes the loaded scenario.
     */
    private void executeScenario() {
        if (!skipTimerBenchmark) {
            TimerBenchmark.measureTimerResolution();
        }

        boolean err = false;

        try {
            scenario.init();
            scenario.run();
        } catch (final PerfCakeException e) {
            log.fatal("Error running scenario: ", e);
            err = true;
        } finally {
            try {
                scenario.close();
            } catch (final ValidationException ve) {
                log.warn(ve.getMessage());
                System.exit(PerfCakeConst.ERR_VALIDATION);
            } catch (final PerfCakeException e) {
                log.fatal("Scenario did not finish properly: ", e);
                err = true;
            }
        }

        if (err) {
            System.exit(PerfCakeConst.ERR_SCENARIO_EXECUTION);
        }

        if (!scenario.areAllThreadsTerminated()) {
            log.warn(
                    "There are some blocked threads that were not possible to terminate. The test results might be flawed."
                            + " This is usually caused by deadlocks or raise conditions in the application under test.");
            System.exit(PerfCakeConst.ERR_BLOCKED_THREADS);
        }
    }
}