All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.petals.cli.AbstractMain Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/**
 * Copyright (c) 2010-2012 EBM WebSourcing, 2012-2016 Linagora
 *
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.cli;

import static org.ow2.petals.cli.shell.ShellFactory.DEBUG_LONG_OPTION;
import static org.ow2.petals.cli.shell.ShellFactory.DEBUG_SHORT_OPTION;
import static org.ow2.petals.cli.shell.ShellFactory.YESFLAG_LONG_OPTION;
import static org.ow2.petals.cli.shell.ShellFactory.YESFLAG_SHORT_OPTION;

import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.logging.LogManager;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.ow2.petals.cli.api.command.ConnectionCommand;
import org.ow2.petals.cli.api.command.exception.CommandInvalidException;
import org.ow2.petals.cli.api.connection.ConnectionParameters;
import org.ow2.petals.cli.api.pref.PreferenceFileException;
import org.ow2.petals.cli.api.pref.Preferences;
import org.ow2.petals.cli.api.pref.exception.MissingDefaultPreferenceFileException;
import org.ow2.petals.cli.api.pref.exception.PreferenceFileNotFoundException;
import org.ow2.petals.cli.api.shell.ShellExtension;
import org.ow2.petals.cli.api.shell.exception.DuplicatedCommandException;
import org.ow2.petals.cli.exception.InitLoggingException;
import org.ow2.petals.cli.shell.AbstractShell;
import org.ow2.petals.cli.shell.ShellFactory;
import org.ow2.petals.cli.shell.exception.ShellCreationException;

/**
 * Abract class used as abtract main class for all Petals CLI
 * 
 * @author Christophe DENEUX - Linagaora
 */
public abstract class AbstractMain {

    /**
     * Footer part of the usage message
     */
    public static final String USAGE_FOOTER = String.format("%n%s%n",
            "Which evolution would you like on Petals? Share it! http://www.petalslink.com/feedback");

    /**
     * The short name of the option printing help on command line
     */
    private static final String HELP_SHORT_OPTION = "H";

    /**
     * The long name of the option printing help on command line
     */
    private static final String HELP_LONG_OPTION = "help";

    /**
     * The short name of the option printing the version of Petals CLI
     */
    protected static final String VERSION_SHORT_OPTION = "V";

    /**
     * The long name of the option printing the version of Petals CLI
     */
    private static final String VERSION_LONG_OPTION = "version";

    /**
     * The connection command, or null if no connection command is available.
     */
    private final ConnectionCommand connectionCommand;

    /**
     * @param connectionCommand
     *            The connection command, or null if no connection command is available.
     */
    public AbstractMain(final ConnectionCommand connectionCommand) {
        this.connectionCommand = connectionCommand;
    }

    /**
     * Print version informations
     */
    private void printVersion() {
        final StringBuilder sb = new StringBuilder();

        sb.append(
                String.format("%s %s%n", this.getUsageHeader(),
                        this.getClass().getPackage().getImplementationVersion()));

        sb.append(String.format("%s %s %s%n", System.getProperty("java.runtime.name"),
                System.getProperty("java.runtime.version"), System.getProperty("java.vm.vendor")));

        sb.append(String.format("%s %s %s%n", System.getProperty("os.name"), System.getProperty("os.version"),
                System.getProperty("os.arch")));

        System.out.print(sb.toString());
    }

    /**
     * Print the usage message
     */
    private void printUsage() {
        
        final StringBuilder usage = new StringBuilder().append("[-").append(DEBUG_SHORT_OPTION)
                .append("] ");
        usage.append("[-").append(YESFLAG_SHORT_OPTION).append("] ");
        
        if (this.connectionCommand != null) {
            // We add arguments of the connection command. We don't add the Yes flag of the connection command
            final HelpFormatter helpFormatter = new HelpFormatter();
            final StringWriter sw = new StringWriter();
            helpFormatter.printUsage(new PrintWriter(sw), HelpFormatter.DEFAULT_WIDTH, "",
                    this.connectionCommand.getOptions());
            usage.append(sw.toString());
        }

        usage.append("[-").append(HELP_SHORT_OPTION).append(" | -").append(VERSION_SHORT_OPTION)
                .append(" | ").append(ShellFactory.getUsage()).toString();
        
        final HelpFormatter formatter = new HelpFormatter();
        formatter.setWidth(USAGE_FOOTER.length());
        formatter.printHelp(String.format("%s%n", this.getUsageHeader()), usage.toString(), this.createOptions(),
                USAGE_FOOTER);
    }

    /**
     * By default, the logging system is initialized from the JAR-embedded
     * configuration file 'petals-cli-logging.properties'. At
     * runtime, it is possible to change this configuration using the system
     * property 'java.util.logging.config.file'.
     */
    private final void initLogging() throws InitLoggingException {
        try {
            final String configurationPath = System.getProperty("java.util.logging.config.file");
            if (configurationPath == null) {
                final InputStream is = this.getClass().getResourceAsStream(
                        "/petals-cli-logging.properties");
                if (is == null) {
                    LogManager.getLogManager().readConfiguration();
                } else {
                    LogManager.getLogManager().readConfiguration(is);
                }
            } else {
                LogManager.getLogManager().readConfiguration();
            }
        } catch (final Exception e) {
            throw new InitLoggingException(e);
        }
    }

