| OpenAMQ/1.2b1 - The ASL PAL System - The Protocol Automation Language

# Document Control

## Change History

1.2b0
  lets you reset the script clock.
1.2a1
  can now specify server to connect to.
1.2a0
  'Bounced' content renamed to 'Returned' content.
1.1d2
  Added -c option for performance statistics (message latencies, and throughputs).
1.1d2
  Content size argument was being ignored - fixed.
1.1d2
  Default target set to 'stdc'.
1.1d2
  was not correctly produced, fixed.
1.1d2
  Added progress indicator to repeat command.
1.1d1
  Added ability to add message headers to content produced by external application (similar to web CGI applications).
1.1d1
  Added command.
1.1d0
  Added -a option which allows full animation of the script.
1.1d0
  Added to allow variables to be set by command-line switches.
1.1d0
  Added -r option to repeat script specified number of times, which can be infinite.
1.1d0
  Added body_size variable, which can be used after a to check size of generated content.
1.1d0
  Added ability to run arbitrary programs (tested with Perl scripts).

# Overview

## Introducing PAL

And Base/2 begat ASL which begat PAL. PAL is a scripting system for ASL-based protocol clients. Mainly, we built PAL to make it simple to write test cases for OpenAMQ. There are several neat things about PAL. One: the scripting language is easy to use. Two: it can be turned into code in any programming language. Three: as AMQ evolves, the PAL scripting language automatically keeps up, since the PAL grammar (the stuff that defines what the scripting commands are) is produced automatically from the ASL specifications for AMQ.

## A Quick Start

Build Base/2 and OpenAMQ. In the asl/pal directory, you'll find a collection of PAL examples. Read these, and try them. To build and run a PAL script, use the 'pal' command.

## Jargon and Conventions

ASL
  Abstract Server Language - an iMatix technology for defining protocols and building clients and servers that implement such protocols. ASL is a language, a set of protocol standards, and a set of tools. ASL is itself constructed using XNF.
XNF
  XML Normal Form - an iMatix technology for defining XML grammars and building code generators that implement such grammars. XNF is a language, and a set of tools. XNF is constructed using XNF. The key point of XNF is to generate a full validating and denormalising parser and to couple this with hand-written back-end code generators (aka "targets").
GSL
  Generator Scripting Lanuage - an iMatix technology used to build code generators. GSL is a scripting language designed to work with tree-structured data (usually XML) and inject this data into a template-driven code generation process. All iMatix code generators are written in GSL, and most are complex enough to benefit from being constructed using XNF.
PAL
  Protocol Automation Language - an iMatix technology for writing scripts to automate the client half of a discussion carried out in a protocol built with ASL. PAL is a add-on to ASL.
PAL/P
  PAL implementation for a protocol called "P". Since PAL is not a single language but a class of languages, all built from the same basis, we call the actually languages "PAL/" plus the name of the prototol. Thus, "PAL/AMQ", "PAL/demo".
Session
  Another name for "channel". We use "channel" at the protocol level and "session" at the API level, mainly because "session" maps to existing APIs like JMS, while "channel" is common usage for multiplexing networking protocols such as ours.

# The Problem PAL Solves

## ASL Refresher

iMatix ASL - the Abstract Server Language - is a protocol and software-construction toolkit we built in order to help develop OpenAMQ. The concept of ASL is loosely based on the concept of formal grammars such as ASN.1, but in typical iMatix fashion is rather more aggressive in aiming to generate high-quality code and documentation.

ASL is a formal language, a grammar. It does not define the low-level wire-protocol but rather the higher-level language that a protocol carries. We describe this language in terms of "methods", grouped into "classes". The specific technology used to carry these methods depends on the specific implementation. Thus, ASL is abstract.

ASL is an extensible framework - a single ASL grammar can be fed to any number of different back-end "targets", each generating some different output. Today we have targets for:

- A Standard C implementation of client and server layers.
- A Java implementation of client layers.
- Documentation in iMatix gurudoc format.

