Extending the command-line
This topic contains information about extending the command-line by providing additional commands.
The Overview of command-line interface article provides general command-line information. This article provides the following information about extending the command line:
You might want to extend the command-line to add support for new features or enhance the existing ones. For example you might want to add a new build command that compiles, packages and publishes a module or perhaps you want to add a command that validates all of the html files in the public folder. You can easily extend the command-line interface in IBM® WebSphere® sMash as each module has the ability to provide new commands. The command-line uses the features of WebSphere sMash like event processing and virtual directories, so any module can provide additional commands.
Creating a new command
You can create a command-line command by registering a handler for the cliTask event and providing an implementation of the onCliTask method.
The easiest way to register a cliTask handler is to put a Groovy or PHP script in the app/tasks directory that allows implicit registration of the handler. The virtual directory feature of WebSphere sMash automatically finds the new cliTask handlers.
The script must provide an implementation of the onCliTask method to be invoked when the cliTask event is fired. The onCliTask method can access the
event zone to obtain the arguments passed on invocation and the name of the event. The following global context paths are also used:
-
/event/task - Contains the name of the invoked task.
-
/event/args - Contains a List of the command-line arguments; none if not specified.
The following Groovy code snippet shows how the task is implemented in Groovy.
def onCliTask() {
def task = zget("/event/task")
System.out.println("task name is " + task)
zget("/event/args").each{System.out.println("arg is " + it)}
}
The following PHP code snippet shows how the task is implemented in PHP.
- Note that if a PHP script is used to implement a task, then the application must specify a dependency on PHP in the config/ivy.xml file
- The name of the class must match the filename so the example below shows the contents of app/tasks/mytask.php
<?php
class mytask {
function onCliTask () {
$task = zget("/event/task");
echo ("Task name is $task.");
$args = zget("/event/args");
foreach ($args as $arg) {
echo " Arg is $arg.";
}
}
}
If the task handler is written in Java™, then the handler must be explicitly registered
for the cliTask event providing the task name.
# MyTask
/config/handlers += [{
"events": "cliTask",
"handler" : "zero.simple.task.MyTask.class",
"conditions" : "/event/task =~ mytask"
}]
The Java-based task must provide an implementation of the onCliTask method and the event zone, available through the GlobalContext, can be used to obtain the event arguments.
public void onCliTask() {
List<String> args = GlobalContext.zget("/event/args");
...
}
The task implementation might need to access the location from which the task was invoked and the location where the WebSphere sMash command-line is installed. The system property user.dir can be used to determine the location from which the task was invoked, but this might not be desired location. The command-line supports executing a task from any directory under the module. The apphome directory is the root directory of the module and can be obtained from the event zone,
event/apphome. The root directory of the module is probably more useful to find artifacts like the config directory.
static public File getAppRoot() {
File app = zget("/event/apphome");
return app;
}
The location where the command-line was installed, also referred to as zerohome, might need to be determined. The command-line scripts set zerohome as an environment property.
String zerohome = System.getenv("ZERO_HOME");
If a PHP script is used in the implementation of a command then the PHP runtime's current working directory will be set to the current working directory of the process which invoked the command.
A task should implement the -json option. This option allows a structured output that can be reliably parsed by applications like the Application Builder. In general, a task displays messages with the LogHelper with level INFO, which writes to stderr. When -json is specified, a json structured result is written to stdout. This enables you to programmatically invoke commands and use the exit code and -json option to determine the results.
The version command in the zero.cli.tasks module has a -full option that displays more comprehensive results for problem determination. The -full option fires a collectInfo event that allows other components to provide additional detailed information to be aggregated. The collectInfo task implementors check for the -json option and, if specified, add their
results to the map that can be obtained from /event/collectResults. The results
are placed in the map under a module label, for example, zero.cli.tasks. If -json is not specified, then the results are displayed using LogHelper.
The map found in the /event/collectResults event zone can not be replaced. The version task requires that this map is updated with the additional information.
The following snippets show registering a handler for the collectInfo event, and providing results.
/config/handlers += [{
"events": "cliTask",
"handler" : "my.component.Dump.class",
"conditions" : "/event/task =~ collectInfo"
}]
public void onCliTask() {
Object obj = zget("/event/collectResults");
if (obj != null && obj instanceof Map ) {
((Map)obj).put("my.component", "my.results");
}
The -json option can be tested by parsing the args passed in /event/args.
Creating a group of tasks
With command-line task support, you can also group a set of tasks, referred to as subtasks. One of the benefits of using subtasks is that users can request help for a single subtask and see all of the available commands. This grouping makes the command-line easier to use for a set of commands that all operate on a single instance, like a module group or a repository. The following snippet shows explicitly registering a subtask for a Java-based implementation.
# Mytask
/config/handlers += [{
"events": "cliTask",
"handler" : "zero.cli.tasks.commands.SubCommand.class",
"conditions" : "/event/task =~ mytask"
}]
# MySubTask
/config/handlers += [{
"events": "cliSubTask",
"handler" : "zero.simple.task.MySubTask.class",
"conditions" : "(/event/task =~ mytask) && (/event/subTask =~ mysubtask)"
}]
In the preceding example, specifying event/task defines the task name, and event/subTask defines the subtask. The condition match invokes the onCliSubTask method of the handler.
Invoking the task directly, zero mytask, displays the help for the task and all of the
subtasks. A subtask is invoked by specifying the task name and then the subtask name, for example,
zero mytask mysubtask. The subtask must implement the onCliSubTask method, as shown in
the following example.
public void onCliSubTask() {
System.out.println("subtask was invoked");
}
A Groovy subtask can be implicitly registered by placing the script in a directory created under app/tasks.
The subtask command can then be invoked by executing zero group1 subtask1
, where group1
is the directory created under app/tasks. The implicit support will first see if there is a handler for the
group1 task by testing if the file app/tasks/group1.groovy exists. If the group1.groovy file does
exist then the cliTask event is fired and the onCliTask method is invoked. However, if the file
group1.groovy does not exist, then the implicit support will test the next argument as the subtask name and determine
if the script app/tasks/group1/subtask1.groovy exists. If the subtask script does exist, then the cliSubTask
event is fired and the onCliSubTask method is invoked. The help support
for implicit subtasks is the same as the explicit support. The implicit subtask support only supports one level of
subtasks below the app/tasks directory.
A Groovy subtask can also be explicitly registered using the SubCommand class. In this case, the
Groovy subtask script should also be explicitly registered as a handler.
The following code sample shows explicitly registering a Groovy task named testing and a subtask named hello. These tasks can be invoke as zero testing hello.
# testing
/config/handlers += [{
"events": "cliTask",
"handler" : "zero.cli.tasks.commands.SubCommand.class",
"conditions" : "/event/task =~ testing"
}]
/config/handlers += [{
"events": "cliSubTask",
"handler" : "hello.groovy",
"conditions" : "(/event/task =~ testing) && (/event/subTask =~ hello)"
}]
The implementation for hello.groovy must be in app/scripts directory.
def onCliSubTask() {
System.out.println("hello from subtask");
}
Providing help for a task
You can use the task support help component to display help text for each of the tasks.
The help component is implemented using property files that are placed in the
app/tasks/help folder. The naming convention for the help property file is
taskname.properties. A task can also supply help for all supported locales by providing
properties files with language, country and variants specified. Each task should provide help, and zero help is the preferred way of determining which commands are available to a module.
The following list is the search order used to find the appropriate help property file for a task using the user’s locale:
- taskname_language_country_variant.properties
- taskname_language_country.properties
- taskname_language.properties
- taskname.properties
As an example, to provide a help properties file for the resolve task for English (default) and Japanese, you could provide the following properties files.
resolve_ja.properties resolve.properties
Additional property files can be supplied to support other locale variants.
You can use the help component to display help for subtasks by placing those help property files under the
app/tasks/help/${subtask} folder, where ${subtask} is the name of the subtask. The help property file
supports both short and full help text. The short help is displayed when help for all tasks, including subtasks, is requested.
Full help is displayed when the help is requested for a single task.
Short text should be limited to one line and is specified in help property file for a task with the label short. The full text is specified with a label of full and allows multiple lines. Each specified line is printed with a new line to allow some control over formatting.
The following example shows a help properties file for a task.
short=Prints the documentation for the given command. full.1=Usage: full.2= full.3=zero help [<command>] full.4= full.5=The command will print the documentation for the given command. If no command is provided, it will print a short summary of all commands currently available to the command-line.