    /**
     * Parse options and command line arguments
     * 
     * @param args
     *            Arguments from the command line
     * @param preferences
     *            The preferences
     * @param shellExtensions
     *            A list of shell extension, or null if no shell extension is available
     * @return system return code
     */
    public int run(final String[] args, final Preferences preferences, final ShellExtension[] shellExtensions) {
        int exitCode = 0;

        try {
            this.initLogging();

            if (preferences != null) {
                preferences.intializePreferenceParameters();
            }

            final CommandLineParser parser = new DefaultParser();
            try {
                // We can't use a static attribute for options because an error will occurs in unit tests: an instance
                // of options can be parsed once.
                final CommandLine cmd = parser.parse(this.createOptions(), args);
                try {
                    if (cmd.hasOption(VERSION_SHORT_OPTION)) {
                        printVersion();
                    } else if (cmd.hasOption(HELP_SHORT_OPTION)) {
                        this.printUsage();
                    } else {
                        exitCode = createAndRunPetalsCLIShell(cmd, args, preferences,
                                shellExtensions, this.getPromptBase());
                    }
                } catch (final ShellCreationException e) {
                    System.err.println("ERROR creating the shell: " + e.getMessage());
                    e.printStackTrace();
                    exitCode = 1;
                } catch (final DuplicatedCommandException e) {
                    // A command has been registered twice. This has not to occur otherwise it's a bug
                    System.err.println("BUG : ****  Command registered twice: " + e.getCommandName() + " ****");
                    exitCode = 1;
                }
            } catch (final ParseException e) {
                System.err.println("ERROR: " + e.getMessage() + "\n");
                printUsage();
                exitCode = 1;
            }
        } catch (final InitLoggingException e) {
            System.err.println("ERROR: " + e.getMessage() + "\n");
            exitCode = 1;
        } catch (final MissingDefaultPreferenceFileException e) {
            System.err.println("ERROR: " + e.getMessage() + "\n");
            exitCode = 1;
        } catch (final PreferenceFileNotFoundException e) {
            System.err.println("ERROR: " + e.getMessage() + "\n");
            exitCode = 1;
        } catch (final PreferenceFileException e) {
            System.err.println("ERROR: " + e.getMessage() + "\n");
            exitCode = 1;
        }

        return exitCode;
    }

    /**
     * Create the right Petals CLI shell according to arguments of the command line and run it.
     * 
     * @param cmdLine
     *            The command line parsed
     * @param args
     *            Initial arguments of the command line
     * @param preferences
     *            Preferences
     * @param shellExtensions
     *            A list of shell extension, or null if no shell extension is available
     * @param basePrompt
     *            The base prompt
     * @return The exit code associated to the shell execution (and its commands executed)
     * @throws DuplicatedCommandException
     *             A command has been registered twice when creating the shell. As it's a hard coded registration, it's
     *             a BIG bug.
     * @throws ParseException
     *             An error occurs creating the shell about provided options or arguments.
     * @throws ShellCreationException
     *             Another error occurs creating the shell
     */
    private int createAndRunPetalsCLIShell(final CommandLine cmdLine, final String[] args,
            final Preferences preferences,
            final ShellExtension[] shellExtensions, final String basePrompt)
            throws DuplicatedCommandException, ShellCreationException, ParseException {

        int exitCode = 0;

        final AbstractShell shell = ShellFactory.getInstance().newShell(cmdLine, args, preferences, shellExtensions,
                this.getPromptBase());
        if (shell != null) {
            shell.setDefaultPrompt();
            if (this.connectionCommand != null) {
                this.connectionCommand.setShell(shell);
            }

            // We add a shutdown hook to disconnect Petals CLI if needed
            final Runtime runtime = Runtime.getRuntime();
            runtime.addShutdownHook(new Thread("Disconnection hook") {
                @Override
                public void run() {
                    shell.disconnectIfNeeded();
                }
            });

            try {
                if (this.connectionCommand != null) {
                    final ConnectionParameters connectionParameters = this.connectionCommand
                            .parseConnectionParameters(cmdLine, true);
                    shell.setConnectionParameters(connectionParameters);
                }

                shell.run();
                exitCode = shell.getExitStatus();

            } catch (final CommandInvalidException e) {
                final String msg = e.getMessage() != null ? e.getMessage()
                        : "An unexpected error occurred. Use the -" + DEBUG_SHORT_OPTION
                                + " option for more information.";
                System.err.println("ERROR: " + msg + "\n");
                printUsage();
                exitCode = 1;

            } catch (final Exception e) {
                final String msg = e.getMessage() != null ? e.getMessage()
                        : "An unexpected error occurred. Use the -" + DEBUG_SHORT_OPTION
                                + " option for more information.";
                System.err.println("ERROR: " + msg + "\n");
                if (cmdLine.hasOption(DEBUG_SHORT_OPTION)) {
                    e.printStackTrace();
                }
                exitCode = 1;
            }

            // Disconnect if needed to prevent resource consumption if the command 'disconnect' was not used
            shell.disconnectIfNeeded();

        } else {
            System.err.println("ERROR: Unable to determine the execution mode: console, command, file or inlined\n");
            printUsage();
            exitCode = 1;
        }

        return exitCode;
    }

    /**
     * Create the general options of the CLI, composed of:
     * 
    *
  • shell options,
  • *
  • connection options.
  • *
* * @return The general options of the CLI */ private Options createOptions() { final Options options = new Options(); options.addOption(DEBUG_SHORT_OPTION, DEBUG_LONG_OPTION, false, "Print stack trace and debugging informations"); options.addOption(YESFLAG_SHORT_OPTION, YESFLAG_LONG_OPTION, false, "Enable automatic confirmation ('yes' flag)"); if (this.connectionCommand != null) { final Iterator




© 2015 - 2025 Weber Informatics LLC | Privacy Policy