This diagram shows the overall ASL code generation process:

     .----------------.
     |  ASL grammar   |         ASL grammar written as XML files, using
     | for protocol P |         inheritence and other XNF techniques.
     `----------------'
             :
    +------------------+
    |  ASL front-end   |        GSL parser, generated from asl.xnf.
    |      parser      |        using the iMatix XNF toolkit.
    +------------------+
             :
     .----------------.
     |  Denormalised, |         Data structure held in memory.
     | validated tree |
     `----------------'
             :
    +------------------+
    |     Specific     |        GSL code generator, written by hand.
    |  back-end target |
    +------------------+
             :
     .----------------.
     | Generated text |         Documentation or code as desired.
     |    outputs     |
     `----------------'

We can summarise the approach that drives ASL:

1. Define an abstract and high-level syntax capable of supporting the full functionality required by all possible protocols in our domain.
2. Implement the code generators for this abstract syntax.
3. Use the syntax to define our full protocol.

Note the major benefit of using ASL: writing a large and rich protocol is very cheap, as 100% of the protocol support both at the client and server side is generated, leaving only the functional implementation of the protocol methods as work for the programmer.

## Large-scale Testing

By making it cheap to design and implement functionality on top of a standard technical base, ASL also encourages very large and explicit protocols. While a hand-built protocol might use a single method for several purposes, ASL would encourage the definition of several individual methods. This clarity is very pleasant for the application developer, but it means that testing must also happen on a large scale.

This - cheap large-scale testing - is the problem that PAL solves.

## The PAL Architecture

These diagrams show how PAL fits into the toolchain. First, the process of constructing a PAL generator for a particular protocol, 'P':

          .----------------.
          |  ASL grammar   |         ASL grammar written as XML files, using
          |    "p.asl"     |         inheritence and other XNF techniques.
          `----------------'
                  :
         +------------------+
         |  ASL front-end   |        GSL parser, generated from asl.xnf.
         |      parser      |        using the iMatix XNF toolkit.
         |  "asl_gen.gsl"   |
         +------------------+
                  :
          .----------------.
          |  Denormalised, |         Data structure held in memory.
          | validated tree |
          `----------------'
                  :
         +------------------+
         |       PAL        |        GSL code generator, hand-made.
         |  back-end target |
         |   "asl_pal.gsl"  |
         +------------------+
            :           :
    +-----------+   +-----------+
    |  PAL XNF  |   |  PAL stdc |    GSL code generators, hand-made
    |  driver   |   |   driver  |
    +-----------+   +-----------+
          :               :
    .-----------.         :
    |   PAL/P   |         :          XNF grammar of PAL language
    |  grammar  |         :          specifically for protocol 'P'
    `-----------'         :
          :               :
    .-----------.   .-----------.
    | front-end |   |   stdc    |    Components of a PAL generator
    | for PAL/P |   |  target   |    specifically for protocol 'P'
    `-----------'   `-----------'

    gsl -target:pal p.asl
    gsl p_pal.xnf

