The PAL LanguagePAL is not a single language but rather a combination of two things:
The standard commands provide loops, if blocks, variables etc. The protocol method commands talk to the server. Some Sample PAL ScriptsAs a basis for this tutorial we take the 'demo' protocol provided with ASL. Here is a sample PAL/demo script: <?xml?> <pal name = "hello" script = "demo_pal_gen" target = "stdc" > <echo> Hello world! </echo> </pal> Here is a script that demonstrates some of the standard PAL control commands: <?xml?> <pal name = "loop1" script = "demo_pal_gen" target = "stdc" > <set name = "index" value = "0" /> <repeat> <inc name = "index" /> <if name = "index" value = "10"> <break/> </if> <else> <echo>I can count up to $index</echo> </else> </repeat> </pal> And an equivalent, shorter version: <?xml?> <pal name = "loop2" script = "demo_pal_gen" target = "stdc" > <repeat times = "10" counter = "index"> <echo>I can count up to $index</echo> </repeat> </pal> To connect to a server and open a session we use the <?xml?> <pal name = "session" script = "demo_pal_gen" target = "stdc" > <session> <echo>version_major=$version_major</echo> <echo>version_minor=$version_minor</echo> <echo>channel_max=$channel_max</echo> <echo>frame_max=$frame_max</echo> <echo>heartbeat=$heartbeat</echo> <echo>context_key=$context_key</echo> <echo>server_product=$server_product</echo> <echo>server_version=$server_version</echo> <echo>server_platform=$server_platform</echo> <echo>server_copyright=$server_copyright</echo> <echo>server_information=$server_information</echo> </session> </pal> 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: <?xml?> <pal name = "single" script = "demo_pal_gen" target = "stdc" > <session> <exchange_declare exchange = "myexchange" class = "fanout" /> <queue_declare queue = "myqueue" scope = "default" /> <queue_bind queue = "myqueue" scope = "default" exchange = "myexchange" /> <basic_content size = "64000" message_id = "id-0001" /> <basic_publish exchange = "myexchange" routing_key = "myqueue" /> <basic_browse queue = "myqueue" scope = "default" /> <basic_arrived> <echo>Message '$message_id' came back to us</echo> </basic_arrived> <empty> <echo>Message did not come back, this is bad!</echo> </empty> </session> </pal> 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: <?xml?> <pal name = "single2" script = "demo_pal_gen" target = "stdc" > <session exchange = "myexchange" queue = "myqueue" scope = "default" > <exchange_declare class = "fanout" /> <queue_declare /> <queue_bind /> <basic_content size = "64000" message_id = "id-0001" /> <basic_publish routing_key = "myqueue" /> <basic_browse /> <basic_arrived> <echo>Message '$message_id' came back to us</echo> </basic_arrived> <empty> <echo>Message did not come back, this is bad!</echo> </empty> </session> </pal> 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): <basic_content exec = "perl -S myprog.pl" /> Scripts can be made flexible by passing arguments on the command line. Here is a simple example: <?xml?> <pal name = "cmdline" script = "demo_pal_gen" target = "stdc" > <set name = "number" value = "1234" cmdline = "N" /> <set name = "string" value = "abcd" cmdline = "S" /> <echo>Number=$number, string=$string</echo> </pal> 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: <?xml?> <pal name = "macros" script = "demo_pal_gen" target = "stdc" > <macro name = "queue new"> <exchange_declare exchange = "stdqueue" class = "fanout" /> <queue_declare queue = "$queue" scope = "default" /> <queue_bind queue = "$queue" exchange = "stdqueue" /> </macro> <macro name = "send message"> <basic_content size = "$size" message_id = "id-$random" /> <basic_publish exchange = "stdqueue" routing_key = "$queue" /> </macro> <session> <set name = "queue" value = "myqueue" /> <invoke macro = "queue new" /> <invoke macro = "send message"> <set name = "size" value = "64000" /> </invoke> <basic_browse queue = "myqueue" scope = "default" /> <basic_arrived> <echo>Message '$message_id' came back to us</echo> </basic_arrived> <empty> <echo>Message did not come back, this is bad!</echo> </empty> </session> </pal> If you use macros to any extent you'll want to look at the The Standard PAL CommandsThese 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 StructureThe basic structure of the script is: <pal name = "scriptname" script = "palscript" target = "stdc" [ <include filename = "filename" /> ]... [ <macro name = "macroname"> [ script command ]... </macro> ]... [ <session> [ script command ]... </session> ]... </pal>
The Include CommandThe <include filename = "scriptfile" />
The Macro CommandThe <macro name = "macroname"> [ script command ]... </macro>
The Session CommandThe <session [ server = "servername" ] [ failover = "msecs" ] > [ script command ]... </session>
The Invoke CommandThe <invoke macro = "macroname" />
The Server CommandThe <server name = "servername" [ stdout = "filename" ] [ stderr = "filename" ] [ where = "directory" ] />
The Timer CommandThe <timer [ action = "show | reset" ] />
The Set CommandThe <set name = "variablename" [ value = "newvalue" ] [ type = "string | integer" ] cmdline = "char" />
The Inc CommandThe <inc name = "variablename" /> The Dec CommandThe <dec name = "variablename" />
The Random CommandThe <random name = "variablename" [ min = "minvalue" ] max = "maxvalue" />
The Read CommandThe <read name = "variablename" [ prompt = "promptstring" ] />
The Echo CommandThe <echo [trace = "1|2|3"]>line of text</echo>
The Assert CommandThe <assert name = "variablename" [ test = "eq | ne | gt | lt | ge | le" ] [ value = "testvalue" ] >[line of text]</assert>
The Repeat CommandThe <repeat [ counter = "variablename" ] [ times = "integer" ] [ progress = "integer" ] > [ script command ]... </repeat>
The While CommandThe <while name = "variablename" [ test = "eq | ne | gt | lt | ge | le" ] [ value = "testvalue" ] [ counter = "variablename" ] [ progress = "integer" ] > [ script command ]... </while>
The Break CommandThe <break/> The <if name = "variablename" [ test = "eq | ne | gt | lt | ge | le" ] [ value = "testvalue" ] > [ script command ]... </if>
The Else CommandThe <else> [ script command ]... </else> The Elsif CommandThe <elsif name = "variablename" [ test = "eq | ne | gt | lt | ge | le" ] [ value = "testvalue" ] > [ script command ]... </elsif>
The Wait CommandThe <wait [ timeout = "milliseconds" ] />
Here is an example of using the <?xml?> <pal name = "waiting" script = "demo_pal_gen" target = "stdc" > <set name = "index" value = "0" /> <echo>Waiting without an active connection...</echo> <wait timeout = "1000" /> <session> <echo>Waiting inside an active connection...</echo> <wait timeout = "1000" /> </session> <echo>OK</echo> </pal> The Sleep CommandThe <sleep timeout = "milliseconds" />
The Abort CommandThe <abort>line of text</abort>
The Exit CommandThe <exit [status = "value"] >
Protocol-Specific PAL CommandsAn ExampleThis 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 <?xml?> <pal name = "content" script = "demo_pal_gen" target = "stdc" > <session queue = "test-queue" exchange = "test-exchange" scope = "test"> <exchange_declare class = "fanout" /> <queue_declare /> <queue_bind> <arguments> <field name = "currency" value = "USD" /> <field name = "symbol" value = "MSFT" /> </arguments> </queue_bind> <repeat times = "10" counter = "id"> <basic_content size = "64000" content_type = "text/html" message_id = "id-$random"> <headers> <field name = "Numbering" value = "$id" /> <field name = "Max-items" value = "100" /> <field name = "Server-name" value = "http://www.openamq.org" /> </headers> </basic_content> <basic_publish routing_key = "test-queue" /> </repeat> <repeat> <basic_browse /> <basic_returned> <echo>Returned: $message_id</echo> </basic_returned> <basic_arrived> <inc name = "count" /> <echo>Arrived: $message_id, numbering=$headers-Numbering</echo> </basic_arrived> <empty> <break/> </empty> </repeat> <echo>Total number of messages exchanged: $count</echo> </session> </pal> General PrinciplesASL 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:
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:
Content CommandsFor 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. <basic_content [ size = "bodysize" ("1024") ] [ fill = "random | null | repeat" ("random") ] [ read = "..." ] [ exec = "..." ] [ headers = "0|(1)" ] [ content_type = "propertyvalue" ] [ content_encoding = "propertyvalue" ] [ message_id = "propertyvalue" ] > [ <headers> [ <field name = "fieldname" [ value = "fieldvalue" ] [ type = "string | integer" ("string") ] /> ]... </headers> ] [ content text ] </basic_content>
Protocol Method CommandsA 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: <class_method [properties...]> <field_table_name> [ <field name = "fieldname" [ value = "fieldvalue" ] [ type = "string | integer" ("string") ] /> ]... </field_table_name> </class_method> 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 ContentFor 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. <basic_arrived [ counter = "variablename" ] > [ script command ]... </basic_arrived> <empty> [ script command ]... </empty>
You can use these variables within an arrived loop:
Processing Returned ContentWe process returned content in a similar way to arrived content: <basic_returned [ counter = "variablename" ] > [ script command ]... </basic_returned> <empty> [ script command ]... </empty>
Synchronous Content ProcessingPAL 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 PAL VariablesPAL uses the convention '$name' to allow variable substitution. This is allowed in:
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:
$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
$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:
PAL resolves a variable reference in this order:
Here is a sample script that demonstrates various ways of using variables: <?xml?> <pal name = "symbols" script = "demo_pal_gen" target = "stdc" > <set name = "expect_major" value = "9" /> <set name = "exchange_class" value = "fanout" /> <set name = "scope" value = "test" /> <set name = "queue" value = "$scope\\-queue" /> <set name = "exchange" value = "$scope\\-exchange" /> <set name = "times" value = "100" /> <set name = "size" value = "64000" /> <session queue = "$queue" exchange = "$exchange" scope = "$scope"> <echo>Connected to $server_product/$server_version - $server_platform</echo> <assert name = "version_major" value = "$expect_major" /> <exchange_declare class = "$exchange_class" /> <queue_declare /> <queue_bind /> <repeat times = "$times" counter = "id"> <basic_content size = "$size" content_type = "text/html" message_id = "id-$id" /> <basic_publish routing_key = "$queue" /> </repeat> <repeat> <basic_browse /> <basic_returned> <echo>Returned: $message_id</echo> </basic_returned> <basic_arrived> <inc name = "count" /> <echo>Arrived: $message_id</echo> </basic_arrived> <empty> <break/> </empty> </repeat> <echo>Total number of messages exchanged: $count</echo> </session> </pal>
|
iMatix Corporation |