alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (CLICommand.java)

This example Glassfish source code file (CLICommand.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

bufferedreader, clicommand, commandexception, commandexception, commandvalidationexception, io, ioexception, log, logging, parammodel, parammodel, parser, reflection, string, string, stringbuffer, stringbuilder, stringbuilder, util

The Glassfish CLICommand.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.enterprise.admin.cli;

import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import java.util.logging.*;

import org.jvnet.hk2.annotations.*;
import org.jvnet.hk2.component.*;
import com.sun.hk2.component.InjectionResolver;

import org.glassfish.api.Param;
import org.glassfish.api.admin.*;
import org.glassfish.api.admin.CommandModel.ParamModel;
import org.glassfish.common.util.admin.CommandModelImpl;
import org.glassfish.common.util.admin.MapInjectionResolver;

import com.sun.enterprise.admin.util.CommandModelData.ParamModelData;
import com.sun.enterprise.admin.cli.remote.RemoteCommand;
import com.sun.enterprise.universal.i18n.LocalStringsImpl;
import com.sun.enterprise.universal.glassfish.ASenvPropertyReader;
import com.sun.logging.LogDomains;


/**
 * Base class for a CLI command.  An instance of a subclass of this
 * class is created using the getCommand method with the name of the
 * command and the information about its environment.
 * <p>
 * A command is executed with a list of arguments using the execute
 * method.  The implementation of the execute method in this class
 * saves the arguments in the protected argv field, then calls the
 * following protected methods in order: prepare, parse, validate,
 * and executeCommand.  A subclass must implement the prepare method
 * to initialize the metadata that specified the valid options for
 * the command, and the executeCommand method to actually perform the
 * command.  The parse and validate method may also be overridden if
 * needed.  Or, the subclass may override the execute method and
 * provide the complete implementation for the command, including
 * option parsing.
 *
 * @author Bill Shannon
 */
@Contract
@Scoped(PerLookup.class)
public abstract class CLICommand implements PostConstruct {
    public static final int ERROR = CLIConstants.ERROR;
    public static final int CONNECTION_ERROR = 2;
    public static final int INVALID_COMMAND_ERROR = 3;
    public static final int SUCCESS = CLIConstants.SUCCESS;
    public static final int WARNING = CLIConstants.WARNING;

    private static final Set<String> unsupported;
    private static final String UNSUPPORTED_CMD_FILE_NAME =
                                    "unsupported-legacy-command-names";
    private static final String PACKAGE_NAME = "com.sun.enterprise.admin.cli";

    private static final LocalStringsImpl strings =
            new LocalStringsImpl(CLICommand.class);

    private static final Map<String,String> systemProps = 
            Collections.unmodifiableMap(new ASenvPropertyReader().getProps());

    protected static final Logger logger =
            Logger.getLogger(CLICommand.class.getPackage().getName());

    // InjectionManager is completely stateless with only one method that
    // operates on its arguments, so we can share a single instance.
    private static final InjectionManager injectionMgr = new InjectionManager();

    /**
     * The name of the command.
     * Initialized in the constructor.
     */
    protected String name;

    /**
     * The program options for the command.
     * Initialized in the constructor.
     */
    @Inject
    protected ProgramOptions programOpts;

    /**
     * The environment for the command.
     * Initialized in the constructor.
     */
    @Inject
    protected Environment env;

    /**
     * The command line arguments for this execution.
     * Initialized in the execute method.
     */
    protected String[] argv;

    /**
     * The metadata describing the command's options and operands.
     */
    protected CommandModel commandModel;

    protected StringBuilder metadataErrors;

    /**
     * The options parsed from the command line.
     * Initialized by the parse method.  The keys
     * are the parameter names from the command model,
     * not the "forced to all lower case" names that
     * are presented to the user.
     */
    protected ParameterMap options;

    /**
     * The operands parsed from the command line.
     * Initialized by the parse method.
     */
    protected List<String> operands;

    /**
     * The passwords read from the password file.
     * Initialized by the initializeCommandPassword method.
     */
    protected Map<String, String> passwords;

    static {
        Set<String> unsup = new HashSet();
        file2Set(UNSUPPORTED_CMD_FILE_NAME, unsup);
        unsupported = Collections.unmodifiableSet(unsup);
    }

    /**
     * Get a CLICommand object representing the named command.
     */
    public static CLICommand getCommand(Habitat habitat, String name)
            throws CommandException {

        // first, check if it's a known unsupported command
        checkUnsupportedLegacyCommand(name);

        // next, try to load our own implementation of the command
        CLICommand cmd = habitat.getComponent(CLICommand.class, name);
        if (cmd != null)
            return cmd;

        // nope, must be a remote command
        logger.finer("Assuming it's a remote command: " + name);
        return new RemoteCommand(name,
            habitat.getComponent(ProgramOptions.class),
            habitat.getComponent(Environment.class));
    }

    /**
     * Constructor used by subclasses when instantiated by HK2.
     * ProgramOptions and Environment are injected.  name is set here.
     */
    protected CLICommand() {
        Service service = this.getClass().getAnnotation(Service.class);

        if (service == null)
            name = "unknown-command";   // should never happen
        else
            name = service.name();
    }

    /**
     * Initialize the logger after being instantiated by HK2.
     */
    public void postConstruct() {
        initializeLogger();
    }

    /**
     * Constructor used by subclasses to save the name, program options,
     * and environment information into corresponding protected fields.
     * Finally, this constructor calls the initializeLogger method.
     */
    protected CLICommand(String name, ProgramOptions programOpts,
            Environment env) {
        this.name = name;
        this.programOpts = programOpts;
        this.env = env;
        initializeLogger();
    }

    /**
     * Execute this command with the given arguemnts.
     * The implementation in this class saves the passed arguments in
     * the argv field and calls the initializePasswords method.
     * Then it calls the prepare, parse, and validate methods, finally
     * returning the result of calling the executeCommand method.
     * Note that argv[0] is the command name.
     *
     * @throws CommandException if execution of the command fails
     * @throws CommandValidationException if there's something wrong
     *          with the options or arguments
     */
    public int execute(String... argv) throws CommandException {
        this.argv = argv;
        initializePasswords();
        logger.finer("Prepare");
        prepare();
        logger.finer("Process program options");
        processProgramOptions();
        logger.finer("Parse command options");
        parse();
        if (checkHelp())
            return 0;
        logger.finer("Prevalidate command options");
        prevalidate();
        logger.finer("Inject command options");
        inject();
        logger.finer("Validate command options");
        validate();
        if (programOpts.isEcho()) {
            logger.info(echoCommand());
            // In order to avoid echoing commands used intenally to the
            // implementation of *this* command, we turn off echo after
            // having echoed this command.
            programOpts.setEcho(false);
        } else if (logger.isLoggable(Level.FINER))
            logger.finer(echoCommand());
        logger.finer("Execute command");
        return executeCommand();
    }

    /**
     * Return the name of this command.
     */
    public String getName() {
        return name;
    }

    /**
     * Returns the program options associated with this command.
     * 
     * @return the command's program options
     */
    public ProgramOptions getProgramOptions() {
        return programOpts;
    }

    /**
     * Return a Reader for the man page for this command,
     * or null if not found.
     */
    public Reader getManPage() {
        return CLIManFileFinder.getCommandManFile(this);
    }

    /**
     * Get the usage text.
     *
     * @return usage text
     */
    public String getUsage() {
        String usage;
        if (commandModel != null && ok(usage = commandModel.getUsageText())) {
            StringBuffer usageText = new StringBuffer();
            usageText.append(
                strings.get("Usage", strings.get("Usage.asadmin")));
            usageText.append(" ");
            usageText.append(usage);
            return usageText.toString();
        } else {
            return generateUsageText();
        }
    }

    private String generateUsageText() {
        StringBuilder usageText = new StringBuilder();
        usageText.append(strings.get("Usage", strings.get("Usage.asadmin")));
        usageText.append(" ");
        usageText.append(getName());
        int len = usageText.length();
        StringBuilder optText = new StringBuilder();
        String lsep = System.getProperty("line.separator");
        for (ParamModel opt : usageOptions()) {
            optText.setLength(0);
            final String optName = lc(opt.getName());
            // "--terse" is part of asadmin utility options
            if (optName.equals("terse"))
                continue;
            // skip "hidden" options
            if (optName.startsWith("_"))
                continue;
            // do not want to display password as an option
            if (opt.getParam().password())
                continue;
            // also do not want to display obsolete options
            if (opt.getParam().obsolete())
                continue;
            // primary parameter is the operand, not an option
            if (opt.getParam().primary())
                continue;
            boolean optional = opt.getParam().optional();
            String defValue = opt.getParam().defaultValue();
            if (optional)
                optText.append("[");
            String sn = opt.getParam().shortName();
            if (ok(sn))
                optText.append('-').append(sn).append('|');
            optText.append("--").append(optName);

            if (opt.getType() == Boolean.class ||
                opt.getType() == boolean.class) {
                // canonicalize default value
                if (ok(defValue) && Boolean.parseBoolean(defValue))
                    defValue = "true";
                else
                    defValue = "false";
                optText.append("[=<").append(optName);
                optText.append(strings.get("Usage.default", defValue));
                optText.append(">]");
            } else {    // STRING or FILE
                if (ok(defValue)) {
                    optText.append(" <").append(optName);
                    optText.append(strings.get("Usage.default", defValue));
                    optText.append('>');
                } else
                    optText.append(" <").append(optName).append('>');
            }
            if (optional)
                optText.append("]");

            if (len + 1 + optText.length() > 80) {
                usageText.append(lsep).append('\t');
                len = 8;
            } else {
                usageText.append(' ');
                len++;
            }
            usageText.append(optText);
            len += optText.length();
        }

        // add --help text
        String helpText = "[-?|--help[=<help(default:false)>]]";
        if (len + 1 + helpText.length() > 80) {
            usageText.append(lsep).append('\t');
            len = 8;
        } else {
            usageText.append(' ');
            len++;
        }
        usageText.append(helpText);
        len += helpText.length();

        optText.setLength(0);
        ParamModel operandParam = getOperandModel();
        String opname = operandParam != null ?
	    	lc(operandParam.getName()) : null;
        if (!ok(opname))
            opname = "operand";

        int operandMin = 0;
        int operandMax = 0;
        if (operandParam != null) {
            operandMin = operandParam.getParam().optional() ? 0 : 1;
            operandMax = operandParam.getParam().multiple() ?
                                                        Integer.MAX_VALUE : 1;
        }
        if (operandMax > 0) {
            if (operandMin == 0) {
                optText.append("[").append(opname);
                if (operandMax > 1)
                    optText.append(" ...");
                optText.append("]");
            } else {
                optText.append(opname);
                if (operandMax > 1)
                    optText.append(" ...");
            }
        }
        if (len + 1 + optText.length() > 80) {
            usageText.append(lsep).append('\t');
            len = 8;
        } else {
            usageText.append(' ');
            len++;
        }
        usageText.append(optText);
        return usageText.toString();
    }

    /**
     * Subclasses can override this method to supply additional
     * or different options that should be part of the usage text.
     * Most commands will never need to do this, but the create-domain
     * command uses it to include the --user option as a required option.
     */
    protected Collection<ParamModel> usageOptions() {
        return commandModel.getParameters();
    }

    @Override
    public String toString() {
        return echoCommand();
    }


    /**
     * Return a string representing the command line used with this command.
     */
    private String echoCommand() {
        StringBuilder sb = new StringBuilder();

        // first, the program options
        sb.append("asadmin ");
        sb.append(programOpts.toString()).append(' ');

        // now the subcommand options and operands
        sb.append(name).append(' ');

        // have we parsed any options yet?
        if (options != null && operands != null) {
            for (ParamModel opt : commandModel.getParameters()) {
                if (opt.getParam().password())
                    continue;       // don't print passwords
                if (opt.getParam().primary())
                    continue;
                // include every option that was specified on the command line
                // and every option that has a default value
                String value = getOption(opt.getName());
                if (value == null) {
                    value = opt.getParam().defaultValue();
                    if (value != null && value.length() == 0)
                        value = null;
                }
                if (value != null) {
                    sb.append("--").append(lc(opt.getName()));
                    if (opt.getType() == Boolean.class ||
                        opt.getType() == boolean.class) {
                        if (Boolean.parseBoolean(value))
                            sb.append("=").append("true");
                        else
                            sb.append("=").append("false");
                    } else {    // STRING or FILE
                        sb.append(" ").append(quote(value));
                    }
                    sb.append(' ');
                }
            }
            for (String o : operands)
                sb.append(quote(o)).append(' ');
        } else if (argv != null) {
            // haven't parsed any options, include raw arguments, if any
            for (String arg : argv)
                sb.append(quote(arg)).append(' ');
        }

        sb.setLength(sb.length() - 1);  // strip trailing space
        return sb.toString();
    }

    /**
     * Quote a value, if the value contains any special characters.
     *
     * @param   value   value to be quoted
     * @return          the possibly quoted value
     */
    public static String quote(String value) {
        int len = value.length();
        if (len == 0)
            return "\"\"";      // an empty string is handled specially

        /*
         * Look for any special characters.  Escape and
         * quote the entire string if necessary.
         */
        boolean needQuoting = false;
        for (int i = 0; i < len; i++) {
            char c = value.charAt(i);
            if (c == '"' || c == '\\' || c == '\r' || c == '\n') {
                // need to escape them and then quote the whole string
                StringBuffer sb = new StringBuffer(len + 3);
                sb.append('"');
                sb.append(value.substring(0, i));
                int lastc = 0;
                for (int j = i; j < len; j++) {
                    char cc = value.charAt(j);
                    if ((cc == '"') || (cc == '\\') || 
                        (cc == '\r') || (cc == '\n'))
                        if (cc == '\n' && lastc == '\r')
                            ;   // do nothing, CR was already escaped
                        else
                            sb.append('\\');    // Escape the character
                    sb.append(cc);
                    lastc = cc;
                }
                sb.append('"');
                return sb.toString();
            } else if (c <= 040 || c >= 0177)
                // These characters cause the string to be quoted
                needQuoting = true;
        }

        if (needQuoting) {
            StringBuffer sb = new StringBuffer(len + 2);
            sb.append('"').append(value).append('"');
            return sb.toString();
        } else 
            return value;
    }

    /**
     * If the program options haven't already been set, parse them
     * on the command line and remove them from the command line.
     * Subclasses should call this method in their prepare method
     * after initializing commandOpts (so usage is available on failure)
     * if they want to allow program options after the command name.
     * Currently RemoteCommand does this, as well as the local commands
     * that also need to talk to the server.
     */
    protected void processProgramOptions() throws CommandException {
        if (!programOpts.isOptionsSet()) {
            logger.finer("Parsing program options");
            /*
             * asadmin options and command options are intermixed.
             * Parse the entire command line for asadmin options,
             * removing them from the command line, and ignoring
             * unknown options.
             */
            Parser rcp = new Parser(argv, 0,
                            ProgramOptions.getValidOptions(), true);
            ParameterMap params = rcp.getOptions();
            List<String> operands = rcp.getOperands();
            argv = operands.toArray(new String[operands.size()]);
            if (params.size() > 0) {
                // at least one program option specified after command name
                logger.finer("Update program options");
                programOpts.updateOptions(params);
                initializeLogger();
                initializePasswords();
                if (!programOpts.isTerse() &&
                        !(params.size() == 1 && params.get("help") != null)) {
                    // warn about deprecated use of program options
                    // (except --help)
                    // XXX - a lot of work for a nice message...
                    Collection<ParamModel> programOptions =
                            ProgramOptions.getValidOptions();
                    StringBuilder sb = new StringBuilder();
                    sb.append("asadmin");
                    for (Map.Entry<String,List p : params.entrySet()) {
                        // find the corresponding ParamModel
                        ParamModel opt = null;
                        for (ParamModel vo : programOptions) {
                            if (vo.getName().equalsIgnoreCase(p.getKey())) {
                                opt = vo;
                                break;
                            }
                        }
                        if (opt == null)        // should never happen
                            continue;

                        // format the option appropriately
                        sb.append(" --").append(p.getKey());
                        List<String> pl = p.getValue();
                        // XXX - won't handle multi-values
                        if (opt.getType() == Boolean.class ||
                            opt.getType() == boolean.class) {
                            if (!pl.get(0).equalsIgnoreCase("true"))
                                sb.append("=false");
                        } else {
                            if (pl != null && pl.size() > 0)
                                sb.append(" ").append(pl.get(0));
                        }
                    }
                    sb.append(" ").append(name).append(" [options] ...");
                    logger.info(strings.get("DeprecatedSyntax"));
                    logger.info(sb.toString());
                }
            }
        }
    }

    /**
     * Initialize the state of the logger based on any program options.
     */
    protected void initializeLogger() {
        if (!logger.isLoggable(Level.FINER)) {
            if (programOpts.isTerse())
                logger.setLevel(Level.INFO);
            else
                logger.setLevel(Level.FINE);
        }
    }

    /**
     * Initialize the passwords field based on the password
     * file specified in the program options, and initialize the
     * program option's password if available in the password file.
     */
    protected void initializePasswords() throws CommandException {
        passwords = new HashMap<String, String>();
        String pwfile = programOpts.getPasswordFile();

        if (ok(pwfile)) {
            passwords = CLIUtil.readPasswordFileOptions(pwfile, true);
            logger.finer("Passwords were read from password file: " +
                                        pwfile);
            String password = passwords.get(
                    Environment.AS_ADMIN_ENV_PREFIX + "PASSWORD");
            if (password != null && programOpts.getPassword() == null)
                programOpts.setPassword(password,
                    ProgramOptions.PasswordLocation.PASSWORD_FILE);
        }
    }

    /**
     * The prepare method must ensure that the commandModel field is set.
     */
    protected void prepare() throws CommandException {
        commandModel = new CommandModelImpl(this.getClass());
    }

    /**
     * The parse method sets the options and operands fields
     * based on the content of the command line arguments.
     * If the program options say this is a help request,
     * we set options and operands as if "--help" had been specified.
     *
     * @throws CommandException if execution of the command fails
     * @throws CommandValidationException if there's something wrong
     *          with the options or arguments
     */
    protected void parse() throws CommandException {
        /*
         * If this is a help request, we don't need the command
         * metadata and we throw away all the other options and
         * fake everything else.
         */
        if (programOpts.isHelp()) {
            options = new ParameterMap();
            options.set("help", "true");
            operands = Collections.emptyList();
        } else {
            Parser rcp =
                new Parser(argv, 1, commandModel.getParameters(),
                    commandModel.unknownOptionsAreOperands());
            options = rcp.getOptions();
            operands = rcp.getOperands();

            /*
             * In the case where we're accepting unknown options as
             * operands, the special "--" delimiter will also be
             * accepted as an operand.  We eliminate it here.
             */
            if (commandModel.unknownOptionsAreOperands() &&
                    operands.size() > 0 && operands.get(0).equals("--"))
                operands.remove(0);
        }
        logger.finer("params: " + options);
        logger.finer("operands: " + operands);
    }

    /**
     * Check if the current request is a help request, either because
     * --help was specified as a programoption or a command option.
     * If so, get the man page using the getManPage method, copy the
     * content to System.out, and return true.  Otherwise return false.
     * Subclasses may override this method to perform a different check
     * or to use a different method to display the man page.
     * If this method returns true, the validate and executeCommand methods
     * won't be called.
     */
    protected boolean checkHelp() throws CommandException {
        if (programOpts.isHelp()) {
            Reader r = getManPage();
            if (r == null)
                throw new CommandException(strings.get("ManpageMissing", name));
            BufferedReader br = new BufferedReader(r);
            String line;
            try {
            while ((line = br.readLine()) != null)
                System.out.println(line);
            } catch (IOException ioex) {
                throw new CommandException(
                            strings.get("ManpageMissing", name), ioex);
            }
            return true;
        } else
            return false;
    }

    /**
     * The prevalidate method supplies missing options from
     * the environment.  It also supplies passwords from the password
     * file or prompts for them if interactive.
     *
     * @throws CommandException if execution of the command fails
     * @throws CommandValidationException if there's something wrong
     *          with the options or arguments
     */
    protected void prevalidate() throws CommandException {
        /*
         * First, check that the command has the proper scope.
         * (Could check this in getCommand(), but at that point we
         * don't have the CommandModel yet.)
         * Remote commands are checked on the server.
         */
        if (!(this instanceof RemoteCommand)) {
            Scoped scoped = this.getClass().getAnnotation(Scoped.class);
            if (scoped == null) {
                throw new CommandException(strings.get("NoScope", name));
            } else if (scoped.value() == Singleton.class) {
                // check that there are no parameters for this command
                if (commandModel.getParameters().size() > 0) {
                    throw new CommandException(strings.get("HasParams", name));
                }
            }
        }

        /*
         * Check for missing options and operands.
         */
        Console cons = programOpts.isInteractive() ? System.console() : null;

        boolean missingOption = false;
        for (ParamModel opt : commandModel.getParameters()) {
            if (opt.getParam().password())
                continue;       // passwords are handled later
	    if (opt.getParam().obsolete() && getOption(opt.getName()) != null)
		logger.info(
			strings.get("ObsoleteOption", opt.getName()));
            if (opt.getParam().optional())
                continue;
            if (opt.getParam().primary())
                continue;
            // if option isn't set, prompt for it (if interactive)
            if (getOption(opt.getName()) == null && cons != null &&
                    !missingOption) {
                cons.printf("%s",
                    strings.get("optionPrompt", lc(opt.getName())));
                String val = cons.readLine();
                if (ok(val))
                    options.set(opt.getName(), val);
            }
            // if it's still not set, that's an error
            if (getOption(opt.getName()) == null) {
                missingOption = true;
                logger.info(
                        strings.get("missingOption", "--" + opt.getName()));
            }
	    if (opt.getParam().obsolete())	// a required obsolete option?
		logger.info(
			strings.get("ObsoleteOption", opt.getName()));
        }
        if (missingOption)
            throw new CommandValidationException(
                    strings.get("missingOptions", name));

        int operandMin = 0;
        int operandMax = 0;
        ParamModel operandParam = getOperandModel();
        if (operandParam != null) {
            operandMin = operandParam.getParam().optional() ? 0 : 1;
            operandMax = operandParam.getParam().multiple() ?
                                                        Integer.MAX_VALUE : 1;
        }

        if (operands.size() < operandMin && cons != null) {
            cons.printf("%s",
                strings.get("operandPrompt", operandParam.getName()));
            String val = cons.readLine();
            if (ok(val)) {
                operands = new ArrayList<String>();
                operands.add(val);
            }
        }
        if (operands.size() < operandMin)
            throw new CommandValidationException(
                    strings.get("notEnoughOperands", name,
                                operandParam.getType()));
        if (operands.size() > operandMax) {
            if (operandMax == 0)
                throw new CommandValidationException(
                    strings.get("noOperandsAllowed", name));
            else if (operandMax == 1)
                throw new CommandValidationException(
                    strings.get("tooManyOperands1", name));
            else
                throw new CommandValidationException(
                    strings.get("tooManyOperands", name, operandMax));
        }

        initializeCommandPassword();
    }

    /**
     * Inject this instance with the final values of all the command
     * parameters.
     *
     * @throws CommandException if execution of the command fails
     * @throws CommandValidationException if there's something wrong
     *          with the options or arguments
     */
    protected void inject() throws CommandException {
        // injector expects operands to be in the ParameterMap with the key
        // "DEFAULT"
        options.set("DEFAULT", operands);

        // if command has a "terse" option, set it from ProgramOptions
        if (commandModel.getModelFor("terse") != null)
            options.set("terse", Boolean.toString(programOpts.isTerse()));

        // initialize the injector.
        InjectionResolver<Param> injector =
                    new MapInjectionResolver(commandModel, options);

        // inject
        try {
            injectionMgr.inject(this, injector);
        } catch (UnsatisfiedDependencyException e) {
            throw new CommandValidationException(e.getMessage(), e);
        }
    }

    /**
     * The validate method can be used by a subclass to validate
     * that the type and quantity of parameters and operands matches
     * the requirements for this command.
     *
     * @throws CommandException if execution of the command fails
     * @throws CommandValidationException if there's something wrong
     *          with the options or arguments
     */
    protected void validate() throws CommandException {
    }

    /**
     * Execute the command using the options in options and the
     * operands in operands.
     *
     * @return the exit code
     * @throws CommandException if execution of the command fails
     * @throws CommandValidationException if there's something wrong
     *          with the options or arguments
     */
    protected abstract int executeCommand() throws CommandException;

    /**
     * Initialize all the passwords required by the command.
     *
     * @throws CommandException
     */
    private void initializeCommandPassword() throws CommandException {
        /*
         * Go through all the valid options and check for required password
         * options that weren't specified in the password file.  If option
         * is missing and we're interactive, prompt for it.  Store the
         * password as if it was a parameter.
         */
        for (ParamModel opt : commandModel.getParameters()) {
            if (!opt.getParam().password())
                continue;
            String pwdname = opt.getName();
            String pwd = getPassword(opt, null, true);
            if (pwd == null) {
                if (opt.getParam().optional())
                    continue;       // not required, skip it
                // if not terse, provide more advice about what to do
                String msg;
                if (programOpts.isTerse())
                    msg = strings.get("missingPassword", name, pwdname);
                else
                    msg = strings.get("missingPasswordAdvice", name, pwdname);
                throw new CommandValidationException(msg);
            }
            options.set(pwdname, pwd);
        }
    }

    /**
     * Get a password for the given option.
     * First, look in the passwords map.  If found, return it.
     * If not found, and not required, return null;
     * If not interactive, return null.  Otherwise, prompt for the
     * password.  If create is true, prompt twice and compare the two values
     * to make sure they're the same.  If the password meets other validity
     * criteria (i.e., length) returns the password.  If defaultPassword is
     * not null, "Enter" selects this default password, which is returned.
     */
    protected String getPassword(ParamModel opt, String defaultPassword,
            boolean create) throws CommandValidationException {

        String passwordName = opt.getName();
        String password = passwords.get(passwordName);
        if (password != null)
            return password;

        if (opt.getParam().optional())
            return null;        // not required

        if (!programOpts.isInteractive())
            return null;        // can't prompt for it

        String description = null;
        if (opt instanceof ParamModelData)
            description = ((ParamModelData)opt).getDescription();
        String newprompt;
        if (ok(description)) {
            if (defaultPassword != null) {
                if (defaultPassword.length() == 0)
                    newprompt =
                        strings.get("NewPasswordDescriptionDefaultEmptyPrompt",
                                            description);
                else
                    newprompt =
                        strings.get("NewPasswordDescriptionDefaultPrompt",
                                            description, defaultPassword);
            } else
                newprompt =
                    strings.get("NewPasswordDescriptionPrompt", description);
        } else {
            if (defaultPassword != null) {
                if (defaultPassword.length() == 0)
                    newprompt =
                        strings.get("NewPasswordDefaultEmptyPrompt",
                                            passwordName);
                else
                    newprompt =
                        strings.get("NewPasswordDefaultPrompt",
                                            passwordName, defaultPassword);
            } else
                newprompt = strings.get("NewPasswordPrompt", passwordName);
        }

        String newpassword = readPassword(newprompt);

        /*
         * If we allow for a default password, and the user just hit "Enter",
         * return the default password.  No need to prompt twice or check
         * for validity.
         */
        if (defaultPassword != null) {
            if (newpassword == null)
                newpassword = "";
            if (newpassword.length() == 0) {
                newpassword = defaultPassword;
                passwords.put(passwordName, newpassword);
                return newpassword;
            }
        }

        /*
         * If not creating a new password, don't need to verify that
         * the user typed it correctly by making them type it twice,
         * and don't need to check it for validity.  Just return what
         * we have.
         */
        if (!create) {
            passwords.put(passwordName, newpassword);
            return newpassword;
        }

        String confirmationPrompt;
        if (ok(description)) {
            confirmationPrompt =
                strings.get("NewPasswordDescriptionConfirmationPrompt",
                            description);
        } else {
            confirmationPrompt =
                strings.get("NewPasswordConfirmationPrompt", passwordName);
        }
        String newpasswordAgain = readPassword(confirmationPrompt);
        if (!newpassword.equals(newpasswordAgain)) {
            throw new CommandValidationException(
                strings.get("OptionsDoNotMatch",
                            ok(description) ? description : passwordName));
        }
        passwords.put(passwordName, newpassword);
        return newpassword;
    }

    /**
     * Display the given prompt and read a password without echoing it.
     * Returns null if no console available.
     */
    protected String readPassword(String prompt) {
        String password = null;
        Console cons = System.console();
        if (cons != null) {
            char[] pc = cons.readPassword("%s", prompt);
            // yes, yes, yes, it would be safer to not keep it in a String
            password = new String(pc);
        }
        return password;
    }

    /**
     * Get the ParamModel that corresponds to the operand
     * (primary parameter).  Return null if none.
     */
    protected ParamModel getOperandModel() {
        for (ParamModel pm : commandModel.getParameters()) {
            if (pm.getParam().primary())
                return pm;
        }
        return null;
    }

    /**
     * Get an option value, that might come from the command line
     * or from the environment.  Return the default value for the
     * option if not otherwise specified.
     */
    protected String getOption(String name) {
        String val = options.getOne(name);
        if (val == null)
            val = env.getStringOption(name);
        if (val == null) {
            // no value, find the default
            ParamModel opt = commandModel.getModelFor(name);
            // if no value was specified and there's a default value, return it
            if (opt != null) {
                String def = opt.getParam().defaultValue();
                if (ok(def))
                    val = def;
            }
        }
        return val;
    }

    /**
     * Get a boolean option value, that might come from the command line
     * or from the environment.
     */
    protected boolean getBooleanOption(String name) {
        String val = getOption(name);
        return val != null && Boolean.parseBoolean(val);
    }

    /**
     * Return the named system property, or property
     * set in asenv.conf.
     */
    protected String getSystemProperty(String name) {
        return systemProps.get(name);
    }

    /**
     * Return all the system properties and properties set
     * in asenv.conf.  The returned Map may not be modified.
     */
    protected Map<String,String> getSystemProperties() {
        return systemProps;
    }

    /**
     * If this is an unsupported command, throw an exception.
     */
    private static void checkUnsupportedLegacyCommand(String cmd)
            throws CommandException {
        for (String c : unsupported) {
            if (c.equals(cmd)) {
                throw new CommandException(
                            strings.get("UnsupportedLegacyCommand", cmd));
            }
        }
        // it is a supported command; do nothing
    }

    /**
     * Prints the exception message with level as FINER.
     *
     * @param e the exception object to print
     */
    protected void printExceptionStackTrace(java.lang.Throwable e) {
        if (!logger.isLoggable(Level.FINER))
            return;
	/*
	java.lang.StackTraceElement[] ste = e.getStackTrace();
	for (int ii = 0; ii < ste.length; ii++)
	    printDebugMessage(ste[ii].toString());
	*/
    	final ByteArrayOutputStream output = new ByteArrayOutputStream(512);
    	e.printStackTrace(new java.io.PrintStream(output));
        try {
            output.close();
        } catch (IOException ex) {
            // ignore
        }
        logger.finer(output.toString());
    }

    protected static boolean ok(String s) {
        return s != null && s.length() > 0;
    }

    // shorthand for this too-verbose operation
    private static String lc(String s) {
    	return s.toLowerCase(Locale.ENGLISH);
    }

    /**
     * Read the named resource file and add the first token on each line
     * to the set.  Skip comment lines.
     */
    private static void file2Set(String file, Set<String> set) {
        BufferedReader reader = null;
        try {
            InputStream is = CLICommand.class.getClassLoader().
                                getResourceAsStream(file);
            if (is == null)
                return;     // in case the resource doesn't exist
            reader = new BufferedReader(new InputStreamReader(is));
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("#"))
                    continue; // # indicates comment
                StringTokenizer tok = new StringTokenizer(line, " ");
                // handles with or without space, rudimendary as of now
                String cmd = tok.nextToken();
                set.add(cmd);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ee) {
                    // ignore
                }

            }
        }
    }
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish CLICommand.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.