Second, the process of turning a PAL script into executable code:

     .----------------.
     |  PAL/P script  |         PAL script written as XML files, using
     |  "example.pal" |         include and other XNF techniques.
     `----------------'
             :
    +------------------+
    |  PAL/P front-end |        GSL parser, generated from p.asl
    |      parser      |        (see previous diagram).
    | "p_pal_gen.gsl"  |
    +------------------+
             :
     .----------------.
     |  Denormalised, |         Data structure held in memory.
     | validated tree |
     `----------------'
             :
    +------------------+
    |       stdc       |        GSL code generator, generated from
    |  back-end target |        p.asl (see previous diagram).
    | "p_pal_stdc.gsl" |
    +------------------+
             :
     .----------------.
     |  Source code   |         Generated C test program.
     |  "example.c"   |
     `----------------'

To illustrate, these are the commands that would be used to perform the above chain, for a script called "example":

    gsl example.pal

The resulting test program is compiled and linked as usual.

# The PAL Language

PAL is not a single language but rather a combination of two things:

1. A standard set of control commands.
2. The classes and methods defined for the specific protocol in question.

The standard commands provide loops, if blocks, variables etc. The protocol method commands talk to the server.

## Some Sample PAL Scripts

As a basis for this tutorial we take the 'demo' protocol provided with ASL. Here is a sample PAL/demo script:

        Hello world!

Here is a script that demonstrates some of the standard PAL control commands:

                I can count up to $index

And an equivalent, shorter version:

            I can count up to $index

To connect to a server and open a session we use the control command. Here is a script that connects to a server and then displays the connection properties:

            version_major=$version_major
            version_minor=$version_minor
            channel_max=$channel_max
            frame_max=$frame_max
            heartbeat=$heartbeat
            context_key=$context_key
            server_product=$server_product
            server_version=$server_version
            server_platform=$server_platform
            server_copyright=$server_copyright
            server_information=$server_information

These are the connection properties for the demo protocol. Other protocols such as AMQ may have other or different properties. You will want to read the API documentation for the protocol to know what these are.

Note that the script does not specify what server to talk to, nor the IP port. These and other options are passed on the command-line. For the standard C PAL implementation run the script executable with "-h" to get a list of all options.

Having established a session we can send methods to the server:

                Message '$message_id' came back to us

                Message did not come back, this is bad!

PAL lets us define often-used method arguments at the 'session' level. These are then inherited to methods that don't explicity specify them. So we can rewrite the above script to make it shorter:

                Message '$message_id' came back to us

                Message did not come back, this is bad!

We can also create content bodies by reading data from test data files, or by running helper commands. See the 'read' and 'exec' options for the content commands. It's as simple as (for instance):

Scripts can be made flexible by passing arguments on the command line. Here is a simple example:

        Number=$number, string=$string

Which we can run with the options -N and -S:

    cmdline -N 9999 -S XXXX

Lastly let's look at macros, which are ways of collecting repetitive commands into groups to save time:

                Message '$message_id' came back to us

                Message did not come back, this is bad!

If you use macros to any extent you'll want to look at the command, described in the next section.

## The Standard PAL Commands

These are the basic scripting commands, which can be nested to form scripts of any complexity:

    invoke  - invoke a macro
    server  - start a protocol server
    set     - define or modify a variable
    inc     - increment a counter variable
    dec     - decrement a counter variable
    echo    - echo text to the console
    abort   - echo text to the console and abort the script
    assert  - assert some condition is true
    repeat  - repeat a loop some number of times
    while   - repeat a loop while some condition is true
    break   - exit a loop
    if      - execute commands if a condition is true
    else    - execute commands if the previous if condition was false
    elsif   - combined if and else
    wait    - wait for the server to return data
    sleep   - pause the script

### Basic Script Structure

The basic structure of the script is:

     ]...
        [
            [ script command ]...
           ]...
        [
            [ script command ]...
           ]...

- To write PAL scripts for the ASL demo protocol, use 'script = "demo_pal_gen"'.
- To write PAL scripts for the AMQ protocol, use 'script = "amq_pal_gen"'.

### The Include Command

The command copies the contents of another PAL file into the current script. It has this syntax:

- The filename must include the file extension. The included file should not have a level but may contain macros or script commands.

### The Macro Command

The command defines a block of commands that can be reused in as a single command in the script. It has this syntax:

        [ script command ]...

- Macros have no effect until they are used through the 'invoke' command.

### The Session Command

The command defines a session:

        [ script command ]...

- PAL may in future allow multiple sessions to be started in parallel, but for now sessions are executed serially. Each session will restart in a new connection, whatever the state of previous sessions.
- The servername can be used to test multiple servers in a single script. This option is not used for general-purpose scripts.
- If the failover is set to an integer greater than zero, on a broken connection the script will pause for the specified number of milliseconds, and then try to reconnect to the same or alternate server. To use alternate servers, specify multiple server names in the 'server' attribute, seperated by spaces.

### The Invoke Command

The command expands a macro:

- If the macro uses variables in commands, you can set these variables either before the command, or inside it, using commands.

### The Server Command

The commands starts or restarts a protocol server:

- Do not specify a file extension (.exe) or your scripts will not be portable.
- If a protocol server was already started, this command stops the server and then restarts it.
- Only one protocol server can be started at a time.
- The name value can include arbitrary server arguments but not shell redirection commands.
- To redirect the server's output, use the stdout and stderr options.

### The Timer Command

The commands shows or resets the script timer.

- The action is optional and defaults to "reset".

### The Set Command

The command defines a variable. Variables can be strings or integers. You can use variables in repeat, while, and if blocks, and as symbols for templating arguments and strings. Untyped variables are typed according to their value.

- The value is optional, and defaults to "".
- If the value is purely numeric, the type will default to "integer", and if not the type will default to "string".
- The cmdline option specifies a single character. Do not use one of the command-line options already used by the PAL implementation (see section at the end of this document).

### The Inc Command

The command increments an integer variable:

### The Dec Command

The command decrements an integer variable:

- Decrementing a variable below zero is illegal and raises a fatal error. This is done to catch script errors - negative values are normally not meaningful in test scripts.

### The Random Command

The command sets a variable to a random value within a specified range:

- The minimum is optional, and defaults to zero.

### The Read Command

The command accepts a line of input from the console and assigns this to a variable:

- The prompt is optional; if defined, this will be shown to the user (with no newline) before the console waits for input.

### The Echo Command

The command echoes a line of text:

    line of text

- The text can use variables with the syntax: $variablename.
- The trace level set using a command-line switch. Use the help option (-h) on the test program for details.

### The Assert Command

The command tests a condition and aborts the script if the condition is false.

    [line of text]

- The variablename is a script variable, or a connection or session property, or a standard PAL variable.
- If the test and value are not specified, they default to "ne" and "0" or "" depending on the type of variable.
- If just the test is not specified, it defaults to "eq".
- If the assert statement includes a message, this is printed before an assertion failure.

### The Repeat Command

The command defines an iterative loop, which can run forever or for a specified number of times. The counter is global (do not use the same counter for two nested loops). To access the counter within the repeat loop, use $variablename.

        [ script command ]...

- If the times attribute is not specified, the loop will run forever or until the script does a .
- The counter does not need to be previously defined. If no counter is specified, the repeat loop will create its own internal counter which cannot then be used as a symbolic value.
- If the progress option is set to an integer N, then after every N passes through the loop, the test script will print a dot to the standard error output.

### The While Command

The command defines a conditional loop, which runs so long as a specified condition is true:

        [ script command ]...

- See the command for an explanation of the test and value properties.
- If a counter is specified, this variable is automatically set to zero when the while loop starts and incremented each time the loop runs. You can access the counter variable after the while loop.
- If the progress option is set to an integer N, then after every N passes through the loop, the test script will print a dot to the standard error output.

### The Break Command

The command exits the enveloping repeat or while loop and has this syntax:

The command defines a block that is executed if a specific condition is true:

        [ script command ]...

- See the command for an explanation of the test and value properties.

### The Else Command

The command defines a block that is executed if the previous condition was false:

        [ script command ]...

### The Elsif Command

The command defines a block that is executed if the previous condition was false and some further condition is true:

        [ script command ]...

- See the command for an explanation of the test and value properties.

### The Wait Command

The command pauses the script for a number of milliseconds, or until content is received from the server, whichever is sooner:

- Inside a session the default timeout is 'forever'. Outside a session, the default timeout is 'zero'.

Here is an example of using the command:

        Waiting without an active connection...

            Waiting inside an active connection...

        OK

### The Sleep Command

The command pauses the script for an exact number of milliseconds. Unlike it's cousin , the sleep time is not affected by any traffic on the session.

- Timeout is required for .

### The Abort Command

The command echoes a line of text and halts the script.

    line of text

- The text can use variables with the syntax: $variablename.

### The Exit Command

The command halts the script.

- The default status value is 0.

## Protocol-Specific PAL Commands

### An Example

This script sends 10 messages to the server and then reads them back. It uses the simple browse commands - not asynchronous consumers - and is specific to the demo protocol (using AMQ one would probably use consumers and commands to get messages):

                    Returned: $message_id

                    Arrived: $message_id, numbering=$headers-Numbering

            Total number of messages exchanged: $count

### General Principles

ASL protocols have the useful property of being very high-level. That is, the protocol methods generally need little or no abstraction to be immediately obvious and useful to application developers. This makes it reasonable in PAL to simply expose the protocol methods directly to the scripting language. This strategy is helped by:

- The use of clear and consistent names for methods and method properties.
- The use of intelligent defaults for optional properties.

ASL protocols share the same connection and channel initiation and tear-down architecture. The methods used to do this - such as Connection.Tune - are hidden from the PAL developer and are not exposed in the PAL script language. Specifically, we hide:

- All Connection class methods.
- The Channel.Open and Close methods.
- All methods sent by the server and received by the client. Since PAL is for client-side automation, these cannot be scripted.

### Content Commands

For the purposes of explanation we will use the 'demo' protocol that is part of the ASL package. The demo protocol defines one content class, "basic".

For each content class, PAL provides a command to create the content and set its properties. E.g.

        [
          [  ]...
         ]
      [ content text ]

- The size attribute specifies the size in octets of the content buffer. Its default value is "1024".
- The fill attribute specifies the fill mode. It can be "random", which sets the body to random data, or "null", which sets it to binary zeroes, or "repeat", which repeats the content text up to the specified size.
- The body of the content item optionally provides a content text. If this is set, it's reformatted as a single line of text, and used as message body. This overrides the default fill ('random').
- The read attribute specifies a file from which to read the content body. This is useful when you want to send test messages with a specific format.
- The exec attribute specifies a command to run, so that the stdout of the command can be used as the content body. The command must be the name of an executable program, on the path, with arguments as desired. The program receives the current content body as stdin, much like a web server CGI program.
- If the headers field is set to zero, the output of the executed program is not reparsed. If one, the output is reparsed to collect message properties and headers as follows: each line specifies a header field name, followed by ":", followed by a space and a value. Field names starting with "x-table-" are stored as-is (minus the x-table- prefix) in a field table with that name. Other fields must match known content properties. Hyphens are allowed in field names, and field names are case-insensitive. The headers are ended with a blank line. Parsed headers create a CGI-like interface for calling programs.
- For each content property defined in the protocol (except field tables) PAL defines an attribute for the content command.
- For field tables, PAL defines a child entity with the same name, e.g. 'headers'. Field tables are then constructed from one or more definitions.
- After a content command, the script can access the content body size as a variables ($body_size in expressions, or body_size in assertions and conditions).

### Protocol Method Commands

A protocol method command sends a protocol method to the server. If the method is a synchronous method, the script waits for a response from the server. If the method is asynchronous, the script continues without waiting. The basic syntax for protocol method commands is:

          [  ]...

Properties that are not specified take a default value, which is zero for numeric properties, FALSE for Boolean properties, and NULL for strings and field tables.

### Processing Arrived Content

For each content class, PAL provides a command that lets you process arrived messages. Contents do not necessarily arrive in a strict synchronous order - it depends on the protocol - so this command acts as a loop, and repeats for each arrived content at the moment it is invoked.

        [ script command ]...

        [ script command ]...

- If a counter is specified, this variable is automatically set to zero when the loop starts and incremented each time the loop runs. You can access the counter variable after the loop.
- If there was no arrived content, the script executes the following command, if any.

You can use these variables within an arrived loop:

- $body_size - size of content body.
- $body_text - content body as printable text.
- $exchange - original exchange to which content was sent.
- $routing_key - routing key specified in content.
- $producer_id - original producer id.

### Processing Returned Content

We process returned content in a similar way to arrived content:

        [ script command ]...

        [ script command ]...

- If a counter is specified, this variable is automatically set to zero when the loop starts and incremented each time the loop runs. You can access the counter variable after the loop.
- If there was no arrived content, the script executes the following command, if any.

### Synchronous Content Processing

PAL does not provide any asynchronous content processing. The script runs as a single-threaded procedure from start to end. Content will arrive when the script is busy, i.e. during any command that talks to the server. To process content after such commands, use the 'arrived' commands. To process content while not doing such commands, use and then use the arrived command.

## PAL Variables

PAL uses the convention '$name' to allow variable substitution. This is allowed in:

- The body of and commands.
- All attributes except variablenames.

PAL defines all connection and session properties as variables. The API documentation for the protocol you are using will list these. For the demo protocol they are:

- Connection properties:

    $channel_max
    $class_id
    $context_key
    $frame_max
    $heartbeat
    $method_id
    $reply_code
    $reply_text
    $server_copyright
    $server_information
    $server_platform
    $server_product
    $server_version
    $version_major
    $version_minor

- Session properties:

    $active
    $class_id
    $consumer_count
    $routing_key
    $exchange
    $message_count
    $method_id
    $queue
    $reply_code
    $reply_text
    $ticket

Note that the standard ASL technique for returning values from protocol methods is via the session properties. Thus the variable 'message_count' holds the number of messages after a queue.browse request and a queue.browse-ok response.

You should avoid using your own variables that conflict with the standard connection and session variables.

PAL defines these built-in variables:

- $script - name of current PAL script.
- $connection - 1 if the connection is alive, else 0.
- $session - 1 if the session is alive, else 0.
- $random - a random integer in the range 0..32767, when used as an insertion value, produces a 4-digit hex string.
- $body_size - the body size of the last content to be created, arrived, or returned.

PAL resolves a variable reference in this order:

1. First, in-built variables.
2. Content properties, inside an arrived/returned loop.
3. Session properties.
4. Connection properties.
5. Script variables and counters.

Here is a sample script that demonstrates various ways of using variables:

            Connected to $server_product/$server_version - $server_platform

                    Returned: $message_id

                    Arrived: $message_id

            Total number of messages exchanged: $count

# PAL Implementations

## The StdC PAL Implementation

### Command-Line Arguments

The standard C implementation creates a command-line program that accepts these arguments:

    Syntax: program [options...]
    Options:
      -s server        Server:port to connect to (localhost)
      -t level         Set trace level (default = 0)
                       0=none, 1=low, 2=medium, 3=high
      -r count         Repeat script count times, 0=forever (1)
      -c               Clock the script (0)
      -a               Animate: show script commands
      -e               Execute in single-step mode (0)
      -q               Quiet mode: no messages
      -v               Show version information
      -h               Show summary of command-line options
    The order of arguments is not important. Switches and filenames
    are case sensitive. See documentation for detailed information.

### Performance Measurements

The -c option clocks the script and produces performance measurement output. Here is an example of a simple stress test script:

        This script sends a large number of messages to a queue and then
        reads them back.  The number of messages can be specified on the
        command-line.

Which produces this output (the figures are obviously just an example):

    ...........
    ...........
    16:41:26: I: elapsed time:781 msecs
    16:41:26: I: outgoing messages:1000 (976 Kbytes)
    16:41:26: I: incoming messages:1000 (976 Kbytes)
    16:41:26: I: total messages:2000 (1952 Kbytes) average:2560/sec (2499 Kbytes/sec)
    16:41:26: I: message latency min=280 max=410 mean=331 dev=37 msecs

## Other Implementations

There are no other implementations of PAL at present.