Package com.sun.enterprise.v3.admin

Source Code of com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2008-2013 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.v3.admin;

import com.sun.enterprise.admin.util.*;
import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.universal.collections.ManifestUtils;
import com.sun.enterprise.universal.glassfish.AdminCommandResponse;
import com.sun.enterprise.util.AnnotationUtil;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.StringUtils;
import com.sun.enterprise.v3.common.XMLContentActionReporter;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Scope;
import javax.inject.Singleton;
import javax.security.auth.Subject;
import javax.validation.*;
import org.glassfish.admin.payload.PayloadFilesManager;
import org.glassfish.api.ActionReport;
import org.glassfish.api.Param;
import org.glassfish.api.admin.*;
import org.glassfish.api.admin.AdminCommandEventBroker.AdminCommandListener;
import org.glassfish.api.admin.Payload;
import org.glassfish.api.admin.ProcessEnvironment;
import org.glassfish.api.admin.SupplementalCommandExecutor.SupplementalCommand;
import org.glassfish.api.logging.LogHelper;
import org.glassfish.common.util.admin.CommandModelImpl;
import org.glassfish.common.util.admin.ManPageFinder;
import org.glassfish.common.util.admin.MapInjectionResolver;
import org.glassfish.common.util.admin.UnacceptableValueException;
import org.glassfish.config.support.CommandTarget;
import org.glassfish.config.support.GenericCrudCommand;
import org.glassfish.config.support.TargetType;
import org.glassfish.hk2.api.MultiException;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.api.*;
import org.glassfish.internal.deployment.DeploymentTargetResolver;
import org.glassfish.kernel.KernelLoggerInfo;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.component.*;
import org.jvnet.hk2.config.InjectionManager;
import org.jvnet.hk2.config.InjectionResolver;
import org.jvnet.hk2.config.MessageInterpolatorImpl;
import org.jvnet.hk2.config.UnsatisfiedDependencyException;

/**
* Encapsulates the logic needed to execute a server-side command (for example,
* a descendant of AdminCommand) including injection of argument values into the
* command.
*
* @author dochez
* @author tjquinn
* @author Bill Shannon
*/
@Service
public class CommandRunnerImpl implements CommandRunner {

    private static final Logger logger = KernelLoggerInfo.getLogger();
    // This is used only for backword compatibility with old behavior
    private static final String OLD_PASSWORD_PARAM_PREFIX = "AS_ADMIN_";

    private static final InjectionManager injectionMgr = new InjectionManager();
    @Inject
    private ServiceLocator habitat;
    @Inject
    private ServerContext sc;
    @Inject
    private Domain domain;
    @Inject
    private ServerEnvironment serverEnv;
    @Inject
    private ProcessEnvironment processEnv;
    @Inject
    private InstanceStateService state;
    @Inject
    private AdminCommandLock adminLock;
    @Inject @Named("SupplementalCommandExecutorImpl")
    SupplementalCommandExecutor supplementalExecutor;


    //private final Map<Class<? extends AdminCommand>, String> commandModelEtagMap = new WeakHashMap<Class<? extends AdminCommand>, String>();
    private final Map<NameCommandClassPair, String> commandModelEtagMap = new IdentityHashMap<NameCommandClassPair, String>();

    @Inject
    private CommandSecurityChecker commandSecurityChecker;
   
    private static final LocalStringManagerImpl adminStrings =
            new LocalStringManagerImpl(CommandRunnerImpl.class);
    private static volatile Validator beanValidator = null;

    /**
     * Returns an initialized ActionReport instance for the passed type or
     * null if it cannot be found.
     *
     * @param name action report type name
     * @return uninitialized action report or null
     */
    @Override
    public ActionReport getActionReport(String name) {
        return habitat.getService(ActionReport.class, name);
    }

    /**
     * Returns the command model for a command name.
     *
     * @param commandName command name
     * @param logger logger to log any error messages
     * @return model for this command (list of parameters,etc...),
     *          or null if command is not found
     */
    @Override
    public CommandModel getModel(String commandName, Logger logger) {
        return getModel(null, commandName, logger);
    }
   
    /**
     * Returns the command model for a command name.
     *
     * @param commandName command name
     * @param logger logger to log any error messages
     * @return model for this command (list of parameters,etc...),
     *          or null if command is not found
     */
    @Override
    public CommandModel getModel(String scope, String commandName, Logger logger) {
        AdminCommand command;
        try {
            String commandServiceName = (scope != null) ? scope + commandName : commandName;
            command = habitat.getService(AdminCommand.class, commandServiceName);
        } catch (MultiException e) {
            LogHelper.log(logger, Level.SEVERE, KernelLoggerInfo.cantInstantiateCommand,
                    e, commandName);
            return null;
        }
        return command == null ? null : getModel(command);
    }
   
    @Override
    public boolean validateCommandModelETag(AdminCommand command, String eTag) {
        if (command == null) {
            return true; //Everithing is ok for unexisting command
        }
        if (eTag == null || eTag.isEmpty()) {
            return false;
        }
        CommandModel model = getModel(command);
        return validateCommandModelETag(model, eTag);
    }
   
    @Override
    public boolean validateCommandModelETag(CommandModel model, String eTag) {
        if (model == null) {
            return true; //Unexisting model => it is ok (but weard in fact)
        }
        if (eTag == null || eTag.isEmpty()) {
            return false;
        }
        String actualETag = CachedCommandModel.computeETag(model);
        return eTag.equals(actualETag);
    }

    /**
     * Obtain and return the command implementation defined by
     * the passed commandName for the null scope.
     *
     * @param commandName command name as typed by users
     * @param report report used to communicate command status back to the user
     * @param logger logger to log
     * @return command registered under commandName or null if not found
     */
    @Override
    public AdminCommand getCommand(String commandName,
            ActionReport report, Logger logger) {
        return getCommand(null, commandName, report, logger);
    }
   
    private static Class<? extends Annotation> getScope(Class<?> onMe) {
        for (Annotation anno : onMe.getAnnotations()) {
            if (anno.annotationType().isAnnotationPresent(Scope.class)) {
                return anno.annotationType();
            }
       
        }
       
        return null;
    }

    /**
     * Obtain and return the command implementation defined by
     * the passed commandName.
     *
     * @param commandName command name as typed by users
     * @param report report used to communicate command status back to the user
     * @param logger logger to log
     * @return command registered under commandName or null if not found
     */
    @Override
    public AdminCommand getCommand(String scope, String commandName,
            ActionReport report, Logger logger) {

        AdminCommand command = null;
        String commandServiceName = (scope != null) ? scope + commandName : commandName;
       
        try {
            command = habitat.getService(AdminCommand.class, commandServiceName);
        } catch (MultiException e) {
            report.setFailureCause(e);
        }
        if (command == null) {
            String msg;

            if (!ok(commandName)) {
                msg = adminStrings.getLocalString("adapter.command.nocommand",
                        "No command was specified.");
            } else {
                // this means either a non-existent command or
                // an ill-formed command
                if (habitat.getServiceHandle(AdminCommand.class, commandServiceName)
                        == null) // somehow it's in habitat
                {
                    msg = adminStrings.getLocalString("adapter.command.notfound", "Command {0} not found", commandName);
                } else {
                    msg = adminStrings.getLocalString("adapter.command.notcreated",
                            "Implementation for the command {0} exists in "
                            + "the system, but it has some errors, "
                            + "check server.log for details", commandName);
                }
            }
            report.setMessage(msg);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            KernelLoggerInfo.getLogger().fine(msg);
            return null;
        }

        Class<? extends Annotation> myScope = getScope(command.getClass());
        if (myScope == null) {
            String msg = adminStrings.getLocalString("adapter.command.noscope",
                    "Implementation for the command {0} exists in the "
                    + "system,\nbut it has no @Scoped annotation", commandName);
            report.setMessage(msg);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            KernelLoggerInfo.getLogger().fine(msg);
            command = null;
        } else if (Singleton.class.equals(myScope)) {
            // check that there are no parameters for this command
            CommandModel model = getModel(command);
            if (model.getParameters().size() > 0) {
                String msg =
                        adminStrings.getLocalString("adapter.command.hasparams",
                        "Implementation for the command {0} exists in the "
                        + "system,\nbut it's a singleton that also has "
                        + "parameters", commandName);
                report.setMessage(msg);
                report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                KernelLoggerInfo.getLogger().fine(msg);
                command = null;
            }
        }

        return command;
    }

