2011-08-28

A New Shell for Equinox - Writing Commands for the Shell

Anatomy of a Gogo command


In the built-in Equinox console commands are provided by classes extending the CommandProvider interface. All commands should be public methods, starting with underscore and accepting as an argument a CommandInterpreter object. A little bit restricting, isn't it? Well, now comes the new shell, with the Gogo-type commands, which have none of these restrictions.

Gogo commands are provided by an arbitrary class (no interfaces to implemet!). The commands should be public methods of the class and they may take arbitrary arguments. The command providers are registered as services with two specific properties:

  • osgi.command.scope - specifies the scope of the commands
  • osgi.command.function - lists the names of all commands in the provider (i.e., the names of the public methods in the class)
Following is a very simple example of a Gogo command, which says "Hello world!".

pulblic class HelloCommand {
  public void sayHello() {
    System.out.println("Hello world!");
  }
}

The code to register this command is:

...
Dictionary<String, Object> properties = new Hashtable();
properties.put("osgi.command.scope", "myscope");
properties.put("osgi.command.function", new String[] {"sayHello"});
context.register(HelloCommand.class, new HelloCommand(), properties);
...

In this way you can register existing classes with public methods as command providers. Sometimes this could be very useful.


Converters and Formatters


As I mentioned, the commands may accept arguments of arbitrary types. Probably you wonder how the strings, read from the input stream, are converted to the command argument type? This is possible because of the special converters which the Gogo shell introduces.

The converter is a class which knows how to convert the value, read from the command line, to the actual argument. For example, your command may accept a Bundle object as an argument. Assume you want to call the command with the bundle id or the bundle symbolic name. You write a converter, which looks up the Bundle object in the BundleContext by the id, or in the PackageAdmin service by its symbolic name, and returns the Bundle object.

The converters are registered as services. A converter should implement the interface org.apache.felix.service.command.Converter. The interface has two methods - convert and format. When the Gogo shell executes a command, it calls all registered converters, passing the input arguments to their convert methods. It calls the command with whichever converted object matches its argument type.


The output of a Gogo command can be an object of arbitrary type. The shell itself cannot know how to display all possible objects. So Gogo uses again the converters, but their format method. For example, your command may return a Dictionary object. You may register a converter, which knows exactly how to format and output the values in the Dictionary. After Gogo executes the command and gets the result, it calls all registered converters on it.

You may register a converter implementation which implements only the convert method, only the format method, or both - it depends on your command. Gogo provides some default converters, so it is not always necessary to provide your own.

Customizing the Tab Completion


As explained in the previous post, the new shell provides tab completion for commands names, variables names and files names. It is possible that the command has specific input and in this case it would have a better knowledge how to complete it. For this case the shell supports custom completers. A custom completer should implement the interface org.eclipse.equinox.console.common.Completer. It has one method - getCandidates, which gets as an argument the whole command line and the current position of the cursor in it, and returns a map with all completion candidates. The keys are the candidates, and the values are the start positions for the completions. The completers are registered as services.

Няма коментари:

Публикуване на коментар