Sunday, June 1, 2008

Creating a simple AIR/Flex UI for a Snobol program

This post presents a little experiment for communicating an Snobol program with an AIR/Flex interface using HTTP.

This post is inspired by the Put a Flex UI On Your Application article by Bruce Eckel.

The example

The example that will be presented is a simple form showing sudo attempts recorded in the /var/log/auth.log log in a Linux box.

A simple program written in Snobol4 using CSnobol4 is used to extract the entries from auth.log . A AIR/Flex program is used to display the data. Both programs are communicated using HTTP.

Although there are several ways to communicate a AIR application with a server side element, HTTP was chosen because of its simplicity to implement in Snobol.

Simple HTTP in Snobol

A way to answer simple HTTP GET method requests from Snobol was required. This requires the creation of a server socket to answer requests. Luckly CSnobol4 includes a nice example for creating a server socket (snolib/serv.sno) using the SERV_LISTEN function. Having this element it was very simple to implement the GET method support.


serverPort = 8080
...
SLOOP FD = SERV_LISTEN("inet", "stream", serverPort) :F(LERR)

INPUT(.NET, 9, "UWT", "/dev/fd/" FD) :F(IERR)
OUTPUT(.NET, 9)

OUTPUT = "Accepting request "

LINE = NET

LINE "GET " GetStringPat $ getString " HTTP/" NUMBER "." NUMBER :F(LERR)

getString ARB "username=" ARB $ requestedUserName ("&" | RPOS(0))

OUTPUT = "Requesting SUDOS for user: " requestedUserName

INPUT(.LOGFILE,10,,"/var/log/auth.log")

NET = "HTTP/1.1 200 OK" CRLF
NET = "Server: SNOBOL4/1.1 (Linux)" CRLF
NET = "Content-Type: text/xml" CRLF
NET = CRLF


Extracting the data

The data extraction process is presented in the following code.


LetterU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LetterL = "abcdefghijklmnopqrstuvwxyz"
Digit = "0123456789"
DirectorySeparator = "/"
GetQuerySeparator = "?"
EscapeChar = "%.&="

GetStringChar = LetterU LetterL Digit DirectorySeparator EscapeChar GetQuerySeparator

GetStringPat = SPAN(GetStringChar)

...



NET = "<sudosdata>"

&ANCHOR = 1
LETTER = LetterU LetterL
USERNAMECHAR = Digit LetterU LetterL
USERNAMEPAT = SPAN(USERNAMECHAR)


READLINE LINE = LOGFILE :F(DONE)
LINE SPAN(LETTER) $ month SPAN(" ") SPAN(Digit) $ day ARB " sudo: " USERNAMEPAT . USER :F(READLINE)
LINE ARB "COMMAND=" ARB . COMMAND RPOS(0)

USER requestedUserName :F(READLINE)

NET = "<sudo><date>" month " " day "</date><user>" USER "</user><command>" COMMAND "</command></sudo>" :(READLINE)

DONE

NET = "</sudosdata>" :S(END)


The answer is formated as XML.

The Interface Code

The interface is a very simple program containing a text input to filter the query for a specify user. The useful HTTPService component is used to get the data from the Snobol program.

The Flex part of the program is the following:


<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
title="Sudos Test">
<mx:Script>
<![CDATA[
import flash.utils.Dictionary;
private function callQuery():void {
request.send();
}
]]>
</mx:Script>
<mx:HBox>
<mx:Label text="Sudos" />
<mx:TextInput id="userName" />
</mx:HBox>
<mx:Button label="Query!" click="callQuery()"/>
<mx:DataGrid id="data" dataProvider="{request.lastResult.sudosdata.sudo}">
<mx:columns>
<mx:DataGridColumn headerText="date" dataField="date"/>
<mx:DataGridColumn headerText="user" dataField="user"/>
<mx:DataGridColumn headerText="command" dataField="command"/>
</mx:columns>
</mx:DataGrid>

<mx:HTTPService id="request" url="http://localhost:8080"
useProxy="false"
method="GET">
<mx:request xmlns="">
<username>{userName.text}</username>
</mx:request>
</mx:HTTPService>
</mx:WindowedApplication>


How it looks

An example of running these programs is the following:



Code for this post can be found here.