GlassFish CLI Landscape In GlassFish it is very easy to add new admin CLI commands once you know where to start. This document attempts to give a brief overview of the CLI landscape to help get you started. 1) CRUD Command If your command will primarily update the DAS's configuration (domain.xml), then you want to implement a CRUD command. A CRUD command is a remote 1 command that supports modifying the DAS configuration. CRUD commands can create, delete and list XML elements from a CLI invocation without requiring the writing of any code. The general approach is to create an interface for a server config bean that extends org.jvnet.hk2.config.ConfigBeanProxy and then annotate the interface. For details see CRUD asadmin commands implementation in GlassFish V3. If the command needs to do some work in addition to updating the configuration then you can add a decorator or supplemental command annotation.
2) Local Only Command If you want to implement a command that does not run in the DAS – i.e. it executes in the asadmin client directly – then you want a local only command. To do this you extend com.sun.enterprise.admin.cli.CLICommand and annotate the class.
Note: For your local command to be picked up by asadmin, the module should be in the Class-Path element of admin/cli/pom.xml. 3) Remote Only Command Your first choice for a remote only command is to make it a CRUD command – see #1. But if your command does not modify the DAS's configuration then a remote only command may be more appropriate. To do this you implement the org.glassfish.api.admin.AdminCommand interface – basically implementing an execute method – and annotate the class.
A couple additional examples are given in this blog: How to add a new cli command (asadmin utlilty) 4) Hybrid Commands If you have a command that needs to perform some local operations while running in the asadmin client plus perform one or more remote operations then you want a hybrid command. To implement a hybrid command you first implement a remote command for the remote part of the operation using #1 or #3. Then implement a local only command that performs the local portion of the command and then uses the com.sun.enterprise.admin.cli.remote.RemoteCommand class to execute the remote command.
Other Command Framework Features Cluster-Aware Commands In 3.1 the ability to propagate commands across a cluster was introduced. For details on how to make your commands cluster aware see: How to make CLIs cluster aware. Bean Validation The 4.0 release introduced the ability to validate AdminCommand parameters using bean validation. The bean validation API (BV) allows fields within a class or the class itself to be annotated with constraints that are used validate whether the properties of the object are valid. This feature allows BV to be used with AdminCommand objects. After the @Param annotations are resolved to inject the parameter values into the AdminCommand object, the BV validate method is called to ensure that any constraints are satisfied. Here is an example from the GetHabitatInfo command. Note that parts of the command implementation not relevant to this example have been omitted.
@Service(name = "_get-habitat-info")
public class GetHabitatInfo implements AdminCommand {
@JavaClassName
@Param(primary = true, optional = true)
String contract = null;
@Pattern(regexp="true|false")
@Param(optional = true)
String started = "false";
...
}
Here, @JavaClassName is a constraint that is defined by the GlassFish config-api module which ensures that the value conforms to the syntax of a Java class. The @Pattern constraint is defined by the BV framework and it ensures that the value of the option is either true or false. The use of @Pattern here produces the same result as if the "acceptableValues" parameter was used with the @Param annotation. BV constraints can also be used at the class level. For example, consider a case where a command accepts two integer options, and the sum of the two options has to be less than 10. This can be implemented as follows:
@Service(name = "some-command")
@SomeCommand.Constraint
public class SomeCommand implements AdminCommand {
@Param(optional=true)
int a = 0;
@Param(optiona=true)
int b = 0;
@Retention(RUNTIME)
@javax.validation.Constraint(validatedBy = SomeCommand.Validator.class)
public static @interface Constraint {
String message() default "The a and b options must sum to less than 10.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public static class Validator
implements ConstraintValidator<SomeCommand.Constraint, SomeCommand>, Payload {
@Override
public void initialize(final SomeCommand.Constraint constraint) { }
@Override
public boolean isValid(final SomeCommand bean,
final ConstraintValidatorContext constraintValidatorContext) {
return bean.a + bean.b < 10
}
}
}
Dynamic Default Value Calculation The 4.0 release introduced the ability to calculate a default value for an option dynamically using a class. Previously, there were two ways to set a default value for a command parameter: 1) Use the defaultValue argument of the @Param annotation
@Param(optional=true, defaultValue="true")
boolean flag;
2) Assign the field with a value, which may be a calculated value by calling a method.
@Param(optional=true)
boolean flag = true;
The dynamic default value calculation feature provides a third way of defining a default value for an @Param: 3) Use the defaultCalculator argument of the @Param annotation (new in 4.0)
@Param(optional=true, defaultCalculator=DefaultCalc.class)
boolean flag;
The DefaultCalc class must extend the org.glassfish.api.ParamDefaultCalculator class. This class has a single method, defaultValue which accepts an ExecutionContext as an argument. The AdminCommandContext extends ExecuteContext, so the defaultValue method can check to see if it actually received an AdminCommandContext and use the information from that object to calculate the default value. Here is an example DefaultCalc class:
class DefaultCalc extends ParamDefaultCalculator {
public DefaultCalc() {}
public String defaultValue(ExecutionContext context) {
return "some calculated value";
}
}
A public constructor is needed for this class so that the command framework is able to create an instance. In GlassFish a remote command is a command that runs in the DAS. It is called remote because you can run asadmin on any machine and connect to a remote DAS by specifying the --host and --port options. A local command is a command that runs in the asadmin client and therefore can only manipulate the GlassFish install that asadmin is running directly out of. |