org.lilyproject.cli.BaseCliTool.java Source code

Java tutorial

Introduction

Here is the source code for org.lilyproject.cli.BaseCliTool.java

Source

/*
 * Copyright 2010 Outerthought bvba
 *
 * 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.lilyproject.cli;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

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.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.xml.DOMConfigurator;
import org.lilyproject.util.exception.StackTracePrinter;

/**
 * Base framework for Lily CLI tools. Purpose is to have some uniformity in the CLI tools and to avoid
 * code duplication.
 *
 * Subclasses can override:
 * <ul>
 * <li>{@link #getCmdName()}
 * <li>{@link #getOptions()}
 * <li>{@link #processOptions(org.apache.commons.cli.CommandLine)}
 * <li>{@link #run(org.apache.commons.cli.CommandLine)}
 * <li>{@link #reportThrowable(Throwable)}
 * </ul>
 */
public abstract class BaseCliTool {
    protected Option helpOption;
    protected Option versionOption;
    protected Option logConfOption;
    protected Option dumpLogConfOption;
    private Options cliOptions;

    protected void start(String[] args) {
        int result = 1;
        try {
            System.out.println();
            result = runBase(args);
        } catch (CliException e) {
            System.err.println();
            System.err.println(e.getMessage());
            System.exit(e.getExitCode());
        } catch (Throwable t) {
            reportThrowable(t);
        } finally {
            try {
                cleanup();
            } catch (Throwable t) {
                System.err.println("Error during cleanup:");
                t.printStackTrace();
            }
        }
        System.out.println();

        if (result != 0) {
            System.exit(result);
        }
    }

    protected void reportThrowable(Throwable throwable) {
        StackTracePrinter.printStackTrace(throwable);
    }

    /**
     * Return the CLI options. When overriding, call super and add your own
     * options.
     */
    public List<Option> getOptions() {
        List<Option> options = new ArrayList<Option>();

        helpOption = new Option("h", "help", false, "Shows help");
        options.add(helpOption);

        versionOption = new Option("v", "version", false, "Shows the version");
        options.add(versionOption);

        logConfOption = OptionBuilder.withArgName("config").hasArg()
                .withDescription("log4j config file (.properties or .xml)").create("log");
        options.add(logConfOption);

        dumpLogConfOption = OptionBuilder.withDescription("Dump default log4j configuration").create("dumplog");
        options.add(dumpLogConfOption);

        return options;
    }

    /**
     * The name of this CLI tool, used in the help message.
     */
    protected abstract String getCmdName();

    /**
     * Override this to perform cleanup after the tool has run, also in case
     * an exception occurred (= called from a finally block).
     */
    protected void cleanup() {

    }

    /**
     * Process option values, typically this performs basic stuff like reading
     * the option value and validating it. First always call super, if non-zero
     * is returned, then return this value immediately.
     */
    protected int processOptions(CommandLine cmd) throws Exception {
        if (cmd.hasOption(helpOption.getOpt())) {
            printHelp();
            return 1;
        }

        if (cmd.hasOption(versionOption.getOpt())) {
            System.out.println(getVersion());
            return 1;
        }

        if (cmd.hasOption(dumpLogConfOption.getOpt())) {
            IOUtils.copy(BaseCliTool.class.getResourceAsStream("log4j.properties"), System.out);
            return 1;
        }

        File logConfFile = null;
        if (cmd.hasOption(logConfOption.getOpt())) {
            logConfFile = new File(cmd.getOptionValue(logConfOption.getOpt()));
            if (!logConfFile.exists()) {
                System.err.println("Specified log4j configuration file does not exist:");
                System.err.println(logConfFile);
            }
        }
        setupLogging(logConfFile);

        return 0;
    }

    /**
     * Perform the actual action. First always call super, if non-zero is returned,
     * then return this value immediately.
     */
    public int run(CommandLine cmd) throws Exception {
        return 0;
    }

    private void setupLogging(File logConfFile) {
        // Reset any configuration log4j might already have loaded (from classpath, cwd, ...).
        LogManager.resetConfiguration();

        // If the user did not specify a configuration file, default to log4j.properties in the working dir
        if (logConfFile == null) {
            File defaultConf = new File("log4j.properties");
            if (defaultConf.exists()) {
                logConfFile = defaultConf;
                // printing to err so that this isn't included when stdout is redirected to a file
                System.err
                        .println("Using log4j.properties from working directory: " + logConfFile.getAbsolutePath());
            }
        }

        if (logConfFile == null) {
            // Use the built-in config
            PropertyConfigurator.configure(BaseCliTool.class.getResource("log4j.properties"));
        } else if (logConfFile.getName().endsWith(".xml")) {
            DOMConfigurator.configure(logConfFile.getAbsolutePath());
        } else {
            PropertyConfigurator.configure(logConfFile.getAbsolutePath());
        }
    }

    private int runBase(String[] args) throws Exception {
        //
        // Set up options
        //
        cliOptions = new Options();

        for (Option option : getOptions()) {
            cliOptions.addOption(option);
        }

        //
        // Parse options
        //
        CommandLineParser parser = new PosixParser();
        CommandLine cmd;
        try {
            cmd = parser.parse(cliOptions, args);
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            System.out.println();

            printHelp();
            return 1;
        }

        //
        // Process options
        //
        int result = processOptions(cmd);
        if (result != 0) {
            return result;
        }

        //
        // Run tool
        //
        return run(cmd);

    }

    protected void printHelp() throws IOException {
        printHelpHeader();
        HelpFormatter help = new HelpFormatter();
        help.printHelp(getCmdName(), cliOptions, true);
        printHelpFooter();
    }

    protected void printHelpHeader() throws IOException {
        String className = getClass().getName();
        String helpHeaderPath = className.replaceAll(Pattern.quote("."), "/") + "_help_header.txt";
        InputStream is = getClass().getClassLoader().getResourceAsStream(helpHeaderPath);
        if (is != null) {
            IOUtils.copy(is, System.out);
            System.out.println();
            System.out.println();
        }
    }

    protected void printHelpFooter() throws IOException {
        String className = getClass().getName();
        String helpHeaderPath = className.replaceAll(Pattern.quote("."), "/") + "_help_footer.txt";
        InputStream is = getClass().getClassLoader().getResourceAsStream(helpHeaderPath);
        if (is != null) {
            System.out.println();
            IOUtils.copy(is, System.out);
            System.out.println();
        }
    }

    protected abstract String getVersion();
}