com.asakusafw.compiler.bootstrap.AllBatchCompilerDriver.java Source code

Java tutorial

Introduction

Here is the source code for com.asakusafw.compiler.bootstrap.AllBatchCompilerDriver.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.compiler.bootstrap;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.asakusafw.compiler.batch.ResourceRepository;
import com.asakusafw.compiler.batch.ResourceRepository.Cursor;
import com.asakusafw.compiler.common.FileRepository;
import com.asakusafw.compiler.common.ZipRepository;
import com.asakusafw.compiler.flow.Location;
import com.asakusafw.vocabulary.batch.Batch;
import com.asakusafw.vocabulary.batch.BatchDescription;

/**
 * Program entry for compiling all batch classes under a class library.
 * @see BatchCompilerDriver
 */
public final class AllBatchCompilerDriver {

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

    private static final Option OPT_OUTPUT;
    private static final Option OPT_PACKAGE;
    private static final Option OPT_HADOOPWORK;
    private static final Option OPT_COMPILERWORK;
    private static final Option OPT_LINK;
    private static final Option OPT_PLUGIN;
    private static final Option OPT_SKIPERROR;
    private static final Option OPT_SCANPATH;
    private static final Option OPT_INCLUDE;
    private static final Option OPT_EXCLUDE;

    private static final Options OPTIONS;
    static {
        OPT_OUTPUT = new Option("output", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optOutput")); //$NON-NLS-1$
        OPT_OUTPUT.setArgName("/path/to/output"); //$NON-NLS-1$
        OPT_OUTPUT.setValueSeparator(File.pathSeparatorChar);
        OPT_OUTPUT.setRequired(true);

        OPT_PACKAGE = new Option("package", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optPackage")); //$NON-NLS-1$
        OPT_PACKAGE.setArgName("pkg.name"); //$NON-NLS-1$
        OPT_PACKAGE.setRequired(true);

        OPT_HADOOPWORK = new Option("hadoopwork", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optHadoopwork")); //$NON-NLS-1$
        OPT_HADOOPWORK.setArgName("batch/working"); //$NON-NLS-1$
        OPT_HADOOPWORK.setRequired(true);

        OPT_COMPILERWORK = new Option("compilerwork", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optCompilerwork")); //$NON-NLS-1$
        OPT_COMPILERWORK.setArgName("/path/to/temporary"); //$NON-NLS-1$
        OPT_COMPILERWORK.setRequired(false);

        OPT_LINK = new Option("link", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optLink")); //$NON-NLS-1$
        OPT_LINK.setArgName("classlib.jar" + File.pathSeparatorChar + "/path/to/classes"); //$NON-NLS-1$ //$NON-NLS-2$

        OPT_PLUGIN = new Option("plugin", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optPlugin")); //$NON-NLS-1$
        OPT_PLUGIN.setArgName("plugin-1.jar" + File.pathSeparatorChar + "plugin-2.jar"); //$NON-NLS-1$ //$NON-NLS-2$
        OPT_PLUGIN.setRequired(false);

        OPT_SKIPERROR = new Option("skiperror", //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optSkiperror")); //$NON-NLS-1$

        OPT_SCANPATH = new Option("scanpath", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optScanpath")); //$NON-NLS-1$
        OPT_SCANPATH.setArgName("/path/to/classlib"); //$NON-NLS-1$
        OPT_SCANPATH.setRequired(true);

        OPT_INCLUDE = new Option("include", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optInclude")); //$NON-NLS-1$
        OPT_INCLUDE.setArgName("class-pattern1[,class-pattern2[,..]]"); //$NON-NLS-1$
        OPT_INCLUDE.setRequired(false);

        OPT_EXCLUDE = new Option("exclude", true, //$NON-NLS-1$
                Messages.getString("AllBatchCompilerDriver.optExclude")); //$NON-NLS-1$
        OPT_EXCLUDE.setArgName("class-pattern1[,class-pattern2[,..]]"); //$NON-NLS-1$
        OPT_EXCLUDE.setRequired(false);

        OPTIONS = new Options();
        OPTIONS.addOption(OPT_OUTPUT);
        OPTIONS.addOption(OPT_PACKAGE);
        OPTIONS.addOption(OPT_HADOOPWORK);
        OPTIONS.addOption(OPT_COMPILERWORK);
        OPTIONS.addOption(OPT_LINK);
        OPTIONS.addOption(OPT_PLUGIN);
        OPTIONS.addOption(OPT_SKIPERROR);
        OPTIONS.addOption(OPT_SCANPATH);
        OPTIONS.addOption(OPT_INCLUDE);
        OPTIONS.addOption(OPT_EXCLUDE);
    }

    /**
     * The program entry.
     * @param args command line arguments
     */
    public static void main(String... args) {
        try {
            if (start(args) == false) {
                System.exit(1);
            }
        } catch (Exception e) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.setWidth(Integer.MAX_VALUE);
            formatter.printHelp(MessageFormat.format("java -classpath ... {0}", //$NON-NLS-1$
                    AllBatchCompilerDriver.class.getName()), OPTIONS, true);
            e.printStackTrace(System.out);
            System.exit(1);
        }
    }

    private static boolean start(String[] args) throws Exception {
        CommandLineParser parser = new BasicParser();
        CommandLine cmd = parser.parse(OPTIONS, args);
        String output = cmd.getOptionValue(OPT_OUTPUT.getOpt());
        String scanPath = cmd.getOptionValue(OPT_SCANPATH.getOpt());
        String packageName = cmd.getOptionValue(OPT_PACKAGE.getOpt());
        String hadoopWork = cmd.getOptionValue(OPT_HADOOPWORK.getOpt());
        String compilerWork = cmd.getOptionValue(OPT_COMPILERWORK.getOpt());
        String link = cmd.getOptionValue(OPT_LINK.getOpt());
        String plugin = cmd.getOptionValue(OPT_PLUGIN.getOpt());
        boolean skipError = cmd.hasOption(OPT_SKIPERROR.getOpt());
        String include = cmd.getOptionValue(OPT_INCLUDE.getOpt());
        String exclude = cmd.getOptionValue(OPT_EXCLUDE.getOpt());

        File outputDirectory = new File(output);
        Location hadoopWorkLocation = Location.fromPath(hadoopWork, '/');
        File compilerWorkDirectory = new File(compilerWork);
        List<File> linkingResources = extractEmbedResources(link);
        List<URL> pluginLocations = extractPluginPath(plugin);
        ClassNamePattern includePattern = ClassNamePattern.parse(include, true);
        ClassNamePattern excludePattern = ClassNamePattern.parse(exclude, false);
        Set<String> errorBatches = new HashSet<>();
        boolean succeeded = true;
        try {
            ResourceRepository scanner = getScanner(new File(scanPath));
            try (Cursor cursor = scanner.createCursor()) {
                while (cursor.next()) {
                    Location location = cursor.getLocation();
                    Class<? extends BatchDescription> batchDescription = getBatchDescription(location,
                            includePattern, excludePattern);
                    if (batchDescription == null) {
                        continue;
                    }
                    boolean singleSucceeded = BatchCompilerDriver.compile(outputDirectory, batchDescription,
                            packageName, hadoopWorkLocation, compilerWorkDirectory, linkingResources,
                            pluginLocations);
                    succeeded &= singleSucceeded;
                    if (singleSucceeded == false) {
                        errorBatches.add(toClassName(location));
                        if (skipError == false) {
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOG.error(MessageFormat.format(
                    Messages.getString("AllBatchCompilerDriver.errorFailedToSearchForBatchClass"), //$NON-NLS-1$
                    scanPath), e);
        }
        if (succeeded) {
            LOG.info(Messages.getString("AllBatchCompilerDriver.infoCompleteBatchClass")); //$NON-NLS-1$
        } else {
            LOG.error(MessageFormat.format(
                    Messages.getString("AllBatchCompilerDriver.errorFailedToCompileBatchClass"), //$NON-NLS-1$
                    errorBatches));
        }

        return succeeded;
    }

    private static List<File> extractEmbedResources(String path) {
        if (path == null) {
            return Collections.emptyList();
        }
        List<File> results = new ArrayList<>();
        for (String s : path.split(File.pathSeparator)) {
            results.add(new File(s));
        }
        return results;
    }

    private static List<URL> extractPluginPath(String path) {
        if (path == null) {
            return Collections.emptyList();
        }
        List<URL> results = new ArrayList<>();
        for (String s : path.split(File.pathSeparator)) {
            if (s.trim().isEmpty()) {
                continue;
            }
            try {
                File file = new File(s);
                if (file.exists() == false) {
                    throw new FileNotFoundException(file.getAbsolutePath());
                }
                URL url = file.toURI().toURL();
                results.add(url);
            } catch (IOException e) {
                LOG.warn(MessageFormat.format(Messages.getString("AllBatchCompilerDriver.warnFailedToLoadPlugin"), //$NON-NLS-1$
                        s), e);
            }
        }
        return results;
    }

    private static Class<? extends BatchDescription> getBatchDescription(Location location,
            ClassNamePattern include, ClassNamePattern exclude) {
        assert location != null;
        assert include != null;
        assert exclude != null;
        if (isValidClassFileName(location) == false) {
            LOG.debug("invalid batch class name: {}", location); //$NON-NLS-1$
            return null;
        }
        String className = toClassName(location);
        Class<? extends BatchDescription> batchClass = loadIfBatchClass(className);
        if (batchClass == null) {
            LOG.debug("not a batch class: {}", className); //$NON-NLS-1$
            return null;
        }
        if (include.accepts(className) == false || exclude.accepts(className)) {
            LOG.info(MessageFormat.format(Messages.getString("AllBatchCompilerDriver.infoSkipBatchClass"), //$NON-NLS-1$
                    className));
            return null;
        }
        LOG.info(MessageFormat.format(Messages.getString("AllBatchCompilerDriver.infoStartBatchClass"), //$NON-NLS-1$
                className));
        return batchClass;
    }

    private static String toClassName(Location location) {
        assert location != null;
        String className = location.toPath('.');
        className = className.substring(0, className.length() - ".class".length()); //$NON-NLS-1$
        return className;
    }

    private static boolean isValidClassFileName(Location location) {
        assert location != null;
        String simpleName = location.getName();
        if (simpleName.endsWith(".class") == false) { //$NON-NLS-1$
            return false;
        }
        for (Location current = location.getParent(); current != null; current = current.getParent()) {
            if (current.getName().indexOf('.') >= 0) {
                return false;
            }
        }
        if (simpleName.indexOf('$') >= 0) {
            return false;
        }
        return true;
    }

    private static Class<? extends BatchDescription> loadIfBatchClass(String className) {
        try {
            Class<?> aClass = Class.forName(className, false, AllBatchCompilerDriver.class.getClassLoader());
            if (BatchDescription.class.isAssignableFrom(aClass) == false) {
                return null;
            }
            if (aClass.isAnnotationPresent(Batch.class) == false) {
                LOG.warn(MessageFormat.format(Messages.getString("AllBatchCompilerDriver.warnMissingAnnotation"), //$NON-NLS-1$
                        aClass.getName()));
                return null;
            }
            return aClass.asSubclass(BatchDescription.class);
        } catch (ClassNotFoundException e) {
            LOG.debug("failed to load batch class", e); //$NON-NLS-1$
            return null;
        }
    }

    private static ResourceRepository getScanner(File scanPath) throws IOException {
        assert scanPath != null;
        String name = scanPath.getName();
        if (scanPath.exists() == false) {
            throw new FileNotFoundException(
                    MessageFormat.format(Messages.getString("AllBatchCompilerDriver.errorMissingScanpath"), //$NON-NLS-1$
                            scanPath));
        }
        if (scanPath.isDirectory()) {
            return new FileRepository(scanPath);
        } else if (scanPath.isFile() && (name.endsWith(".zip") || name.endsWith(".jar"))) { //$NON-NLS-1$ //$NON-NLS-2$
            return new ZipRepository(scanPath);
        } else {
            throw new IOException(
                    MessageFormat.format(Messages.getString("AllBatchCompilerDriver.errorInvalidScanpath"), //$NON-NLS-1$
                            scanPath));
        }
    }

    private AllBatchCompilerDriver() {
        return;
    }
}