com.zuehlke.sbdfx.utils.CampApplBase.java Source code

Java tutorial

Introduction

Here is the source code for com.zuehlke.sbdfx.utils.CampApplBase.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.zuehlke.sbdfx.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;

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.PosixParser;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;

/**
 *
 * @author cbu
 */
public abstract class CampApplBase {

    private final static Logger LOGGER = LoggerFactory.getLogger(CampApplBase.class);

    protected final Options OPTIONS = new Options();

    private int exitCode = -1;

    @SuppressWarnings("static-access")
    private static final Option O_CONFIG_FILE = OptionBuilder.withLongOpt("config") //$NON-NLS-1$
            .withDescription("config file for string options (keys are long option names). " //$NON-NLS-1$
                    + "Values of config file can be overridden as they " //$NON-NLS-1$
                    + "have less priority than command line args. (default: config.txt") //$NON-NLS-1$
            .hasArg(true).create("c"); //$NON-NLS-1$

    @SuppressWarnings("static-access")
    private static final Option O_TIMEOUT_MINUTES = OptionBuilder.withLongOpt("timeout-minutes") //$NON-NLS-1$
            .withDescription(
                    "timeout for the whole application in minutes. If exceeded, the System will terminate unconditionally (default: 30 minutes).") //$NON-NLS-1$
            .hasArg(true).create("t");

    private CommandLine commandLine;
    private Exception applicationException;
    private int defaultTimeoutMinutes = 30;
    final Properties propertiesFromConfigFile = new Properties();

    private final static double MILLISECONDS_PER_MINUTE = 1000 * 60;

    {
        LoggingInitializer.assertLoggingInitialized();
        OPTIONS.addOption(O_CONFIG_FILE);
        OPTIONS.addOption(O_TIMEOUT_MINUTES);
    }

    protected void mainWithoutSystemExit(final String[] args) {
        final boolean argumentsParsedSuccessfully = parseAndApplyArgs(args);
        if (argumentsParsedSuccessfully) {
            performActions();
        }
    }

    public void setDefaultTimeoutMinutes(final int defaultTimeoutMinutes) {
        this.defaultTimeoutMinutes = defaultTimeoutMinutes;
    }

    private void startWatchdog() {
        final double timeoutMinutes = Double
                .parseDouble(getOptionValue(O_TIMEOUT_MINUTES, String.valueOf(defaultTimeoutMinutes)));
        TimeoutWatchdog.watchJVM((long) (timeoutMinutes * MILLISECONDS_PER_MINUTE));
    }

