Some basic STAF, with a simple example program

I’ve been playing with the Software Testing Automation Framework lately, and I thought I’d post some of my notes. This isn’t directly related to mobile games except for the fact that every type of software engineering requires testing. The root of STAF is an application that allows machines to control and monitor processes on other machines across a network (as peers instead of client/server). It has a rich set of features (logging, remote file system control, etc.) as well as APIs in multiple languages to allow you to write your own applications using STAF as a tool. To me it looks like this framework could be applied to lots of different distributed computing applications, but automated testing is what it’s designed and optimized for.

If you’re planning to use STAF but you’ve never used it before, here’s what I’d recommend to get started: Bookmark the documentation page and the User’s Guide. The User’s Guide has extensive helpful explanations on how to install STAF, then it contains some high-level discussion of the concepts followed by a detailed list of all of the commands and their options. To get a basic understanding of how STAF works and how to use it, read Getting Started With STAF V3, which includes some good explanation of how to use STAF as well as going over the demo. This blog post is intended as a supplement to the “Getting Started With STAF” tutorial. Here I’ll present an even simpler code example that shows how to interact with a process from the STAF command line.

Commands can be submitted to STAF either by a program (using APIs provided by STAF) or through the command line. Submitting commands from a program is more convenient in the long run — especially if your goal is automation. However, STAF commands submitted from within a program have the same basic structure as they do on the command line, so it’s useful to start by familiarizing yourself with the simpler example of just typing the commands directly before writing a program to do it for you. In what follows, I’ll assume you’ve already downloaded and installed STAF (using the links above), and it’s currently running on your machine (and you have Java installed as well). I’m doing this example from a Windows machine, but it’s undoubtedly the same or simpler on Unix/Linux.

From the command line, you start with the name of the STAF executable, and then the arguments form the command. The first argument is the machine you’d like to communicate with (I’ll stick with local here since using multiple instances and machines is covered thoroughly in the demo in “Getting Started With STAF”). The second argument is the name of the STAF service you’d like to call, the third argument is the name of the operation you’d like the service to perform, and the rest of the arguments are options and precisions. For example, suppose I’d like to call the help service. I would type the following:

STAF local help help

I get the following reply:

Response
--------
HELP Service Help

REGISTER SERVICE <Name> ERROR <Number> INFO <String> DESCRIPTION <String>

UNREGISTER SERVICE <Name> ERROR <Number>

[SERVICE <Name>] ERROR <Number>

LIST SERVICES | [SERVICE <Name>] ERRORS

HELP

It looks redundant to type help twice, but it isn’t. All of the actions that STAF offers are grouped into services, so you always need to select the service first and then tell the service what you want it to do. In the response above, STAF has listed for me all of the operations offered by the help service — that is, all of the ways I could finish a line that begins STAF local help. You can see that the one I chose is listed — the help operation is the last one on the list. Also note that STAF commands and other options are not case sensitive (though paths are).

To me, the most interesting basic thing that STAF can do is launch a process. This is done through the process service. To see what operations it offers, I start with a help query:

C:\>staf local process help
Response
--------
PROCESS Service Help

START [SHELL [<Shell>]] COMMAND <Command> [PARMS <Parms>] [WORKDIR <Directory>]
[VAR <Variable>=<Value>] [ENV <Variable>=<Value>] [USEPROCESSVARS]
[WORKLOAD <Name>] [TITLE <Title>] [WAIT [Timeout] | ASYNC]
[STOPUSING <Method>] [STATICHANDLENAME <Name>]
[NEWCONSOLE | SAMECONSOLE] [FOCUS <Background | Foreground | Minimized>]
[USERNAME <User Name> [PASSWORD <Password>]]
[DISABLEDAUTHISERROR | IGNOREDISABLEDAUTH]
[STDIN <File>] [STDOUT <File> | STDOUTAPPEND <File>]
[STDERR <File> | STDERRAPPEND <File> | STDERRTOSTDOUT]
[RETURNSTDOUT] [RETURNSTDERR] [RETURNFILE <File>]...
[NOTIFY ONEND [HANDLE <Handle> | NAME <Name>]
[MACHINE <Machine>] [PRIORITY <Priority>] [KEY <Key>]]

STOP <ALL CONFIRM | WORKLOAD <Name> | HANDLE <Handle>> [USING <Method>]

LIST [HANDLES] [RUNNING] [COMPLETED] [WORKLOAD <Name>] [LONG]
LIST SETTINGS

QUERY HANDLE <Handle>

FREE <ALL | WORKLOAD <Name> | HANDLE <Handle>>

NOTIFY REGISTER ONENDOFHANDLE <Handle> [HANDLE <Handle> | NAME <Name>]
[MACHINE <Machine>] [PRIORITY <Priority>]

NOTIFY UNREGISTER ONENDOFHANDLE <Handle> [HANDLE <Handle> | NAME <Name>]
[MACHINE <Machine>] [PRIORITY <Priority>]

NOTIFY LIST ONENDOFHANDLE <Handle>