    /**
     * Obtain a new command invocation object for the null scope.
     * Command invocations can be configured and used
     * to trigger a command execution.
     *
     * @param name name of the requested command to invoke
     * @param report where to place the status of the command execution
     * @param subject the Subject under which to execute the command
     * @return a new command invocation for that command name
     */
    @Override
    public CommandInvocation getCommandInvocation(String name,
            ActionReport report, Subject subject) {
        return getCommandInvocation(null, name, report, subject);
    }

    /**
     * Obtain a new command invocation object.
     * Command invocations can be configured and used
     * to trigger a command execution.
     *
     * @param scope the scope (or name space) for the command
     * @param name name of the requested command to invoke
     * @param report where to place the status of the command execution
     * @param subject the Subject under which to execute the command
     * @return a new command invocation for that command name
     */
    @Override
    public CommandInvocation getCommandInvocation(String scope, String name,
            ActionReport report, Subject subject) {
        return new ExecutionContext(scope, name, report, subject);
    }

    public static boolean injectParameters(final CommandModel model, final Object injectionTarget,
            final InjectionResolver<Param> injector,
            final ActionReport report) {

        if (injectionTarget instanceof GenericCrudCommand) {
            GenericCrudCommand c = GenericCrudCommand.class.cast(injectionTarget);
            c.setInjectionResolver(injector);
        }

        // inject
        try {
            injectionMgr.inject(injectionTarget, injector);
        } catch (UnsatisfiedDependencyException e) {
            Param param = e.getAnnotation(Param.class);
            CommandModel.ParamModel paramModel = null;
            for (CommandModel.ParamModel pModel : model.getParameters()) {
                if (pModel.getParam().equals(param)) {
                    paramModel = pModel;
                    break;
                }
            }
            String errorMsg;
            final String usage = getUsageText(model);
            if (paramModel != null) {
                String paramName = paramModel.getName();
                String paramDesc = paramModel.getLocalizedDescription();

                if (param.primary()) {
                    errorMsg = adminStrings.getLocalString("commandrunner.operand.required",
                            "Operand required.");
                } else if (param.password()) {
                    errorMsg = adminStrings.getLocalString("adapter.param.missing.passwordfile",
                            "{0} command requires the passwordfile "
                            + "parameter containing {1} entry.",
                            model.getCommandName(), paramName);
                } else if (paramDesc != null) {
                    errorMsg = adminStrings.getLocalString("admin.param.missing",
                            "{0} command requires the {1} parameter ({2})",
                            model.getCommandName(), paramName, paramDesc);

                } else {
                    errorMsg = adminStrings.getLocalString("admin.param.missing.nodesc",
                            "{0} command requires the {1} parameter",
                            model.getCommandName(), paramName);
                }
            } else {
                errorMsg = adminStrings.getLocalString("admin.param.missing.nofound",
                        "Cannot find {1} in {0} command model, file a bug",
                        model.getCommandName(), e.getUnsatisfiedName());
            }
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setMessage(errorMsg);
            report.setFailureCause(e);
            ActionReport.MessagePart childPart =
                    report.getTopMessagePart().addChild();
            childPart.setMessage(usage);
            return false;
        }
        catch (MultiException e) {
            // If the cause is UnacceptableValueException -- we want the message
            // from it.  It is wrapped with a less useful Exception.
           
            Exception exception = null;
            for (Throwable th : e.getErrors()) {
                Throwable cause = th;
                while (cause != null) {
                    if ((cause instanceof UnacceptableValueException) ||
                            (cause instanceof IllegalArgumentException)) {
                        exception = (Exception) th;
                        break;
                    }
                   
                    cause = cause.getCause();
                }
            }
           
            if (exception == null) {
                // Not an UnacceptableValueException or IllegalArgumentException
                exception = e;
            }

            logger.log(Level.SEVERE, KernelLoggerInfo.invocationException, exception);
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setMessage(exception.getMessage());
            report.setFailureCause(exception);
            ActionReport.MessagePart childPart =
                    report.getTopMessagePart().addChild();
            childPart.setMessage(getUsageText(model));
            return false;
        }
       
        checkAgainstBeanConstraints(injectionTarget, model.getCommandName());
        return true;
    }

