org.vbossica.springbox.cliapp.ModuleLauncher.java Source code

Java tutorial

Introduction

Here is the source code for org.vbossica.springbox.cliapp.ModuleLauncher.java

Source

/*
 * Copyright 2012-2013 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.vbossica.springbox.cliapp;

import java.io.PrintStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.lang.Validate;
import org.reflections.Reflections;

/**
 * Entry-point command line application that automatically discovers modules in the classpath and let individual ones
 * be executed. To register a module:
 *
 * <ol>
 *   <li>Create a class that implements the {@link Module} interface</li>
 *   <li>Annotate the class with {@link ModuleConfiguration}</li>
 *   <li>Place the compiles class in the classpath</li>
 *   <li>Execute {@code org.vbossica.springbox.cliapp.ModuleLauncher --package [package_name] --list} to get the
 *   list of modules present in the package {@code package_name}</li>
 *   <li>Execute {@code org.vbossica.springbox.cliapp.ModuleLauncher --package [package_name] --module [module_name]} to
 *   execute one registered module</li>
 * </ol>
 *
 * @author vladimir
 */
public class ModuleLauncher {

    private static final String HELP_OPTION = "help";
    private static final String PACKAGE_OPTION = "package";
    private static final String LIST_OPTION = "list";
    private static final String MODULE_OPTION = "module";

    public static void main(String[] args) {
        new ModuleLauncher().process(args);
    }

    /**
     * Explores the classpath and returns all the classes with the {@link ModuleConfiguration} annotation.
     */
    private Map<String, ModuleConfig> findModules(String pkgName) {
        Map<String, ModuleConfig> result = Maps.newHashMap();

        Set<Class<?>> annotated = new Reflections(pkgName).getTypesAnnotatedWith(ModuleConfiguration.class);
        for (Class<?> cls : annotated) {
            ModuleConfiguration annotation = cls.getAnnotation(ModuleConfiguration.class);
            ModuleConfig config = new ModuleConfig();
            config.name = annotation.name();
            config.description = annotation.description();
            config.className = cls.getName();

            result.put(config.name, config);
        }
        return result;
    }

    /**
     * Explores the classpath and returns a sorted list of names and descriptions (separated by
     * a dash) of classes annotated with {@link ModuleConfiguration}.
     */
    private List<String> findSortedModuleDescriptions(String pkgName) {
        List<String> names = Lists.newArrayList();

        Set<Class<?>> annotated = new Reflections(pkgName).getTypesAnnotatedWith(ModuleConfiguration.class);
        for (Class<?> cls : annotated) {
            ModuleConfiguration annotation = cls.getAnnotation(ModuleConfiguration.class);
            names.add(annotation.name() + " - " + annotation.description());
        }
        Collections.sort(names);
        return names;
    }

    private final static class ModuleConfig {
        String name;
        String description;
        String className;
    }

    @SuppressWarnings("AccessStaticViaInstance")
    private void process(final String[] args) {
        Options options = new Options()
                .addOption(OptionBuilder.withLongOpt(HELP_OPTION).withDescription("shows this help").create('h'))
                .addOption(OptionBuilder.hasArg().withArgName("name").withDescription("name of the package to scan")
                        .create(PACKAGE_OPTION))
                .addOption(OptionBuilder.withDescription("lists all registered modules").create(LIST_OPTION))
                .addOption(OptionBuilder.hasArg().withArgName("name")
                        .withDescription("name of the module to execute").create(MODULE_OPTION));

        String packageName = null;

        try {
            CommandLine cmd = new PosixParser().parse(options, args, true);
            if (cmd.hasOption(HELP_OPTION) && !cmd.hasOption(MODULE_OPTION)) {
                printHelp(options);
                return;
            }
            if (cmd.hasOption(PACKAGE_OPTION)) {
                packageName = cmd.getOptionValue(PACKAGE_OPTION);
            }
            if (cmd.hasOption(LIST_OPTION)) {
                listModules(packageName, System.out);
                return;
            }
            if (cmd.hasOption(MODULE_OPTION)) {
                String tool = cmd.getOptionValue(MODULE_OPTION);
                initializeTool(packageName, cmd, tool);
            } else {
                System.err.println("missing module definition");
            }
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
            printHelp(options);
        }
    }

    private void listModules(final String packageName, PrintStream out) {
        Validate.notEmpty(packageName, "required package must be set");

        out.println("Registered modules:");
        for (String module : findSortedModuleDescriptions(packageName)) {
            out.println("  " + module);
        }
    }

    private void initializeTool(final String packageName, final CommandLine cmd, final String moduleName)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Validate.notEmpty(packageName, "required package must be set");

        Map<String, ModuleConfig> configs = findModules(packageName);
        ModuleConfig moduleConfig = configs.get(moduleName);
        if (null == moduleConfig || null == moduleConfig.className) {
            throw new IllegalArgumentException("module couldn't be found: " + moduleName);
        }
        Module module = (Module) Class.forName(moduleConfig.className).newInstance();
        Options options = module.getOptions();
        if (options == null) {
            options = new Options();
        }
        if (cmd.hasOption(HELP_OPTION)) {
            printModuleHelp(moduleConfig, options);
            return;
        }
        try {
            CommandLine subCmd = new PosixParser().parse(options, cmd.getArgs());
            module.process(subCmd);
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
            ex.printStackTrace(System.err);
            printModuleHelp(moduleConfig, options);
            System.exit(1);
        }
    }

    private void printModuleHelp(ModuleConfig moduleConfig, Options options) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("java " + ModuleLauncher.class.getName() + " --module " + moduleConfig.name,
                options != null ? options : new Options(), true);
    }

    private void printHelp(final Options options) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("java " + ModuleLauncher.class.getName(), options, true);
    }

}