Extending UTerm

UTerm provides you all the required bits and pieces to monitor and manage the UltraESB instance on a command line. Given that, sometimes users have there own custom requirements to monitor there solutions/applications deployed on the UltraESB, in which case the UTerm custom extension capability can be used to include new commands into the UTerm.

Writing a custom command

Writing a custom command just requires writing the logic that you want to expose as a command. This class should have the methods which contain the commands that you want to expose annotated with the UTerm runtime runtime annotations.

This sub section shows how to write a command class exposing two commands to the users via the UTerm. Before starting the exercise, there are few things that needs your attention. You need to take a dependency on the ultraesb-uterm library.

Extend from CommandsBase

If your command talks to any of the UltraESB MXBeans exposed over JMX, which is most likely to be the case, extending from the abstract CommndsBase class is helpful, as it provides very convenient way of accessing the MXBean interfaces.

Note
Not a must to extend from CommandsBase, but you should if using the UltraESB MXBeans.

The full qualified class name of the CommandsBase is "org.adroitlogic.ultraesb.terminal.commands.CommandsBase", we will see the usage of this to call the JMX MXBeans to retrieve data from the UltraESB instance inside the command implementation.

Should throw TerminalException

To use the error handling and proper error reporting capabilities of the UTerm framework, any method exposed as a command should throw a TerminalException. The full qualified class name of the TerminalException is "org.adroitlogic.ultraesb.terminal.TerminalException"

It is recommended that you catch all possible exceptions within the method implementation and only throw a TerminalException with a sensible message, as that will displayed to the user on an error condition.

Use of MultiColumnResult

If the results of a particular command should be formatted into a multi column tabular data format, you should use a MultiColumnResult object to return the dataset. This will help the command implementation to completely concentrate on the commands functionality, while the result set formatting being transparently handled by the UTerm framework.

The full qualified class name of the MultiColumnResult is "org.adroitlogic.ultraesb.terminal.util.MultiColumnResult".

So the resulting class would be as follows;

package org.adroitlogic.ultraesb.terminal.commands;

import org.adroitlogic.ultraesb.jmx.JMXConstants;
import org.adroitlogic.ultraesb.jmx.core.EndpointManagementMXBean;
import org.adroitlogic.ultraesb.jmx.core.SequenceManagementMXBean;
import org.adroitlogic.ultraesb.jmx.view.EndpointView;
import org.adroitlogic.ultraesb.jmx.view.SequenceView;
import org.adroitlogic.ultraesb.terminal.TerminalException;

import java.util.ArrayList;
import java.util.List;

public class SearchCommand extends CommandsBase {

  public List<String> searchEndpoints(String contains, boolean caseSensitive) throws TerminalException {
    if (contains == null) {
      throw new TerminalException("Search string cannot be null");
    }

    List<String> searchResults = new ArrayList<String>();
    for (EndpointView endpoint : getMXBeanProxy(JMXConstants.MXBEAN_NAME_ENDPOINTS,
          EndpointManagementMXBean.class).getEndpoints()) {
      if (contains(endpoint.getId(), contains, caseSensitive)) {
        searchResults.add(endpoint.getId());
      }
    }
    return searchResults;
  }

  public List<String> searchSequences(String contains, boolean caseSensitive) throws TerminalException {
    if (contains == null) {
      throw new TerminalException("Search string cannot be null");
    }

    List<String> searchResults = new ArrayList<String>();
    for (SequenceView endpoint : getMXBeanProxy(JMXConstants.MXBEAN_NAME_ENDPOINTS,
          SequenceManagementMXBean.class).getSequences()) {
      if (contains(endpoint.getId(), contains, caseSensitive)) {
        searchResults.add(endpoint.getId());
      }
    }
    return searchResults;
  }

  private boolean contains(String id, String contains, boolean caseSensitive) {
    if (!caseSensitive) {
      id = id.toUpperCase();
      contains = contains.toUpperCase();
    }
    return id.indexOf(contains) != -1;
  }

}

If you carefully look at this class you will be able to see that we use the "getMXBeanProxy" method defined in the CommandsBase to very easily get hold of a MXBean reference and talk to the UltraESB instance via JMX to retrieve data all transparently.

Now we have written the class with using all the optimizations of the UTerm framework, yet it is not a command. We need to annotate the methods which are exposed as commands using the following annotations for it to be complete.

UTerm Annotations

There are two major types of annotations that helps to specify the meta information require in converting a method into a command on UTerm.

TerminalCommand annotation

This annotation is a markable annotation with certain metadata describing the command into the UTerm framework. The retention policy for this annotation is runtime and is targeting method element type. It has 4 properties to describe the command.

Property Name Usage Type Required

name

the long form name of the command

String

YES

alias

the short form name of the command

String

YES

description

the description of the command to be used in printing the usage with help

String

YES

helpFooter

the footer of the usage help, if any

String

NO

As you can see these properties completely describes the command, and this annotation helps the command writer concentrate on the functionality without bothering about how to expose it as a command.

Decouples the command properties from the logic
TerminalCommand annotation decouples the command properties from the execution logic so that implementing a command is just a matter of writing a Java method which does some unit of work.

Full qualified class name of the annotation class is "org.adroitlogic.ultraesb.terminal.annotations.TerminalCommand".

CommandOption annotation

