Administrative command infrastructure in V3

The proliferation of containers and independent packaging and loading of such containers requires a new administrative command infrastructure to support the following features :

  • location independence : admin commands should be loadable from any module known to GlassFish
  • extensible : there should not be be a preset list of commands known to GlassFish, they should be discovered on demand
  • HK2 components : commands should be able to use injection to express their dependencies and extraction to provide results to the community.

AdminCommand

AdminCommand implementations represent the implementation of one admin command. Each implementation should be annotated with @Service to be eligible for resource injection/extraction.

A context object is provided which is used to access the command parameters (although this is not the prefered way, see below @Param) (note : we might remove this parameters access). The context also provide the ActionReport implementation which is an abstraction of a reporting tool (for text or html reports for instance).

/**
 * This is an admin command interface, command implementation have to be 
 * stateless.
 *
 * @author Jerome Dochez
 */
@Contract
public interface AdminCommand {       
    
    /**
     * Executes the command with the command parameters passed as Properties 
     * where the keys are the paramter names and the values the parameter values
     * @param context information 
     */
    public void execute(AdminCommandContext context);

}

AdminCommand implementations are services therefore their implementation is location independent as long as the module containing them is known to the GlassFish runtime.

AdminCommandContext

Bag of utility services access :

/**
 * Useful services for Deployer service implementation
 *
 * @author Jerome Dochez
 */
public class AdminCommandContext implements ExecutionContext {
    
    public final ActionReport report;
    public final Properties params;
    public final Logger logger;
    
    public AdminCommandContext(Logger logger, ActionReport report, Properties params) {
        this.logger = logger;
        this.report = report;
        this.params = params;
    }
    /**
     * Returns the Reporter for this action
     * @return ActionReport implementation suitable for the client
     */
    public ActionReport getActionReport() {
        return report;
    }
    
    /**
     * Returns the DeployCommand parameters 
     * @return the command parameters
     */
    public Properties getCommandParameters() {
        return params;
    }

    /**
     * Returns the Logger
     * @return the logger
     */
    public Logger getLogger() {
        return logger;
    }
}

Param

The param annotation is used by AdminCommand implementations to identify the parameters to the command. This is used by the GlassFish runtime to do the following task :

  • parameter validation : all non optional parameters must be provided before the command is executed
  • i18n : using the optional i18n, the system can automatically get the local resource associated with a command and display to the user.
  • reflection : the system can use reflection to find the list of parameters to a command and generate appropriate help message generically.
  • injection : all parameters are injected in the annotated field/method before the command is executed.
/**
 * Param is a parameter to a command. This annotation can be placed on a field or
 * setter method to identify the parameters of a command and have those parameters
 * injected by the system before the command is executed.
 *
 * The system will check that all non optional parameters are satisfied before invoking
 * the command.
 *
 * @author Jerome Dochez
 */
@Retention(RUNTIME)
@Target({METHOD,FIELD})
public @interface Param {

    /**
     * Retuns the name of the parameter as it has be specified by the client when invoking
     * the command. By default the name is deducted from the name of the annotated element.
     * If the annotated element is a field, it is the filed name.
     * If the annoated element is a mehod, it is the JavaBeans property name from the setter
     * method name
     *
     * @return the parameter name.
     */
    public String name() default "";

    /**
     * Returns a list of comma separated acceptable values for this parameter. The system
     * will check that one of the value is used before invoking the command.
     *
     * @return the list of comma separated acceptable values
     */
    public String acceptableValues() default "";

    /**
     * Returns true if the parameter is optional to the successful invocation of the command
     * @return true if the parameter is optional
     */
    public boolean optional() default false;

    /**
     * Returns the short name associated with the parameter so that the user can specify
     * -p as well as -password when invoking the command.
     *
     * @return the parameter short name
     */
    public String shortName() default "";

    /**
     * Returns true if this is the primary parameter for the command which mean that the
     * client does not have to pass the parameter name but just the value to the command.
     *
     * @return true if this is the primary command parameter.
     */
    public boolean primary() default false;

}

We should add maven plugins to generate HTML pages with the reflective information gathered from AdminCommand @Param annotations, much like javadoc is doing.

Internationalization

Each parameter or admin command can be annotated with the i18n annotation to identify the localstring key associated with the command or parameter. By default the system will use the following mapping

{command name}.command for the command key
{command name}.command.<param name> for the parameter key

these default mapping can be overridden using the @i18n annotation.

/**
 * Identify an I18n resource associated with the annotated element. The value() holds the name
 * of the resource as it stored in the LocalStrings.properties and can be used by the runtime
 * to generate appropriate localized meta-data.
 *
 * @author Jerome Dochez
 */
@Retention(RUNTIME)
@Target({TYPE,METHOD,FIELD})
public @interface I18n {

    /**
     * Returns the string identify the i18n resource from the resource bundle associated with
     * the class containing the annotation.
     *
     * @return a string identifying the resource in the bundle
     */
    public String value();
}

Example :

Here is an example of a "cool" command that takes 2 parameters, one is optional and also uses injection.

usage would be : asadmin coo --foo XXX --bar YYY

package com.foo.mycontainer;

/**
 * Sample command
 */
@Service(name="cool")
public Class MyCommand implements AdminCommand {

    @Inject
    Domain domain;

    @Param
    String foo;

    @Param(name="bar", optional=true)
    @I18n("myoldname")
    String renamedTooManyTimes

    /**
     * Executes the command with the command parameters passed as Properties 
     * where the keys are the paramter names and the values the parameter values
     * @param context information 
     */
    public void execute(AdminCommandContext context) {
        // domain and foo are not null
        // renamedTooManyTimes can be null.
    }
}

LocalStrings : 

cool.command=Does something really cool
cool.command.foo=The foo parameter 
cool.command.myoldname=The other parameter explanation