com.asakusafw.testdriver.tools.runner.BatchTestRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.asakusafw.testdriver.tools.runner.BatchTestRunner.java

Source

/**
 * Copyright 2011-2017 Asakusa Framework Team.
 *
 * 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 com.asakusafw.testdriver.tools.runner;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.UUID;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.asakusafw.testdriver.TestDriverContext;
import com.asakusafw.vocabulary.batch.Batch;
import com.asakusafw.vocabulary.batch.BatchDescription;
import com.asakusafw.yaess.core.BatchScript;

/**
 * The program entry point of Asakusa batch application runner.
 * @since 0.6.0
 * @version 0.7.1
 * @see BatchTestTool
 */
public final class BatchTestRunner {

    static final Logger LOG = LoggerFactory.getLogger(BatchTestRunner.class);

    /**
     * The YAESS script path (relative from batch application directory).
     */
    public static final String PATH_YAESS_SCRIPT = "etc/yaess-script.properties"; //$NON-NLS-1$

    static final Option OPT_BATCH_ID;
    static final Option OPT_ARGUMENT;
    static final Option OPT_PROPERTY;

    private static final Options OPTIONS;
    static {
        OPT_BATCH_ID = new Option("b", "batch", true, //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("BatchTestRunner.optBatch")); //$NON-NLS-1$
        OPT_BATCH_ID.setArgName("batch_id"); //$NON-NLS-1$
        OPT_BATCH_ID.setRequired(true);

        OPT_ARGUMENT = new Option("A", "argument", true, //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("BatchTestRunner.optArgument")); //$NON-NLS-1$
        OPT_ARGUMENT.setArgs(2);
        OPT_ARGUMENT.setValueSeparator('=');
        OPT_ARGUMENT.setArgName("name=value"); //$NON-NLS-1$
        OPT_ARGUMENT.setRequired(false);

        OPT_PROPERTY = new Option("D", "property", true, //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("BatchTestRunner.optProperty")); //$NON-NLS-1$
        OPT_PROPERTY.setArgs(2);
        OPT_PROPERTY.setValueSeparator('=');
        OPT_PROPERTY.setArgName("name=value"); //$NON-NLS-1$
        OPT_PROPERTY.setRequired(false);

        OPTIONS = new Options();
        OPTIONS.addOption(OPT_BATCH_ID);
        OPTIONS.addOption(OPT_ARGUMENT);
    }

    private final TestDriverContext context;

    private final String batchId;

    private final String executionIdPrefix;

    /**
     * Creates a new instance.
     * @param batchClass the target batch class
     * @since 0.7.1
     */
    public BatchTestRunner(Class<? extends BatchDescription> batchClass) {
        if (batchClass == null) {
            throw new IllegalArgumentException("batchClass must not be null"); //$NON-NLS-1$
        }
        Batch annotation = batchClass.getAnnotation(Batch.class);
        if (annotation == null) {
            throw new IllegalArgumentException(
                    MessageFormat.format(Messages.getString("BatchTestRunner.errorMissingBatchAnnotation"), //$NON-NLS-1$
                            batchClass.getName(), Batch.class.getSimpleName()));
        }

        this.context = new TestDriverContext(batchClass);
        this.batchId = annotation.name();
        this.executionIdPrefix = UUID.randomUUID().toString();
        initialize();
    }

    /**
     * Creates a new instance.
     * @param batchId the target batch ID
     * @since 0.7.1
     */
    public BatchTestRunner(String batchId) {
        if (batchId == null) {
            throw new IllegalArgumentException("batchId must not be null"); //$NON-NLS-1$
        }
        this.context = new TestDriverContext(BatchTestRunner.class);
        this.batchId = batchId;
        this.executionIdPrefix = UUID.randomUUID().toString();
        initialize();
    }

    private void initialize() {
        // NOTE: We must use the system "batchapps" path instead of a temporary location
        context.useSystemBatchApplicationsInstallationPath(true);
    }

    /**
     * Sets the Asakusa Framework installation path.
     * The default value is {@code $ASAKUSA_HOME}.
     * @param path the framework installation path
     * @return this
     * @since 0.7.1
     */
    public BatchTestRunner withFramework(File path) {
        context.setFrameworkHomePath(path);
        return this;
    }

    /**
     * Sets the Asakusa batch applications installation path.
     * The default value is {@code $ASAKUSA_HOME/batchapps}.
     * @param path the batch applications installation path
     * @return this
     * @since 0.7.1
     */
    public BatchTestRunner withApplications(File path) {
        context.setBatchApplicationsInstallationPath(path);
        return this;
    }

    /**
     * Sets a batch argument for this runner.
     * @param name the argument name
     * @param value the argument value
     * @return this
     * @since 0.7.1
     */
    public BatchTestRunner withArgument(String name, String value) {
        context.getBatchArgs().put(name, value);
        return this;
    }

    /**
     * Sets batch arguments for this runner.
     * @param arguments the arguments name value map
     * @return this
     * @since 0.7.1
     */
    public BatchTestRunner withArguments(Map<String, String> arguments) {
        if (arguments != null) {
            context.getBatchArgs().putAll(arguments);
        }
        return this;
    }

    /**
     * Sets a Hadoop property for this runner.
     * @param key the property key
     * @param value the property value
     * @return this
     * @since 0.7.1
     */
    public BatchTestRunner withProperty(String key, String value) {
        context.getExtraConfigurations().put(key, value);
        return this;
    }

    /**
     * Sets Hadoop properties for this runner.
     * @param properties the properties key value map
     * @return this
     * @since 0.7.1
     */
    public BatchTestRunner withProperties(Map<String, String> properties) {
        if (properties != null) {
            context.getExtraConfigurations().putAll(properties);
        }
        return this;
    }

    /**
     * Run Asakusa batch application.
     * @return the exit code
     * @since 0.7.1
     */
    public int execute() {
        long t0 = System.currentTimeMillis();
        try {
            RunTask.Configuration configuration = loadConfiguration();
            RunTask task = new RunTask(configuration);
            task.perform();
        } catch (AssertionError e) {
            LOG.error(MessageFormat.format(Messages.getString("BatchTestRunner.errorFailedToExecute"), //$NON-NLS-1$
                    batchId), e);
            return 1;
        } catch (Exception e) {
            LOG.error(MessageFormat.format(Messages.getString("BatchTestRunner.errorFailedToExecute"), //$NON-NLS-1$
                    batchId), e);
            return 1;
        } finally {
            context.cleanUpTemporaryResources();
        }
        if (LOG.isInfoEnabled()) {
            long t1 = System.currentTimeMillis();
            LOG.info(MessageFormat.format(Messages.getString("BatchTestRunner.infoElapsedTime"), //$NON-NLS-1$
                    t1 - t0));
        }
        return 0;
    }

    /**
     * Program entry.
     * @param args program arguments
     */
    public static void main(String[] args) {
        int exitCode = execute(args);
        if (exitCode != 0) {
            System.exit(exitCode);
        }
    }

    /**
     * Program entry.
     * @param args program arguments
     * @return the exit code
     * @see #execute(String, Map)
     */
    public static int execute(String[] args) {
        BatchTestRunner runner;
        try {
            runner = parseArguments(args);
        } catch (Exception e) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.setWidth(Integer.MAX_VALUE);
            formatter.printHelp(MessageFormat.format("java -classpath ... {0}", //$NON-NLS-1$
                    BatchTestRunner.class.getName()), OPTIONS, true);
            LOG.error(MessageFormat.format(Messages.getString("BatchTestRunner.errorInvalidArgument"), //$NON-NLS-1$
                    Arrays.toString(args)), e);
            return 1;
        }
        return runner.execute();
    }

    /**
     * Run Asakusa batch application.
     * @param batchId the target batch ID
     * @param batchArguments the batch arguments (nullable)
     * @return the exit code
     */
    public static int execute(String batchId, Map<String, String> batchArguments) {
        return new BatchTestRunner(batchId).withArguments(batchArguments).execute();
    }

    /**
     * Run Asakusa batch application without any batch arguments.
     * @param batchId the target batch ID
     * @return the exit code
     * @see #execute(String, Map)
     */
    public static int execute(String batchId) {
        return execute(batchId, null);
    }

    static BatchTestRunner parseArguments(String[] args) throws ParseException {
        assert args != null;
        CommandLineParser parser = new BasicParser();
        CommandLine cmd = parser.parse(OPTIONS, args);

        String batchId = cmd.getOptionValue(OPT_BATCH_ID.getOpt());
        LOG.debug("Batch ID: {}", batchId); //$NON-NLS-1$

        Properties arguments = cmd.getOptionProperties(OPT_ARGUMENT.getOpt());
        LOG.debug("Batch arguments: {}", arguments); //$NON-NLS-1$

        Properties properties = cmd.getOptionProperties(OPT_PROPERTY.getOpt());
        LOG.debug("Extra properties: {}", arguments); //$NON-NLS-1$

        return new BatchTestRunner(batchId).withArguments(toMap(arguments)).withProperties(toMap(properties));
    }

    private RunTask.Configuration loadConfiguration() {
        BatchScript script;
        File scriptFile = getScriptFile(context, batchId);
        LOG.debug("Loading script: {}", scriptFile); //$NON-NLS-1$
        try {
            Properties properties = loadProperties(scriptFile);
            script = BatchScript.load(properties);
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    MessageFormat.format(Messages.getString("BatchTestRunner.errorInvalidYaessScript"), //$NON-NLS-1$
                            scriptFile),
                    e);
        }

        LOG.debug("Analyzed YAESS bootstrap arguments"); //$NON-NLS-1$
        return new RunTask.Configuration(context, script, executionIdPrefix);
    }

    private static File getScriptFile(TestDriverContext context, String batchId) {
        assert context != null;
        assert batchId != null;
        File batchappBase = context.getBatchApplicationsInstallationPath();
        File batchapp = new File(batchappBase, batchId);
        File scriptFile = new File(batchapp, PATH_YAESS_SCRIPT);
        return scriptFile;
    }

    private static Map<String, String> toMap(Properties p) {
        assert p != null;
        Map<String, String> results = new TreeMap<>();
        for (Map.Entry<Object, Object> entry : p.entrySet()) {
            results.put((String) entry.getKey(), (String) entry.getValue());
        }
        return results;
    }

    private static Properties loadProperties(File path) throws IOException {
        assert path != null;
        try (FileInputStream in = new FileInputStream(path)) {
            Properties properties = new Properties();
            properties.load(in);
            return properties;
        }
    }
}