Guide 3: Integration Overview for Telnet Service Syntax-Tree Example (3)

Overview

The Telnet Service is a library that provides easy-to-integrate TELNET functionality to Java(TM) applications. Althrough its main purpose is to integrate servers as an administration console, its versatility puts creativity as the only limitation to its usage.

The Syntax-Tree extension provides an abstraction over the command syntax by providing an easy way to describe the operation structures.

Introduction

This example is based on 'Example 1: Telnet Service Core Simple Example Step-by-Step' and extends its functionality by adding JAAS authentication (using the telnetservice-jaas library).

The following sections show the basics of this integration activity using a simple (trivial) example.

It is therefore recommended to read those examples, check the 'Related Documentation' section below.

Steps To Add Syntax Tree Support to Your Telnet Service Enabled Application

The telnetservice-syntaxtree extension provides an API to construct a parse tree for low-level syntax recognition (details not included in this document). It also provides an easy way to define those commands using Java Annotations, as shown below.

As with the previous example, we will extend Example1 and overload its initialize() method to register the desired extension. We will also use some of its exposed API methods to customize it.

 public class Example3 extends Example1
{
    public void initialize() {
        this.serviceState = new ServiceState();

        this.telnetService = new TelnetService();
        this.telnetService.setBindPort(2323);

        TelnetServiceExtensionSyntaxTreeImpl extension =
            new TelnetServiceExtensionSyntaxTreeImpl();        

Note that we did not call the super.initialize() , this is because we do not want the default extension defined in Example1 to be registered.

The beans Example3BeanA and Example3BeanB are pojos that hold our business logic, or delegate to parts of our system that do. We will cover those in a bit.

        extension.registerBeanOperations(new Example3BeanA());
        extension.registerBeanOperations(new Example3BeanB());

The syntax tree is basically a state machine with a cursor that positions the current session state in that tree. Two main kinds of transitions: 'contextual' and 'global'. A few most commonly used commands are provided with the extension package (fr.gedeon.telnetservice.syntaxtree.operations ), we'll use some of them here:

        extension.getBaseSyntaxTree().addGlobalTransition(new HelpOperationTransition());
        extension.getBaseSyntaxTree().addGlobalTransition(new QuitOperationTransition());
        extension.getBaseSyntaxTree().addGlobalTransition(new ExitOperationTransition());
        extension.getBaseSyntaxTree().addGlobalTransition(new ExitAllOperationTransition());

        super.telnetService.registerExtension(extension);
    }
    
    public static void main(String[] args) throws TelnetServiceStartException {
        Example3 example = new Example3();
        example.initialize();
        example.run();
    }
}

Selecting the Target Beans

The telnetservice-syntaxtree library is planned to provide different ways to interface with target business logic beans. The currently available way is to declare their syntax by annotating them (or create annotated wrapper to them).

The following two beans are used for the example:

Annotating a bean for exposure to the telnetservice interface consists of specifying which of its methods are to be exposed as operations, where those operations would be placed in the syntax tree and what name they should hold (as well as a textual description that the interface would be able to show in a help listing, for example). Also, for exposed methods, the parameters are annotated to provide the interface with the name each parameter is to hold when it's exposed as an attribute of the operation, as well as an optional description of that parameter.

As an example, Example3BeanA would expose two operations:

  • hello that maps to the helloOperation method, and
  • + that maps to the add method.

    Looking at Example3BeanA method helloOperation :

        @BeanOperation(
            path = {"example3", "beanA"},
            name = "hello",
            description = "the description of the operation"
        )
        public String helloOperation(
                        @BeanOperationParameter(
                            name="name",
                            description="the name to greet"
                        )String name
        ) {
            return "Hello, " + name + "!";
        }

    The path attribute of the @BeanOperation annotation is a String[] that specifies the contextual location of this operation in the syntax tree. In this example the operation would sit under /example3/beanA/ . The parts of this path can be virtually any string that does not contain whitespace characters.

    The optional attribute of the @BeanOperationParameter annotation is not shown above, it's defaulted to false .

Going For a Test Run

Compile, run Example3:

/> help
    example3 : Go to sub-context example3

    help : Displays this help listing. Also use <filter>? to limit help items to those starting with <filter>
    quit : Quit this session

/> example3

? is a shortcut for help

/example3/> ?
    B : Go to sub-context B
    beanA : Go to sub-context beanA
    op : return void

    exit : Exit current context
    help : Displays this help listing. Also use <filter>? to limit help items to those starting with <filter>
    quit : Quit this session

/example3/> beanA
/example3/beanA/> ?
    + : add 2 numbers
    hello : the description of the operation

    exit : Exit current context
    help : Displays this help listing. Also use <filter>? to limit help items to those starting with <filter>
    quit : Quit this session

/example3/beanA/> hello
/example3/beanA/hello/
> ?
    cancel : Cancel the operation
    name : the name to greet

    help : Displays this help listing. Also use <filter>? to limit help items to those starting with <filter>
    quit : Quit this session

/example3/beanA/hello/
> name

> ?
    <value> : string
    cancel : Cancel the attribute value assign

    help : Displays this help listing. Also use <filter>? to limit help items to those starting with <filter>
    quit : Quit this session

/example3/beanA/hello/name/
> "Mr. W"

The prompt shows the state of the call as it progresses. Once all mandatory attributes have been set, the call operation becomes enabled.

/example3/beanA/hello/name
    name<--Mr. W
> ?
    call : Call the operation
    cancel : Cancel the operation

    help : Displays this help listing. Also use <filter>? to limit help items to those starting with <filter>
    quit : Quit this session

/example3/beanA/hello/
    name<--Mr. W
> call
Hello, Mr. W!

/example3/beanA/> quit

Note that the command was typed a step at a time for the sake of the example, the same effect can be achieved with a one-liner:

/> example3 beanA hello name "Mr. W" call
Hello, Mr. W!

Also note that the commands can be made case insensitive by setting that flag to true in the action handler.

Download the Example Source

The complete example sources can be downloaded at the project download section .