This annotation also is a markable annotation with certain metadata describing the command options of a particular command into the UTerm framework. The retention policy for this annotation is runtime and is targeting method parameter element type. It has 4 properties to describe the command options, and used to make additional/optional method parameters into options in executing the command.

Property Name Usage Type Required

shortForm

the short form (single letter) name of the command option, at usage prefixed with '-'

String

YES

longForm

the long form name of the command option, at usage prefixed with '–'

String

YES

argument

whether the option requires an argument

Boolean

NO

description

the description of the command option to be used in printing the usage with help

String

YES

As you can see these properties completely describes the command option, and this annotation helps the command writer concentrate on the functionality without bothering about how to map method parameters into command options.

Command options are most of the time boolean switches
While it is possible to have options providing arguments to the command execution, most of the time the command options are boolean parameters specifying a switch in the execution

Full qualified class name of the annotation class is "org.adroitlogic.ultraesb.terminal.annotations.CommandOption".

With these annotations we could convert over class to expose 2 commands out of the 2 methods by annotating the methods and method parameters as follows;

package org.adroitlogic.ultraesb.terminal.commands;

import org.adroitlogic.ultraesb.jmx.JMXConstants;
import org.adroitlogic.ultraesb.jmx.core.EndpointManagementMXBean;
import org.adroitlogic.ultraesb.jmx.core.SequenceManagementMXBean;
import org.adroitlogic.ultraesb.jmx.view.EndpointView;
import org.adroitlogic.ultraesb.jmx.view.SequenceView;
import org.adroitlogic.ultraesb.terminal.TerminalException;
import org.adroitlogic.ultraesb.terminal.annotations.CommandOption;
import org.adroitlogic.ultraesb.terminal.annotations.TerminalCommand;

import java.util.ArrayList;
import java.util.List;

public class SearchCommand extends CommandsBase {

  @TerminalCommand(
      name = "search-endpoints",
      alias = "sep",
      description = "search the endpoints with id containing the given string")
  public List<String> searchEndpoints(String contains,
    @CommandOption(
        shortForm = "s",
        longForm = "case-sensitive",
        description = "search the term case sensitively") boolean caseSensitive) throws TerminalException {
    if (contains == null) {
      throw new TerminalException("Search string cannot be null");
    }

    List<String> searchResults = new ArrayList<String>();
    for (EndpointView endpoint : getMXBeanProxy(JMXConstants.MXBEAN_NAME_ENDPOINTS,
          EndpointManagementMXBean.class).getEndpoints()) {
      if (contains(endpoint.getId(), contains, caseSensitive)) {
        searchResults.add(endpoint.getId());
      }
    }
    return searchResults;
  }

  @TerminalCommand(
      name = "search-sequences",
      alias = "ssq",
      description = "search the sequences with id containing the given string")
  public List<String> searchSequences(String contains,
    @CommandOption(
        shortForm = "s",
        longForm = "case-sensitive",
        description = "search the term case sensitively") boolean caseSensitive) throws TerminalException {
    if (contains == null) {
      throw new TerminalException("Search string cannot be null");
    }

    List<String> searchResults = new ArrayList<String>();
    for (SequenceView endpoint : getMXBeanProxy(JMXConstants.MXBEAN_NAME_ENDPOINTS,
          SequenceManagementMXBean.class).getSequences()) {
      if (contains(endpoint.getId(), contains, caseSensitive)) {
        searchResults.add(endpoint.getId());
      }
    }
    return searchResults;
  }

  private boolean contains(String id, String contains, boolean caseSensitive) {
    if (!caseSensitive) {
      id = id.toUpperCase();
      contains = contains.toUpperCase();
    }
    return id.indexOf(contains) != -1;
  }

}

The annotated class represents a command and the registration of this command into UTerm framework will be discussed next.

Long and short form command names should be unique
For the command to be operational within UTerm the given short form and the long form of the command names need to be unique, before selecting the command name (specially the short form) you better have a look at the available commands under the UTerm Command Reference.

Integrating the command into UTerm

Hard part is already done and we have the command source now. The above source should be compiled and packed into a jar file, and copy it to the lib/custom directory (which is the place where you put all the custom libraries) of the UltraESB installation.

After making it available in the class path it is just a matter of configuring the class name as an extension to UTerm via the uterm.properties file with the property key "ultra.terminal.command.ext.x" where x is an incrementing number starting from 1 for each and every command class.

So integrating our command into the UTerm can be done with adding the following property into the uterm.properties file.

ultra.terminal.command.ext.1=org.adroitlogic.ultraesb.terminal.commands.SearchCommand

Now if you start UTerm and type in help you will be able to see our new command appearing on the list of available commands and you could see the usage of the command with the -h option.

uterm> sep -h
usage: sep
'sep' equivalent to 'search-endpoints'
search the endpoints with id containing the given string
 -h,--help             prints this command help
 -s,--case-sensitive   search the term case sensitively

uterm>

Then if you try to invoke this command over default UltraESB instance for searching "out" endpoints, it will be as follows;

uterm> sep out
echo-proxy-outDestination
uterm>
Help option is automatically added
The usage of this command given the ’-h` option too, which we have not declared, but the UTerm framework adds the'-h’ option to all the commands by default.

That is it about UTerm and its value.

In this topic
In this topic
Contact Us