SET [DEFAULTSTOPUSING <Method>] [DEFAULTCONSOLE <New | Same>]
[DEFAULTFOCUS <Background | Foreground | Minimized>]
[PROCESSAUTHMODE <Auth Mode>]
[DEFAULTAUTHUSERNAME <User Name>] [DEFAULTAUTHPASSWORD <Password>]
[DEFAULTAUTHDISABLEDACTION <Error | Ignore>] [DEFAULTSHELL <Shell>]
[DEFAULTNEWCONSOLESHELL <Shell>] [DEFAULTSAMECONSOLESHELL <Shell>]

HELP

Not bad. I’m tempted to just try starting an arbitrary process and stopping it. I’ll pick an executable that I know is there on my Windows machine such as notepad. From the above help information, I can see that I’d like to use the start operation and that it requires a command option followed by the command I want my system to execute:

C:\>staf local process start command notepad.exe
Response
--------
8

And a notepad miraculously opens up on my machine! (As cool as that is, it’s obviously that much funnier if you replace local with the address of a friend’s machine that also has the STAF daemon running.)

Now what is that 8 it gave me in response? That is the handle, which is the process ID within the STAF universe, used to interact with the process. I can use it to stop my process as follows:

C:\>staf local process stop handle 8
Response
--------

And the notepad disappears. The response is blank because STAF has nothing to say about that. But suppose I wanted to know the return code that the process ended with. I can query the handle:

C:\>staf local process query handle 8
Response
--------
Handle : 8
Handle Name : <None>
Title : <None>
Workload : <None>
Shell : <None>
Command : notepad.exe
Parms : <None>
Workdir : <None>
Focus : Background
User Name : <None>
Key : <None>
PID : 2496
Start Mode : Async
Start Date-Time: 20080311-10:18:32
End Date-Time : 20080311-10:19:07
Return Code : 4294967295

Wow, in addition to the return code, STAF gave a whole set of properties and values. This is standard for STAF, and the Java API for communicating with STAF naturally provides utilities for converting the return data to Map objects and vice-versa. While we’re on the subject, let’s create a simple Java program that can communicate with the STAF process after being launched by STAF. The “Getting Started With STAF” demo includes both sides of the equation — a process that launches another through STAF and the program that is launched through STAF. But in the interest of making this the simplest Java/STAF illustration possible, I’ve only written a program for STAF to launch, and I’ll communicate with it from the command line.

Here’s the code, in a file called STAFMiniProcess.java, placed in the directory C:\STAF\workshop\

import com.ibm.staf.*;
import java.util.*;

/**
* This class is a simple illustration of the STAF lifecycle.
* At the root, this code is based on open source code, therefore
* it is open source and is subject to the standard rules.
*/
public class STAFMiniProcess {
private STAFHandle handle = null;
private STAFResult result = null;
private String machine = "local";

public static void main(String[] args) {
try {
System.out.println("starting mini process");
STAFMiniProcess process = new STAFMiniProcess();
} catch(Exception e) {
e.printStackTrace();
}
}

public STAFMiniProcess() throws Exception {

try {
// register with STAF and get the process ID (handle)
handle = new STAFHandle("STAF_Mini");
} catch(STAFException e) {
System.out.println("Error, response code: " + e.rc);
terminate();
}

// Get the handle number from the handle object so
// that we can use it in commands directed at this process
int handleNum = handle.getHandle();
System.out.println("Handle #: " + handleNum);

// find out the STAF-related configuration information
// about the machine that's running this process.
// To submit a STAF command -- just as on the command
// line -- you specify the machine (here "local"), the
// name of the service (here "misc"), and the operation
// plus all of the options together as a single string:
result = handle.submit2(machine, "misc", "whoami");
System.out.println("Who am I? " + result.result);
String machineNickname = null;
// Use the marshalling context utilities to parse the
// properties and values returned by the STAF command:
if (STAFMarshallingContext.isMarshalledData(result.result)) {
STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
result.result);
Map map = (Map)mc.getRootObject();
Object[] keys = map.keySet().toArray();
for(int i =0; i < keys.length; i++) {
String value = (String)(map.get(keys[i]));
System.out.println("found " + keys[i] + " = " + value);
// save the machineNickname value for use in a later
// query command:
if("machineNickname".equals(keys[i])) {
machineNickname = value;
}
}
}

// Now loop indefinitely, querying for information from STAF
// about what to do next:
while (true) {
// Send information to STAF (for the benefit of other processes
// that are monitoring this process):
result = handle.submit2(machine, "MONITOR", "LOG MESSAGE \"WORKING\"");
System.out.println("monitor: " + result.result);

// check the queue for stop message:
result = handle.submit2(machine, "queue", "get type STAF/STAFMini/Stop");

if (result != null && result.rc == STAFResult.Ok) {
System.out.println("Received stop message: " + result.result);
break;
}

// check the value of the STAF variable "STAFMini/FunMessage"
// Stop the program if it's set:
result = handle.submit2(machine, "VAR", "resolve string {STAFMini/FunMessage}");

if (result != null && result.rc == STAFResult.Ok) {
System.out.println("Received message: " + result.result);
break;
}

// wait one second before looping again:
result = handle.submit2(machine, "DELAY", "DELAY 1000");
System.out.println("delay: " + result.result);
// to keep track of when each loop runs, query this process
// (by specifying both the machine and the handle since
// the handle is a machine-specific value):
result = handle.submit2(machine, "monitor", "query machine "
+ machineNickname + " handle " + handleNum);
if(STAFMarshallingContext.isMarshalledData(result.result)) {
STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
result.result);
Map map = (Map)mc.getRootObject();
// find the timestamp and print it:
System.out.println("timestamp = " + map.get("timestamp"));
}
}
// end the program once we've broken out of the loop:
terminate();
}