    protected boolean parseAndApplyArgs(final String[] args) {
        boolean argumentsParsedSuccessfully = false;

        try {
            setExitCode(-1);
            LOGGER.debug("Given Options: {}", Joiner.on(" ").join(args)); //$NON-NLS-1$ //$NON-NLS-2$
            final CommandLineParser parser = new PosixParser();
            commandLine = parser.parse(OPTIONS, args);
            parseConfigFile();
            applyOptions();
            startWatchdog();
            argumentsParsedSuccessfully = true;
        } catch (final Exception e) {
            LOGGER.error("parseAndApplyArgs failed", e);
            LOGGER.error("\n\nInvalid Command Line: " + e.getMessage() + "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$
            printUsage();
        }
        return argumentsParsedSuccessfully;
    }

    /**
     * @return the {@link Exception} thrown by the application, typically
     * causing an exit code of -1
     */
    public Exception getApplicationException() {
        return applicationException;
    }

    protected final void performActions() {
        try {
            setExitCode(-1);
            doPerformActions();
            setExitCode(0);
            LOGGER.info("Finished Successfully."); //$NON-NLS-1$
        } catch (final Exception e) {
            applicationException = e;
            LOGGER.error("Failed to perform actions", e); //$NON-NLS-1$
        }
    }

    private void parseConfigFile() throws FileNotFoundException, IOException {
        final String configFileLocation = getOptionValue(O_CONFIG_FILE, "config.txt").trim(); //$NON-NLS-1$
        final File configFile = new File(configFileLocation);
        if (configFile.exists()) {
            LOGGER.info("Reading options from config file " + configFile.getAbsolutePath()); //$NON-NLS-1$
            FileReader reader = null;
            try {
                reader = new FileReader(configFile);
                propertiesFromConfigFile.load(reader);
            } finally {
                IOUtils.closeQuietly(reader);
            }
        }
    }

    protected abstract void doPerformActions() throws Exception;

    protected List<String> getOptionValues(final Option option) {
        final String[] optionValues = commandLine.getOptionValues(option.getLongOpt());
        if (optionValues == null) {
            return Collections.emptyList();
        }
        final List<String> result = Lists.newArrayList();
        for (final String optionValue : optionValues) {
            if (StringUtils.isNotBlank(optionValue)) {
                result.add(optionValue);
            }
        }
        return result;
    }

    protected void applyOptions() {
        applyApplicationSpecificOptions();
    }

    protected void applyApplicationSpecificOptions() {
        // intentionally left empty. To be implemented by subclasses for
        // additional options.
    }

    protected String getOptionValue(final Option option) {
        return getOptionValue(option, null);
    }

    protected boolean hasOption(final Option option) {
        return commandLine.hasOption(option.getLongOpt());
    }

    protected String getOptionValue(final Option option, final String defaultValue) {

        String result = commandLine.getOptionValue(option.getLongOpt(), "").trim(); //$NON-NLS-1$
        if (result.length() > 0) {
            return result;
        }

        final String longOpt = option.getLongOpt();
        result = propertiesFromConfigFile.getProperty(longOpt, "").trim(); //$NON-NLS-1$
        if (result.length() > 0) {
            return result;
        }
        if (defaultValue != null) {
            return defaultValue;
        }
        throw new IllegalArgumentException("Missing value for required option " + longOpt); //$NON-NLS-1$
    }

    protected boolean isOptionPresent(final Option option) {
        return commandLine.hasOption(option.getLongOpt());
    }

    protected Long getOptionValueAsLong(final Option option, final Long defaultValue) {
        final String optionValueString = getOptionValue(option,
                defaultValue != null ? String.valueOf(defaultValue) : ""); //$NON-NLS-1$
        if (StringUtils.isNotBlank(optionValueString)) {
            try {
                return Long.parseLong(optionValueString);
            } catch (final Exception e) {
                throw new IllegalArgumentException(
                        MessageFormat.format("Failed to parse option {0} as integer: {1}", //$NON-NLS-1$
                                option.getLongOpt(), optionValueString));
            }
        } else {
            return defaultValue;
        }
    }

    protected File getOptionValueAsFile(final Option option, final String defaultValue, final boolean forceExists) {
        final String optionValueString = getOptionValue(option,
                defaultValue != null ? String.valueOf(defaultValue) : ""); //$NON-NLS-1$
        File result = null;
        if (StringUtils.isNotBlank(optionValueString)) {
            result = new File(optionValueString);
        } else if (StringUtils.isNotBlank(defaultValue)) {
            result = new File(defaultValue);
        } else {
            throw new IllegalArgumentException("Missing option or default value: " + option.getOpt());
        }
        if (forceExists && !result.exists()) {
            throw new IllegalArgumentException(
                    "File does not exist for option : " + option.getOpt() + " - " + result.getAbsolutePath());
        }
        return result;
    }

    private void printUsage() {
        final HelpFormatter formatter = new HelpFormatter();
        formatter.setWidth(120);
        formatter.printHelp("\n", OPTIONS); //$NON-NLS-1$
    }

    public void setExitCode(final int exitCode) {
        this.exitCode = exitCode;
    }

    public int getExitCode() {
        return exitCode;
    }

    protected Option getSelectedOption(final Set<Option> exclusiveOptions) {
        Option selectedMode = null;
        for (final Option o : exclusiveOptions) {
            if (isOptionPresent(o)) {
                if (selectedMode == null) {
                    selectedMode = o;
                } else {
                    final Function<Option, String> optionToLongArgs = new Function<Option, String>() {
                        @Override
                        public String apply(final Option from) {
                            return from.getLongOpt();
                        }
                    };
                    throw new IllegalArgumentException(
                            "Only one of the following options allowed: " + Joiner.on(", ")
                                    .join(Lists.transform(Lists.newArrayList(exclusiveOptions), optionToLongArgs)));
                }
            }
        }
        return selectedMode;
    }

}