    private static synchronized void initBeanValidator() {
        if (beanValidator != null) {
            return;
        }
        ClassLoader cl = System.getSecurityManager() == null ?
                Thread.currentThread().getContextClassLoader():
                AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                    @Override
                    public ClassLoader run() {
                        return Thread.currentThread().getContextClassLoader();
                    }
                });
        try {
            Thread.currentThread().setContextClassLoader(Validation.class.getClassLoader());
            ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
            ValidatorContext validatorContext = validatorFactory.usingContext();
            validatorContext.messageInterpolator(new MessageInterpolatorImpl());               
            beanValidator = validatorContext.getValidator();
        } finally {
            Thread.currentThread().setContextClassLoader(cl);
        }      
    }
   
    private static void checkAgainstBeanConstraints(Object component, String cname) {
        initBeanValidator();
               
        Set<ConstraintViolation<Object>> constraintViolations = beanValidator.validate(component);
        if (constraintViolations == null || constraintViolations.isEmpty()) {
            return;
        }
        StringBuilder msg = new StringBuilder(adminStrings.getLocalString("commandrunner.unacceptableBV",
                "Parameters for command {0} violate the following constraints: ",
                cname));
        boolean addc = false;
        String violationMsg = adminStrings.getLocalString("commandrunner.unacceptableBV.reason",
                "on parameter [ {1} ] violation reason [ {0} ]");
        for (ConstraintViolation cv : constraintViolations) {
            if (addc) {
                msg.append(", ");
            }
            msg.append(MessageFormat.format(violationMsg, cv.getMessage(), cv.getPropertyPath()));
            addc = true;
        }
        throw new UnacceptableValueException(msg.toString());
    }
   
    /**
     * Executes the provided command object.
     *
     * @param model model of the command (used for logging and reporting)
     * @param command the command service to execute
     * @param context the AdminCommandcontext that has the payload and report
     */
    private ActionReport doCommand(
            final CommandModel model,
            final AdminCommand command,
            final AdminCommandContext context,
            final CommandRunnerProgressHelper progressHelper) {

        ActionReport report = context.getActionReport();
        report.setActionDescription(model.getCommandName() + " AdminCommand");

        // We need to set context CL to common CL before executing
        // the command. See issue #5596
        final Thread thread = Thread.currentThread();
        final ClassLoader origCL = thread.getContextClassLoader();
        final ClassLoader ccl = sc.getCommonClassLoader();
           
        AdminCommand wrappedCommand = new WrappedAdminCommand(command) {
            @Override
            public void execute(final AdminCommandContext context) {
                try {
                    if (origCL != ccl) {
                        thread.setContextClassLoader(ccl);
                    }
                    /*
                     * Execute the command in the security context of the
                     * previously-authenticated subject.
                     */
                    Subject.doAs(context.getSubject(),
                            new PrivilegedAction<Void> () {

                        @Override
                        public Void run() {
                            command.execute(context);
                            return null;
                        }

                    });
                } finally {
                    if (origCL != ccl) {
                        thread.setContextClassLoader(origCL);
                    }
                }
            }
        };

        // look for other wrappers using CommandAspect annotation
        final AdminCommand otherWrappedCommand = CommandSupport.createWrappers(habitat, model, wrappedCommand, report);
           
        try {
            Subject.doAs(context.getSubject(),
                            new PrivilegedAction<Void> () {

                        @Override
                        public Void run() {
                            try {
                                if (origCL != ccl) {
                                    thread.setContextClassLoader(ccl);
                                }
                                otherWrappedCommand.execute(progressHelper.wrapContext4MainCommand(context));
                                return null;
                            } finally {
                                if (origCL != ccl) {
                                    thread.setContextClassLoader(origCL);
                                }
                            }
                        }

                    });
           
        } catch (Throwable e) {
            logger.log(Level.SEVERE, KernelLoggerInfo.invocationException, e);
            report.setMessage(e.toString());
            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
            report.setFailureCause(e);
            }
       
        return context.getActionReport();
    }

    /**
     * Get the usage-text of the command.
     * Check if <command-name>.usagetext is defined in LocalString.properties.
     * If defined, then use the usagetext from LocalString.properties else
     * generate the usagetext from Param annotations in the command class.
     *
     * @param model command model
     * @return usagetext
     */
    static String getUsageText(CommandModel model) {
        StringBuilder usageText = new StringBuilder();

        String usage;
        if (ok(usage = model.getUsageText())) {
            usageText.append(
                    adminStrings.getLocalString("adapter.usage", "Usage: "));
            usageText.append(usage);
            return usageText.toString();
        } else {
            return generateUsageText(model);
        }
    }

    /**
     * Generate the usage-text from the annotated Param in the command class.
     *
     * @param model command model
     * @return generated usagetext
     */
    private static String generateUsageText(CommandModel model) {
        StringBuffer usageText = new StringBuffer();
        usageText.append(
                adminStrings.getLocalString("adapter.usage", "Usage: "));
        usageText.append(model.getCommandName());
        usageText.append(" ");
        StringBuffer operand = new StringBuffer();
        for (CommandModel.ParamModel pModel : model.getParameters()) {
            final Param param = pModel.getParam();
            final String paramName =
                    pModel.getName().toLowerCase(Locale.ENGLISH);
            // skip "hidden" options
            if (paramName.startsWith("_")) {
                continue;
            }
            // do not want to display password as an option
            if (param.password()) {
                continue;
            }
            // do not want to display obsolete options
            if (param.obsolete()) {
                continue;
            }
            final boolean optional = param.optional();
            final Class<?> ftype = pModel.getType();
            Object fvalue = null;
            String fvalueString = null;
            try {
                fvalue = param.defaultValue();
                if (fvalue != null) {
                    fvalueString = fvalue.toString();
                }
            } catch (Exception e) {
                // just leave it as null...
            }
            // this is a param.
            if (param.primary()) {
                if (optional) {
                    operand.append("[").append(paramName).append("] ");
                } else {
                    operand.append(paramName).append(" ");
                }
                continue;
            }

            if (optional) {
                usageText.append("[");
            }

            usageText.append("--").append(paramName);
            if (ok(param.defaultValue())) {
                usageText.append("=").append(param.defaultValue());
            } else if (ftype.isAssignableFrom(String.class)) {
                // check if there is a default value assigned
                if (ok(fvalueString)) {
                    usageText.append("=").append(fvalueString);
                } else {
                    usageText.append("=").append(paramName);
                }
            } else if (ftype.isAssignableFrom(Boolean.class)) {
                // note: There is no defaultValue for this param.  It might
                // hava  value -- but we don't care -- it isn't an official
                // default value.
                usageText.append("=").append("true|false");
            } else {
                usageText.append("=").append(paramName);
            }

            if (optional) {
                usageText.append("] ");
            } else {
                usageText.append(" ");
            }
        }
        usageText.append(operand);
        return usageText.toString();
    }
   
    @Override
    public BufferedReader getHelp(CommandModel model) throws CommandNotFoundException {
        BufferedReader manPage = getManPage(model.getCommandName(), model);
        if (manPage != null) {
            return manPage;
        } else {
            StringBuilder hlp = new StringBuilder(256);
            StringBuilder part = new StringBuilder(64);
            hlp.append("NAME").append(ManifestUtils.EOL);
            part.append(model.getCommandName());
            String description = model.getLocalizedDescription();
            if (ok(description)) {
                part.append(" - ").append(model.getLocalizedDescription());
            }
            hlp.append(formatGeneratedManPagePart(part.toString(), 5, 65)).append(ManifestUtils.EOL);
            //Usage
            hlp.append(ManifestUtils.EOL).append("SYNOPSIS").append(ManifestUtils.EOL);
            hlp.append(formatGeneratedManPagePart(getUsageText(model), 5, 65));
            //Options
            hlp.append(ManifestUtils.EOL).append(ManifestUtils.EOL);
            hlp.append("OPTIONS").append(ManifestUtils.EOL);
            CommandModel.ParamModel operand = null;
            for (CommandModel.ParamModel paramModel : model.getParameters()) {
                Param param = paramModel.getParam();
                if (param == null || paramModel.getName().startsWith("_") ||
                        param.password() || param.obsolete()) {
                    continue;
                }
                if (param.primary()) {
                    operand = paramModel;
                    continue;
                }
                hlp.append("     --").append(paramModel.getName().toLowerCase(Locale.ENGLISH));
                hlp.append(ManifestUtils.EOL);
                if (ok(param.shortName())) {
                    hlp.append("      -").append(param.shortName().toLowerCase(Locale.ENGLISH));
                    hlp.append(ManifestUtils.EOL);
                }
                String descr = paramModel.getLocalizedDescription();
                if (ok(descr)) {
                    hlp.append(formatGeneratedManPagePart(descr, 9, 65));
                }
                hlp.append(ManifestUtils.EOL);
            }
            //Operand
            if (operand != null) {
                hlp.append("OPERANDS").append(ManifestUtils.EOL);
                hlp.append("     ").append(operand.getName().toLowerCase(Locale.ENGLISH));
                hlp.append(ManifestUtils.EOL);
                String descr = operand.getLocalizedDescription();
                if (ok(descr)) {
                    hlp.append(formatGeneratedManPagePart(descr, 9, 65));
                }
            }
            return new BufferedReader(new StringReader(hlp.toString()));
        }
    }
   
    private String formatGeneratedManPagePart(String part, int prefix, int lineLength) {
        if (part == null) {
            return null;
        }
        if (prefix < 0) {
            prefix = 0;
        }
        //Prepare prefix
        StringBuilder sb = new StringBuilder(prefix);
        for (int i = 0; i < prefix; i++) {
            sb.append(' ');
        }
        String prfx = sb.toString();
        StringBuilder result = new StringBuilder(part.length() + prefix + 16);
        boolean newLine = true;
        boolean lastWasCR = false;
        int counter = 0;
        for (int i = 0; i < part.length(); i++) {
            boolean addPrefix = newLine;
            char ch = part.charAt(i);
            switch (ch) {
                case '\n':
                    if (!lastWasCR) {
                        newLine = true;
                    } else {
                        lastWasCR = false;
                    }
                    counter = 0;
                    break;
                case '\r':
                    newLine = true;
                    lastWasCR = true;
                    counter = 0;
                    break;
                default:
                    newLine = false;
                    lastWasCR = false;
                    counter++;
            }
            if (addPrefix && !newLine) {
                result.append(prfx);
                counter += prefix;
            }
            result.append(ch);
            if (lineLength > 0 && counter >= lineLength && !newLine) {
                newLine = true;
                result.append(ManifestUtils.EOL);
                counter = 0;
            }
        }
        return result.toString();
    }

    public void getHelp(AdminCommand command, ActionReport report) {

        CommandModel model = getModel(command);
        report.setActionDescription(model.getCommandName() + " help");

        // XXX - this is a hack for now.  if the request mapped to an
        // XMLContentActionReporter, that means we want the command metadata.
        if (report instanceof XMLContentActionReporter) {
            getMetadata(command, model, report);
        } else {
            report.setMessage(model.getCommandName() + " - "
                    + model.getLocalizedDescription());
            report.getTopMessagePart().addProperty("SYNOPSIS",
                    encodeManPage(new BufferedReader(new StringReader(
                    getUsageText(model)))));
            for (CommandModel.ParamModel param : model.getParameters()) {
                addParamUsage(report, param);
            }
            report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
        }
    }

    /**
     * Return the metadata for the command.  We translate the parameter
     * and operand information to parts and properties of the ActionReport,
     * which will be translated to XML elements and attributes by the
     * XMLContentActionReporter.
     *
     * @param command the command
     * @param model the CommandModel describing the command
     * @param report  the (assumed to be) XMLContentActionReporter
     */
    private void getMetadata(AdminCommand command, CommandModel model,
            ActionReport report) {
        ActionReport.MessagePart top = report.getTopMessagePart();
        ActionReport.MessagePart cmd = top.addChild();
        // <command name="name">
        cmd.setChildrenType("command");
        cmd.addProperty("name", model.getCommandName());
        if (model.unknownOptionsAreOperands()) {
            cmd.addProperty("unknown-options-are-operands", "true");
        }
        String usage = model.getUsageText();
        if (ok(usage)) {
            cmd.addProperty("usage", usage);
        }
        CommandModel.ParamModel primary = null;
        // for each parameter add
        // <option name="name" type="type" short="s" default="default"
        //   acceptable-values="list"/>
        for (CommandModel.ParamModel p : model.getParameters()) {
            Param param = p.getParam();
            if (param.primary()) {
                primary = p;
                continue;
            }
            ActionReport.MessagePart ppart = cmd.addChild();
            ppart.setChildrenType("option");
            ppart.addProperty("name", p.getName());
            ppart.addProperty("type", typeOf(p));
            ppart.addProperty("optional", Boolean.toString(param.optional()));
            if (param.obsolete()) // don't include it if it's false
            {
                ppart.addProperty("obsolete", "true");
            }
            String paramDesc = p.getLocalizedDescription();
            if (ok(paramDesc)) {
                ppart.addProperty("description", paramDesc);
            }
            if (ok(param.shortName())) {
                ppart.addProperty("short", param.shortName());
            }
            if (ok(param.defaultValue())) {
                ppart.addProperty("default", param.defaultValue());
            }
            if (ok(param.acceptableValues())) {
                ppart.addProperty("acceptable-values", param.acceptableValues());
            }
            if (ok(param.alias())) {
                ppart.addProperty("alias", param.alias());
            }
        }

        // are operands allowed?
        if (primary != null) {
            // for the operand(s), add
            // <operand type="type" min="0/1" max="1"/>
            ActionReport.MessagePart primpart = cmd.addChild();
            primpart.setChildrenType("operand");
            primpart.addProperty("name", primary.getName());
            primpart.addProperty("type", typeOf(primary));
            primpart.addProperty("min",
                    primary.getParam().optional() ? "0" : "1");
            primpart.addProperty("max", primary.getParam().multiple()
                    ? "unlimited" : "1");
            String desc = primary.getLocalizedDescription();
            if (ok(desc)) {
                primpart.addProperty("description", desc);
            }
        }
    }

    /**
     * Map a Java type to one of the types supported by the asadmin client.
     * Currently supported types are BOOLEAN, FILE, PROPERTIES, PASSWORD, and
     * STRING.  (All of which should be defined constants on some class.)
     *
     * @param p the Java type
     * @return  the string representation of the asadmin type
     */
    private static String typeOf(CommandModel.ParamModel p) {
        Class t = p.getType();
        if (t == Boolean.class || t == boolean.class) {
            return "BOOLEAN";
        } else if (t == File.class || t == File[].class) {
            return "FILE";
        } else if (t == Properties.class) { // XXX - allow subclass?
            return "PROPERTIES";
        } else if (p.getParam().password()) {
            return "PASSWORD";
        } else {
            return "STRING";
        }
    }

    /**
     * Return an InputStream for the man page for the named command.
     */
    public static BufferedReader getManPage(String commandName,
            CommandModel model) {
        Class clazz = model.getCommandClass();
        if (clazz == null) {
            return null;
        }
        return ManPageFinder.getCommandManPage(commandName, clazz.getName(),
                Locale.getDefault(), clazz.getClassLoader(), logger);
    }

    private void addParamUsage(
            ActionReport report,
            CommandModel.ParamModel model) {
        Param param = model.getParam();
        if (param != null) {
            // this is a param.
            String paramName = model.getName().toLowerCase(Locale.ENGLISH);
            // skip "hidden" options
            if (paramName.startsWith("_")) {
                return;
            }
            // do not want to display password in the usage
            if (param.password()) {
                return;
            }
            // do not want to display obsolete options
            if (param.obsolete()) {
                return;
            }
            if (param.primary()) {
                // if primary then it's an operand
                report.getTopMessagePart().addProperty(paramName + "_operand",
                        model.getLocalizedDescription());
            } else {
                report.getTopMessagePart().addProperty(paramName,
                        model.getLocalizedDescription());
            }
        }
    }

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

    /**
     * Validate the parameters with the Param annotation.  If parameter is
     * not defined as a Param annotation then it's an invalid option.
     * If parameter's key is "DEFAULT" then it's a operand.
     *
     * @param model command model
     * @param parameters parameters from URL
     *
     */
    static void validateParameters(final CommandModel model,
            final ParameterMap parameters) throws MultiException {

        ParameterMap adds = null; // renamed password parameters
       
        // loop through parameters and make sure they are
        // part of the Param declared field
        for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
            String key = entry.getKey();

            // to do, we should validate meta-options differently.
            if (key.equals("DEFAULT")) {
                continue;
            }

            // help and Xhelp are meta-options that are handled specially
            if (key.equals("help") || key.equals("Xhelp")) {
                continue;
            }
           
            if (key.startsWith(OLD_PASSWORD_PARAM_PREFIX)) {
                // This is an old prefixed password parameter being passed in.
                // Strip the prefix and lowercase the name
                key = key.substring(OLD_PASSWORD_PARAM_PREFIX.length()).toLowerCase(Locale.ENGLISH);
                if (adds == null) adds = new ParameterMap();
                adds.add(key, entry.getValue().get(0));
            }

            // check if key is a valid Param Field
            boolean validOption = false;
            // loop through the Param field in the command class
            // if either field name or the param name is equal to
            // key then it's a valid option
            for (CommandModel.ParamModel pModel : model.getParameters()) {
                validOption = pModel.isParamId(key);
                if (validOption) {
                    break;
                }     
            }
           
            if (!validOption) {
                throw new MultiException(new IllegalArgumentException(" Invalid option: " + key));
            }
        }
        parameters.mergeAll(adds);
    }

    /**
     * Check if the variable, "skipParamValidation" is defined in the command
     * class.  If defined and set to true, then parameter validation will be
     * skipped from that command.
     * This is used mostly for command referencing.  For example the
     * list-applications command references list-components command and you
     * don't want to define the same params from the class that implements
     * list-components.
     *
     * @param command - AdminCommand class
     * @return true if to skip param validation, else return false.
     */
    static boolean skipValidation(AdminCommand command) {
        try {
            final Field f =
                    command.getClass().getDeclaredField("skipParamValidation");
            AccessController.doPrivileged(new PrivilegedAction<Object>() {

                @Override
                public Object run() {
                    f.setAccessible(true);
                    return null;
                }
            });
            if (f.getType().isAssignableFrom(boolean.class)) {
                return f.getBoolean(command);
            }
        } catch (NoSuchFieldException e) {
            return false;
        } catch (IllegalAccessException e) {
            return false;
        }
        //all else return false
        return false;
    }

    private static String encodeManPage(BufferedReader br) {
        if (br == null) {
            return null;
        }

        try {
            String line;
            StringBuilder sb = new StringBuilder();

            while ((line = br.readLine()) != null) {
                sb.append(line);
                sb.append(ManifestUtils.EOL_TOKEN);
            }
            return sb.toString();
        } catch (Exception ex) {
            return null;
        } finally {
            try {
                br.close();
            } catch (IOException ioex) {
            }
        }
    }

    private static CommandModel getModel(AdminCommand command) {

        if (command instanceof CommandModelProvider) {
            return ((CommandModelProvider) command).getModel();
        } else {
            return new CommandModelImpl(command.getClass());
        }
    }

    /**
     * Called from ExecutionContext.execute.
     */
    private void doCommand(ExecutionContext inv, AdminCommand command,
            final Subject subject, final Job commandInstance) {

        CommandModel model;
        try {
            CommandModelProvider c = CommandModelProvider.class.cast(command);
            model = c.getModel();
        } catch (ClassCastException e) {
            model = new CommandModelImpl(command.getClass());
        }
        UploadedFilesManager ufm = null;
        ActionReport report = inv.report();
        report.setActionDescription(model.getCommandName() + " command");
        report.setActionExitCode(ActionReport.ExitCode.SUCCESS);
        ParameterMap parameters;
        final AdminCommandContext context = new AdminCommandContextImpl(
                logger, report, inv.inboundPayload(), inv.outboundPayload(),
                commandInstance.getEventBroker(),
                commandInstance.getId());
        context.setSubject(subject);
        List<RuntimeType> runtimeTypes = new ArrayList<RuntimeType>();
        FailurePolicy fp = null;
        Set<CommandTarget> targetTypesAllowed = new HashSet<CommandTarget>();
        ActionReport.ExitCode preSupplementalReturn = ActionReport.ExitCode.SUCCESS;
        ActionReport.ExitCode postSupplementalReturn = ActionReport.ExitCode.SUCCESS;
        CommandRunnerProgressHelper progressHelper =
                new CommandRunnerProgressHelper(command, model.getCommandName(), commandInstance, inv.progressStatusChild);              

        // If this glassfish installation does not have stand alone instances / clusters at all, then
        // lets not even look Supplemental command and such. A small optimization
        boolean doReplication = false;
        if ((domain.getServers().getServer().size() > 1) || (!domain.getClusters().getCluster().isEmpty())) {
            doReplication = true;
        } else {
            logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.devmode",
                    "The GlassFish environment does not have any clusters or instances present; Replication is turned off"));
        }
        try {
            //Get list of suplemental commands
            Collection<SupplementalCommand> suplementalCommands =
                    supplementalExecutor.listSuplementalCommands(model.getCommandName());
            try {
                /*
                 * Extract any uploaded files and build a map from parameter names
                 * to the corresponding extracted, uploaded file.
                 */
                ufm = new UploadedFilesManager(inv.report, logger,
                        inv.inboundPayload());

                if (inv.typedParams() != null) {
                    logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.delegatedcommand",
                            "This command is a delegated command. Dynamic reconfiguration will be bypassed"));
                    InjectionResolver<Param> injectionTarget =
                            new DelegatedInjectionResolver(model, inv.typedParams(),
                            ufm.optionNameToFileMap());
                    if (injectParameters(model, command, injectionTarget, report)) {
                        inv.setReport(doCommand(model, command, context, progressHelper));
                    }
                    return;
                }

                parameters = inv.parameters();
                if (parameters == null) {
                    // no parameters, pass an empty collection
                    parameters = new ParameterMap();
                }

                if (isSet(parameters, "help") || isSet(parameters, "Xhelp")) {
                    BufferedReader in = getManPage(model.getCommandName(), model);
                    String manPage = encodeManPage(in);

                    if (manPage != null && isSet(parameters, "help")) {
                        inv.report().getTopMessagePart().addProperty("MANPAGE", manPage);
                    } else {
                        report.getTopMessagePart().addProperty(
                                AdminCommandResponse.GENERATED_HELP, "true");
                        getHelp(command, report);
                    }
                    return;
                }

                try {
                    if (!skipValidation(command)) {
                        validateParameters(model, parameters);
                    }
                } catch (MultiException e) {
                    // If the cause is UnacceptableValueException -- we want the message
                    // from it.  It is wrapped with a less useful Exception.

                    Exception exception = e;
                    for (Throwable cause : e.getErrors()) {
                        if (cause != null
                                && (cause instanceof UnacceptableValueException)) {
                            // throw away the wrapper.
                            exception = (Exception) cause;
                            break;
                        }
                
                    }
                   
                    logger.log(Level.SEVERE, KernelLoggerInfo.invocationException, exception);
                    report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                    report.setMessage(exception.getMessage());
                    report.setFailureCause(exception);
                    ActionReport.MessagePart childPart =
                            report.getTopMessagePart().addChild();
                    childPart.setMessage(getUsageText(model));
                    return;
                }

                // initialize the injector and inject
                MapInjectionResolver injectionMgr =
                        new MapInjectionResolver(model, parameters,
                        ufm.optionNameToFileMap());
                injectionMgr.setContext(context);
                if (!injectParameters(model, command, injectionMgr, report)) {
                    return;
                }

                CommandSupport.init(habitat, command, context, commandInstance);

                /*
                 * Now that parameters have been injected into the command object,
                 * decide if the current Subject should be permitted to execute
                 * the command.  We need to wait until after injection is done
                 * because the class might implement its own authorization check
                 * and that logic might need the injected values.
                 */
                final Map<String,Object> env = buildEnvMap(parameters);
                try {
                    if ( ! commandSecurityChecker.authorize(context.getSubject(), env, command, context)) {
                        /*
                         * If the command class tried to prepare itself but
                         * could not then the return is false and the command has
                         * set the action report accordingly.  Don't process
                         * the command further and leave the action report alone.
                         */
                        return;
                    }
                } catch (SecurityException ex) {
                    report.setFailureCause(ex);
                    report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                    report.setMessage(adminStrings.getLocalString("commandrunner.noauth",
                            "User is not authorized for this command"));
                    return;
                } catch (Exception ex) {
                    report.setFailureCause(ex);
                    report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                    report.setMessage(adminStrings.getLocalString("commandrunner.errAuth",
                            "Error during authorization"));
                    return;
                }
                   
               
                logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.injectiondone",
                        "Parameter mapping, validation, injection completed successfully; Starting paramater injection"));

                // Read cluster annotation attributes
                org.glassfish.api.admin.ExecuteOn clAnnotation = model.getClusteringAttributes();
                if (clAnnotation == null) {
                    runtimeTypes.add(RuntimeType.DAS);
                    runtimeTypes.add(RuntimeType.INSTANCE);
                    fp = FailurePolicy.Error;
                } else {
                    if (clAnnotation.value().length == 0) {
                        runtimeTypes.add(RuntimeType.DAS);
                        runtimeTypes.add(RuntimeType.INSTANCE);
                    } else {
                        runtimeTypes.addAll(Arrays.asList(clAnnotation.value()));
                    }
                    if (clAnnotation.ifFailure() == null) {
                        fp = FailurePolicy.Error;
                    } else {
                        fp = clAnnotation.ifFailure();
                    }
                }
                TargetType tgtTypeAnnotation = command.getClass().getAnnotation(TargetType.class);

                //@ExecuteOn(RuntimeType.SINGLE_INSTANCE) cannot be combined with
                //@TargetType since we do not want to replicate the command
                if (runtimeTypes.contains(RuntimeType.SINGLE_INSTANCE)) {
                   if (tgtTypeAnnotation != null) {

                       report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                       report.setMessage(adminStrings.getLocalString("commandrunner.executor.targettype.unallowed",
                               "Target type is not allowed on single instance command {0}  ,"
                                       , model.getCommandName()));
                       return;
                   }
                   //Do not replicate the command when there is
                   //@ExecuteOn(RuntimeType.SINGLE_INSTANCE)
                   doReplication = false;
                }

                String targetName = parameters.getOne("target");
                if (targetName == null || model.getModelFor("target").getParam().obsolete()) {
                    if (command instanceof DeploymentTargetResolver) {
                        targetName = ((DeploymentTargetResolver) command).getTarget(parameters);
                    } else {
                        targetName = "server";
                    }
                }

                logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.target",
                        "@ExecuteOn parsing and default settings done; Current target is {0}", targetName));

                if (serverEnv.isDas()) {

                    //Do not replicate this command if it has @ExecuteOn(RuntimeType.SINGLE_INSTANCE)
                    //and the user is authorized to execute on DAS
                    // TODO add authorization check
                    /*if (runtimeTypes.contains(RuntimeType.SINGLE_INSTANCE)) {
                        //If authorization fails
                        report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                        report.setMessage(adminStrings.getLocalString("commandrunner.executor.das.unallowed",
                                "Not authorized to execute command {0} on DAS"
                                        , model.getCommandName()));
                        progressHelper.complete(context);
                        return;
                    }*/

                    // Check if the command allows this target type; first read the annotation
                    //TODO : See is @TargetType can also be moved to the CommandModel

                    if (tgtTypeAnnotation != null) {
                        targetTypesAllowed.addAll(Arrays.asList(tgtTypeAnnotation.value()));
                    }
                    //If not @TargetType, default it
                    if (targetTypesAllowed.isEmpty()) {
                        targetTypesAllowed.add(CommandTarget.DAS);
                        targetTypesAllowed.add(CommandTarget.STANDALONE_INSTANCE);
                        targetTypesAllowed.add(CommandTarget.CLUSTER);
                        targetTypesAllowed.add(CommandTarget.CONFIG);
                    }

                    // If the target is "server" and the command is not marked for DAS,
                    // add DAS to RuntimeTypes; This is important because those class of CLIs that
                    // do not always have to be run on DAS followed by applicable instances
                    // will have @ExecuteOn(RuntimeType.INSTANCE) and they have to be run on DAS
                    // ONLY if the target is "server"
                    if (CommandTarget.DAS.isValid(habitat, targetName)
                            && !runtimeTypes.contains(RuntimeType.DAS)) {
                        runtimeTypes.add(RuntimeType.DAS);
                    }

                    logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.runtimeTypes",
                            "RuntimeTypes are: {0}", runtimeTypes.toString()));
                    logger.fine(adminStrings.getLocalString("dynamicreconfiguration,diagnostics.targetTypes",
                            "TargetTypes are: {0}", targetTypesAllowed.toString()));

                    // Check if the target is valid
                    //Is there a server or a cluster or a config with given name ?
                    if ((!CommandTarget.DOMAIN.isValid(habitat, targetName))
                            && (domain.getServerNamed(targetName) == null)
                            && (domain.getClusterNamed(targetName) == null)
                            && (domain.getConfigNamed(targetName) == null)) {
                        report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                        report.setMessage(adminStrings.getLocalString("commandrunner.executor.invalidtarget",
                                "Unable to find a valid target with name {0}", targetName));
                        return;
                    }
                    //Does this command allow this target type
                    boolean isTargetValidType = false;
                    Iterator<CommandTarget> it = targetTypesAllowed.iterator();
                    while (it.hasNext()) {
                        if (it.next().isValid(habitat, targetName)) {
                            isTargetValidType = true;
                            break;
                        }
                    }
                    if (!isTargetValidType) {
                        StringBuilder validTypes = new StringBuilder();
                        it = targetTypesAllowed.iterator();
                        while (it.hasNext()) {
                            validTypes.append(it.next().getDescription()).append(", ");
                        }
                        report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                        report.setMessage(adminStrings.getLocalString("commandrunner.executor.invalidtargettype",
                                "Target {0} is not a supported type. Command {1} supports these types of targets only : {2}",
                                targetName, model.getCommandName(), validTypes.toString()));
                        return;
                    }
                    //If target is a clustered instance and the allowed types does not allow operations on clustered
                    //instance, return error
                    if ((CommandTarget.CLUSTERED_INSTANCE.isValid(habitat, targetName))
                            && (!targetTypesAllowed.contains(CommandTarget.CLUSTERED_INSTANCE))) {
                        Cluster c = domain.getClusterForInstance(targetName);
                        report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                        report.setMessage(adminStrings.getLocalString("commandrunner.executor.instanceopnotallowed",
                                "The {0} command is not allowed on instance {1} because it is part of cluster {2}",
                                model.getCommandName(), targetName, c.getName()));
                        return;
                    }
                    logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.replicationvalidationdone",
                            "All @ExecuteOn attribute and type validation completed successfully. Starting replication stages"));
                }
               
                /**
                 * We're finally ready to actually execute the command instance.
                 * Acquire the appropriate lock.
                 */
                Lock lock = null;
                boolean lockTimedOut = false;
                try {
                    // XXX: The owner of the lock should not be hardcoded.  The
                    //      value is not used yet.
                    lock = adminLock.getLock(command, "asadmin");
                   
                    //Set there progress statuses
                    for (SupplementalCommand supplementalCommand : suplementalCommands) {
                        progressHelper.addProgressStatusToSupplementalCommand(supplementalCommand);
                    }

                    // If command is undoable, then invoke prepare method
                    if (command instanceof UndoableCommand) {
                        UndoableCommand uCmd = (UndoableCommand) command;
                        logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.prepareunodable",
                                "Command execution stage 1 : Calling prepare for undoable command {0}", inv.name()));
                        if (!uCmd.prepare(context, parameters).equals(ActionReport.ExitCode.SUCCESS)) {
                            report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                            report.setMessage(adminStrings.getLocalString("commandrunner.executor.errorinprepare",
                                    "The command {0} cannot be completed because the preparation for the command failed "
                                    + "indicating potential issues : {1}", model.getCommandName(), report.getMessage()));
                            return;
                        }
                    }

                    ClusterOperationUtil.clearInstanceList();

                    // Run Supplemental commands that have to run before this command on this instance type
                    logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.presupplemental",
                            "Command execution stage 2 : Call pre supplemental commands for {0}", inv.name()));
                    preSupplementalReturn = supplementalExecutor.execute(suplementalCommands,
                            Supplemental.Timing.Before, context, parameters, ufm.optionNameToFileMap());
                    if (preSupplementalReturn.equals(ActionReport.ExitCode.FAILURE)) {
                        report.setActionExitCode(preSupplementalReturn);
                        if (!StringUtils.ok(report.getTopMessagePart().getMessage())) {
                            report.setMessage(adminStrings.getLocalString("commandrunner.executor.supplementalcmdfailed",
                                "A supplemental command failed; cannot proceed further"));
                        }
                        return;
                    }
                    //Run main command if it is applicable for this instance type
                    if ((runtimeTypes.contains(RuntimeType.ALL))
                            || (serverEnv.isDas() &&
                                (CommandTarget.DOMAIN.isValid(habitat, targetName) || runtimeTypes.contains(RuntimeType.DAS)))
                            || runtimeTypes.contains(RuntimeType.SINGLE_INSTANCE)
                            || (serverEnv.isInstance() && runtimeTypes.contains(RuntimeType.INSTANCE))) {
                        logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.maincommand",
                                "Command execution stage 3 : Calling main command implementation for {0}", inv.name()));
                        report = doCommand(model, command, context, progressHelper);
                        inv.setReport(report);
                    }



                    if (!FailurePolicy.applyFailurePolicy(fp,
                            report.getActionExitCode()).equals(ActionReport.ExitCode.FAILURE)) {
                        //Run Supplemental commands that have to be run after this command on this instance type
                        logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.postsupplemental",
                                "Command execution stage 4 : Call post supplemental commands for {0}", inv.name()));
                        postSupplementalReturn = supplementalExecutor.execute(suplementalCommands,
                                Supplemental.Timing.After, context, parameters, ufm.optionNameToFileMap());
                        if (postSupplementalReturn.equals(ActionReport.ExitCode.FAILURE)) {
                            report.setActionExitCode(postSupplementalReturn);
                            report.setMessage(adminStrings.getLocalString("commandrunner.executor.supplementalcmdfailed",
                                    "A supplemental command failed; cannot proceed further"));
                            return;
                        }
                    }
                } catch (AdminCommandLockTimeoutException ex) {
                    lockTimedOut = true;
                    String lockTime = formatSuspendDate(ex.getTimeOfAcquisition());
                    String logMsg = "Command: " + model.getCommandName()
                            + " failed to acquire a command lock.  REASON: time out "
                            + "(current lock acquired on " + lockTime + ")";
                    String msg = adminStrings.getLocalString("lock.timeout",
                            "Command timed out.  Unable to acquire a lock to access "
                            + "the domain.  Another command acquired exclusive access "
                            + "to the domain on {0}.  Retry the command at a later "
                            + "time.", lockTime);
                    report.setMessage(msg);
                    report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                } catch (AdminCommandLockException ex) {
                    lockTimedOut = true;
                    String lockTime = formatSuspendDate(ex.getTimeOfAcquisition());
                    String lockMsg = ex.getMessage();
                    String logMsg;

                    logMsg = "Command: " + model.getCommandName()
                            + " was blocked.  The domain was suspended by a "
                            + "user on:" + lockTime;

                    if (lockMsg != null && !lockMsg.isEmpty()) {
                        logMsg += " Reason: " + lockMsg;
                    }

                    String msg = adminStrings.getLocalString("lock.notacquired",
                            "The command was blocked.  The domain was suspended by "
                            + "a user on {0}.", lockTime);

                    if (lockMsg != null && !lockMsg.isEmpty()) {
                        msg += " "
                                + adminStrings.getLocalString("lock.reason", "Reason:")
                                + " " + lockMsg;
                    }

                    report.setMessage(msg);
                    report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                } finally {
                    // command is done, release the lock
                    if (lock != null && lockTimedOut == false) {
                        lock.unlock();
                    }
                }

            } catch (Exception ex) {
                logger.log(Level.SEVERE, KernelLoggerInfo.invocationException, ex);
                report.setActionExitCode(ActionReport.ExitCode.FAILURE);
                report.setMessage(ex.getMessage());
                report.setFailureCause(ex);
                ActionReport.MessagePart childPart =
                        report.getTopMessagePart().addChild();
                childPart.setMessage(getUsageText(model));
                return;
            }
            /*
             * Command execution completed; If this is DAS and the command succeeded,
             * time to replicate; At this point we will get the appropriate ClusterExecutor
             * and give it complete control; We will let the executor take care all considerations
             * (like FailurePolicy settings etc)
             * and just give the final execution results which we will set as is in the Final
             * Action report returned to the caller.
             */

            if (processEnv.getProcessType().isEmbedded()) {
                return;
            }
            if (preSupplementalReturn == ActionReport.ExitCode.WARNING
                    || postSupplementalReturn == ActionReport.ExitCode.WARNING) {
                report.setActionExitCode(ActionReport.ExitCode.WARNING);
            }
            if (doReplication
                    && (!FailurePolicy.applyFailurePolicy(fp, report.getActionExitCode()).equals(ActionReport.ExitCode.FAILURE))
                    && (serverEnv.isDas())
                    && (runtimeTypes.contains(RuntimeType.INSTANCE) || runtimeTypes.contains(RuntimeType.ALL))) {
                logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.startreplication",
                        "Command execution stages completed on DAS; Starting replication on remote instances"));
                ClusterExecutor executor = null;
                // This try-catch block is a fix for 13838
                try {
                    if (model.getClusteringAttributes() != null && model.getClusteringAttributes().executor() != null) {
                        executor = habitat.getService(model.getClusteringAttributes().executor());
                    } else {
                        executor = habitat.getService(ClusterExecutor.class, "GlassFishClusterExecutor");
                    }
                } catch (UnsatisfiedDependencyException usdepex) {
                    logger.log(Level.WARNING, KernelLoggerInfo.cantGetClusterExecutor, usdepex);
                }
                if (executor != null) {
                    report.setActionExitCode(executor.execute(model.getCommandName(), command, context, parameters));
                    if (report.getActionExitCode().equals(ActionReport.ExitCode.FAILURE)) {
                        report.setMessage(adminStrings.getLocalString("commandrunner.executor.errorwhilereplication",
                                "An error occurred during replication"));
                    } else {
                        if (!FailurePolicy.applyFailurePolicy(fp,
                                report.getActionExitCode()).equals(ActionReport.ExitCode.FAILURE)) {
                            logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.afterreplsupplemental",
                                    "Command execution stage 5 : Call post-replication supplemental commands for {0}", inv.name()));
                            ActionReport.ExitCode afterReplicationSupplementalReturn = supplementalExecutor.execute(suplementalCommands,
                                    Supplemental.Timing.AfterReplication, context, parameters, ufm.optionNameToFileMap());
                            if (afterReplicationSupplementalReturn.equals(ActionReport.ExitCode.FAILURE)) {
                                report.setActionExitCode(afterReplicationSupplementalReturn);
                                report.setMessage(adminStrings.getLocalString("commandrunner.executor.supplementalcmdfailed",
                                        "A supplemental command failed; cannot proceed further"));
                                return;
                            }
                        }
                    }
                }
            }
            if (report.getActionExitCode().equals(ActionReport.ExitCode.FAILURE)) {
                // If command is undoable, then invoke undo method method
                if (command instanceof UndoableCommand) {
                    UndoableCommand uCmd = (UndoableCommand) command;
                    logger.fine(adminStrings.getLocalString("dynamicreconfiguration.diagnostics.undo",
                            "Command execution failed; calling undo() for command {0}", inv.name()));
                    uCmd.undo(context, parameters, ClusterOperationUtil.getCompletedInstances());
                }
            } else {
                //TODO : Is there a better way of doing this ? Got to look into it
                if ("_register-instance".equals(model.getCommandName())) {
                    state.addServerToStateService(parameters.getOne("DEFAULT"));
                }
                if ("_unregister-instance".equals(model.getCommandName())) {
                    state.removeInstanceFromStateService(parameters.getOne("DEFAULT"));
                }
            }
        } finally {
            if (ufm != null) {
                ufm.close();
            }
        }
    }
   
    private Map<String,Object> buildEnvMap(final ParameterMap params) {
        final Map<String,Object> result = new HashMap<String,Object>();
        for (Map.Entry<String,List<String>> entry : params.entrySet()) {
            final List<String> values = entry.getValue();
            if (values != null && values.size() > 0) {
                result.put(entry.getKey(), values.get(0));
            }
        }
        return result;
    }
   
    /*
     * Some private classes used in the implementation of CommandRunner.
     */
    /**
     * ExecutionContext is a CommandInvocation, which
     * defines a command excecution context like the requested
     * name of the command to execute, the parameters of the command, etc.
     */
    class ExecutionContext implements CommandInvocation {
       
        private class NameListerPair {
           
            private String nameRegexp;
            private AdminCommandEventBroker.AdminCommandListener listener;

            public NameListerPair(String nameRegexp, AdminCommandListener listener) {
                this.nameRegexp = nameRegexp;
                this.listener = listener;
            }
           
        }

        protected final String scope;
        protected final String name;
        protected ActionReport report;
        protected ParameterMap params;
        protected CommandParameters paramObject;
        protected Payload.Inbound inbound;
        protected Payload.Outbound outbound;
        protected Subject subject;
        protected ProgressStatus progressStatusChild;
        protected boolean isManagedJob;
        private   List<NameListerPair> nameListerPairs = new ArrayList<NameListerPair>();

        private ExecutionContext(String scope, String name, ActionReport report, Subject subject) {
            this.scope = scope;
            this.name = name;
            this.report = report;
            this.subject = subject;
        }

        @Override
        public CommandInvocation parameters(CommandParameters paramObject) {
            this.paramObject = paramObject;
            return this;
        }

        @Override
        public CommandInvocation parameters(ParameterMap params) {
            this.params = params;
            return this;
        }

        @Override
        public CommandInvocation inbound(Payload.Inbound inbound) {
            this.inbound = inbound;
            return this;
        }

        @Override
        public CommandInvocation outbound(Payload.Outbound outbound) {
            this.outbound = outbound;
            return this;
        }

        @Override
        public CommandInvocation listener(String nameRegexp, AdminCommandEventBroker.AdminCommandListener listener) {
            nameListerPairs.add(new NameListerPair(nameRegexp, listener));
            return this;
        }
       
        @Override
        public CommandInvocation progressStatusChild(ProgressStatus ps) {
            this.progressStatusChild = ps;
            return this;
        }
       
        @Override
        public CommandInvocation managedJob() {
            this.isManagedJob = true;
            return this;
        }
       
        @Override
        public void execute() {
            execute(null);
        }

        private ParameterMap parameters() {
            return params;
        }

        private CommandParameters typedParams() {
            return paramObject;
        }

        private String name() {
            return name;
        }

        private String scope() {
            return scope;
        }

        @Override
        public ActionReport report() {
            return report;
        }
       
        private void setReport(ActionReport ar) {
            report = ar;
        }

        private Payload.Inbound inboundPayload() {
            return inbound;
        }

        private Payload.Outbound outboundPayload() {
            return outbound;
        }
       
        @Override
        public void execute(AdminCommand command) {
            if (command == null) {
                command = getCommand(scope(), name(), report(), logger);
                if (command == null) {
                    return;
                }
            }
            /*
             * The caller should have set the subject explicitly.  In case
             * it didn't, try setting it from the current access controller context
             * since the command framework will have set that before invoking
             * the original command's execute method.
             */
            if (subject == null) {
                subject = AccessController.doPrivileged(new PrivilegedAction<Subject>() {
                    @Override
                    public Subject run() {
                        return Subject.getSubject(AccessController.getContext());
                    }
                });
            }
           
            if(!isManagedJob) {
                isManagedJob = AnnotationUtil.presentTransitive(ManagedJob.class, command.getClass());
            }
            JobCreator jobCreator = null;
            JobManager jobManager = null;

            jobCreator = habitat.getService(JobCreator.class,scope+"job-creator");
            jobManager = habitat.getService(JobManagerService.class);

            if (jobCreator == null ) {
                jobCreator = habitat.getService(JobCreatorService.class);

            }

            Job commandInstance = null;
            if (isManagedJob) {
                commandInstance = jobCreator.createJob(jobManager.getNewId(), scope(), name(), subject, isManagedJob, parameters());
            else {
                commandInstance = jobCreator.createJob(null, scope(), name(), subject, isManagedJob, parameters());
            }

            //Register the brokers  else the detach functionality will not work
            for (NameListerPair nameListerPair : nameListerPairs) {
                commandInstance.getEventBroker().registerListener(nameListerPair.nameRegexp, nameListerPair.listener);
            }

            if (isManagedJob)  {
                jobManager.registerJob(commandInstance);
            }
            CommandRunnerImpl.this.doCommand(this, command, subject, commandInstance);
            commandInstance.complete(report(), outboundPayload());
            if (progressStatusChild != null) {
                progressStatusChild.complete();
            }
            CommandSupport.done(habitat, command, commandInstance);
        }
    }

    /**
     * An InjectionResolver that uses an Object as the source of
     * the data to inject.
     */
    private static class DelegatedInjectionResolver
            extends InjectionResolver<Param> {

        private final CommandModel model;
        private final CommandParameters parameters;
        private final MultiMap<String, File> optionNameToUploadedFileMap;

        public DelegatedInjectionResolver(CommandModel model,
                CommandParameters parameters,
                final MultiMap<String, File> optionNameToUploadedFileMap) {
            super(Param.class);
            this.model = model;
            this.parameters = parameters;
            this.optionNameToUploadedFileMap = optionNameToUploadedFileMap;

        }

        @Override
        public boolean isOptional(AnnotatedElement element, Param annotation) {
            String name = CommandModel.getParamName(annotation, element);
            CommandModel.ParamModel param = model.getModelFor(name);
            return param.getParam().optional();
        }

        @Override
        public <V> V getValue(Object component, AnnotatedElement target, Type genericType, Class<V> type) {

            // look for the name in the list of parameters passed.
            if (target instanceof Field) {
                final Field targetField = (Field) target;
                try {
                    Field sourceField =
                            parameters.getClass().getField(targetField.getName());
                    AccessController.doPrivileged(new PrivilegedAction<Object>() {

                        @Override
                        public Object run() {
                            targetField.setAccessible(true);
                            return null;
                        }
                    });
                    Object paramValue = sourceField.get(parameters);
                   
                    /*
                     * If this field is a File, then replace the param value
                     * (which is whatever the client supplied on the command) with
                     * the actual absolute path(s) of the uploaded and extracted
                     * file(s) if, in fact, the file(s) was (were) uploaded.
                     */
                   
                    final List<String> paramFileValues =
                            MapInjectionResolver.getUploadedFileParamValues(
                            targetField.getName(),
                            targetField.getType(),
                            optionNameToUploadedFileMap);
                    if (!paramFileValues.isEmpty()) {
                        V fileValue = (V) MapInjectionResolver.convertListToObject(target, type, paramFileValues);
                        return fileValue;
                    }
                    /*
                    if (paramValue==null) {
                    return convertStringToObject(target, type,
                    param.defaultValue());
                    }
                     */
                    // XXX temp fix, to revisit
                    if (paramValue != null) {
                        checkAgainstAcceptableValues(target,
                                paramValue.toString());
                    }
                    return type.cast(paramValue);
                } catch (IllegalAccessException e) {
                } catch (NoSuchFieldException e) {
                }
            }
            return null;
        }

        private static void checkAgainstAcceptableValues(
                AnnotatedElement target, String paramValueStr) {
            Param param = target.getAnnotation(Param.class);
            String acceptable = param.acceptableValues();
            String paramName = CommandModel.getParamName(param, target);

            if (ok(acceptable) && ok(paramValueStr)) {
                String[] ss = acceptable.split(",");

                for (String s : ss) {
                    if (paramValueStr.equals(s.trim())) {
                        return;         // matched, value is good
                    }
                }

                // didn't match any, error
                throw new UnacceptableValueException(
                        adminStrings.getLocalString(
                        "adapter.command.unacceptableValue",
                        "Invalid parameter: {0}.  Its value is {1} "
                        + "but it isn''t one of these acceptable values: {2}",
                        paramName,
                        paramValueStr,
                        acceptable));
            }
        }
    }
   
    /**
     * Is the boolean valued parameter specified?
     * If so, and it has a value, is the value "true"?
     */
    private static boolean isSet(ParameterMap params, String name) {
        String val = params.getOne(name);
        if (val == null) {
            return false;
        }
        return val.length() == 0 || Boolean.valueOf(val).booleanValue();
    }
   
    /** Works as a key in ETag cache map
     */
    private static class NameCommandClassPair {
        private String name;
        private Class<? extends AdminCommand> clazz;
        private int hash; //immutable, we can cache it

        public NameCommandClassPair(String name, Class<? extends AdminCommand> clazz) {
            this.name = name;
            this.clazz = clazz;
            hash = 3;
            hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
            hash = 67 * hash + (this.clazz != null ? this.clazz.hashCode() : 0);
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final NameCommandClassPair other = (NameCommandClassPair) obj;
            if (this.clazz != other.clazz) {
                return false;
            }
            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            return hash;
        }    
    }

    /**
     * Encapsulates handling of files uploaded to the server in the payload
     * of the incoming HTTP request.
     * <p>
     * Extracts any such files from the payload into a temporary directory
     * under the domain's applications directory.  (Putting them there allows
     * the deployment processing to rename the uploaded archive to another location
     * under the applications directory, rather than having to copy them.)
     */
    private class UploadedFilesManager {

        private final ActionReport report;
        private final Logger logger;
        /**
         * maps option names as sent with each uploaded file to the corresponding
         * extracted files
         */
        private MultiMap<String, File> optionNameToFileMap;

        /*
         * PFM needs to be a field so it is not gc-ed before the
         * UploadedFilesManager is closed.
         */
        private PayloadFilesManager.Temp payloadFilesMgr = null;

        private UploadedFilesManager(final ActionReport report,
                final Logger logger,
                final Payload.Inbound inboundPayload) throws IOException, Exception {
            this.logger = logger;
            this.report = report;
            extractFiles(inboundPayload);
        }

        private MultiMap<String, File> optionNameToFileMap() {
            return optionNameToFileMap;
        }

        private void close() {
            if (payloadFilesMgr != null) {
                payloadFilesMgr.cleanup();
            }
        }

        private void extractFiles(final Payload.Inbound inboundPayload)
                throws Exception {
            if (inboundPayload == null) {
                return;
            }

            final File uniqueSubdirUnderApplications = chooseTempDirParent();
            payloadFilesMgr = new PayloadFilesManager.Temp(
                    uniqueSubdirUnderApplications,
                    report,
                    logger);

            /*
             * Extract the files into the temp directory.
             */
            final Map<File, Properties> payloadFiles =
                    payloadFilesMgr.processPartsExtended(inboundPayload);

            /*
             * Prepare the map of command options names to corresponding
             * uploaded files.
             */
            optionNameToFileMap = new MultiMap<String, File>();
            for (Map.Entry<File, Properties> e : payloadFiles.entrySet()) {
                final String optionName = e.getValue().getProperty("data-request-name");
                if (optionName != null) {
                    logger.finer("UploadedFilesManager: map " + optionName
                            + " to " + e.getKey());
                    optionNameToFileMap.add(optionName, e.getKey());
                }
            }
        }

        private File chooseTempDirParent() throws IOException {
            final File appRoot = new File(domain.getApplicationRoot());

            /*
             * Apparently during embedded runs the applications directory
             * might not be present already.  Create it if needed.
             */
            if (!appRoot.isDirectory()) {
                if (!appRoot.exists() && !appRoot.mkdirs()) {
                    throw new IOException(adminStrings.getLocalString("commandrunner.errCreDir",
                            "Could not create the directory {0}; no further information is available.",
                            appRoot.getAbsolutePath()));
                }
            }

            return appRoot;
        }
    }

    /**
     * Format the lock acquisition time.
     */
    private String formatSuspendDate(Date lockTime) {
        if (lockTime != null) {
            String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
            return sdf.format(lockTime);
        } else {
            return adminStrings.getLocalString("lock.timeoutunavailable",
                    "<<Date is unavailable>>");
        }
    }
}
TOP

Related Classes of com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.