public void terminate() {
try {
if (handle != null) {
// Send information to STAF (for the benefit of other processes
// that are monitoring this process):
result = handle.submit2(machine, "MONITOR", "LOG MESSAGE \"DONE\"");
// unregister the handle so STAF is aware this process is done:
handle.unRegister();
}
} catch(STAFException e) {
} finally {
System.exit(0);
}
}
}

Now, compile this class, put the class file in a jar, and add the jar to your classpath. Then you can run it from STAF using the following command:

STAF local process start command java STAFMiniProcess ASYNC STDOUT c:\STAF\workshop\output STDERR c:\STAF\workshop\errput

Note in the above I’m being sloppy with capitalization (to emphasize that these commands aren’t case-sensitive). Additionally, to keep this example simple, I’m just using System.out.println() statements and having STAF redirect stdout to a file rather than using the log service. Instead, this example illustrates getting a handle, monitoring a process through the monitor service, setting and querying STAF variables, putting messages on the queue and then reading them from within the process, and marshalling/unmarshalling STAF data. That should be sufficient for a first try.

If you look in the code, you can see that I’ve set it up to read a variable called {STAFMini/FunMessage}. It just periodically checks whether any value at all has been set for this variable, and if so, the program ends. STAF variables have a bunch of different possible scopes, but for now I’d just like to set a value for this variable for this process only so that I can run the program again without having to delete the value of the variable. Thus, assuming that the handle returned when I launched the process was 76, I use the VAR service to set a value as follows:

staf local var set handle 76 var STAFMini/FunMessage=arbitraryValue

And the program stops. Now if I look in the output file I specified when launching the process, I notice that the handle number the STAFMiniProcess received when registering itself internally is the same as the handle number I got as a response when launching the process from the command line. This is the behavior I’d expect and hope for, but it’s interesting since processes that aren’t started by STAF can register themselves and get handles in the same way. That means that STAF is smart enough to figure out that this Java program that’s asking for a handle is the same process it just launched.

Another service I’m calling from inside the program is the monitor service. The program writes log messages to STAF which can be queried externally to see what the program is up to. So, assuming I’ve started it again and my handle number is now 78, I can access this message from the command line:

C:\>staf local monitor query machine andevah handle 78

Response
--------
Date-Time: 20080311-14:11:23
Message : WORKING

(In my STAF.cfg file I added a line to nickname my machine “andevah”.)

If you look in the code, you’ll see that each loop it checks the message queue for a message of type STAF/STAFMini/Stop, and this also breaks the loop and stops the program. Here’s the command to put such a message on the queue:

C:\>staf local queue queue handle 78 type STAF/STAFMini/Stop message DoesntMatterWhatTheValueIs
Response
--------

Now that I’ve stopped the process, I can check the monitor again:

C:\>staf local monitor query machine andevah handle 78
Response
--------
Date-Time: 20080311-14:12:49
Message : DONE

Now the message is DONE because that’s what the program writes to the monitor service just before terminating. In practice the monitor would be used for more detailed, granular, and specific information sent from the process. If all you’d like to know is whether the process is still going or whether it’s done, you can use the process query operation (as I did earlier in this post) and check whether an end time and return code are set.

That’s it for this simple example, and I hope it helps clarify the first steps in using STAF.

Advertisements

6 comments so far

  1. shankar on

    the program should be displayed with the result and proper explanation.

  2. shankar on

    the program should be typed fully and then the output should be displayed and the explanation should be given for the full program.

  3. Swati on

    Hey this one gives me a basic idea of STAF process command.
    But i need to execute a bat file with some parameters.How to send those parameters????I can see the help but i need an example how to use it…..
    Any help is really appreciated.

  4. Van on

    Very good, it helps me to know the STAF.
    This should be added in the STAF home page

  5. Jiten on

    This example was a cool one .. thanks for the effort which has been put.

    I do not know if really it needed the outputs to be shown here on the post… why someone would need them when Command line print statements are already listed.

    thanks a lot!

  6. victoria on

    Hi,
    Thanks for ur posting. i have a little bit knowledge on STAF server with java service. doubt on command parser.
    i command look like this
    **
    staf local my_service file “c:\1.txt” limit 10
    **
    read 1.txt file and display upto 10 lines.

    staf local my_service file “c:\1.txt”
    read 1.txt file and display all the content

    my issue is from the above command limit is optional for my_service. if limit present value should be given.
    how can i approach this..Please help me


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: