quExec by example

Introduction

This document shall help you to get familiar with quExec by walking through some examples that can easily be reproduced in your environment with the binary distribution of quExec. The sample cases are also used for release tests of quExec and can be applied to check the proper integration of quExec in a new runtime environment (e.g. when changing the JMS broker software).

So far the samples only refer to scenarios where quExec is used by running the net.sourceforge.quexec.OSCommandExecutor at the command-line, even though quExec is primarily intended to be used through its API as a library for Java applications. Nevertheless, the examples show you directly how quExec works and the code used by OSCommandExecutor can be taken as a blueprint for your accessing the quExec API in your own applications.

How to run the examples

Start script and environment

The following descriptions assume that you use the start script

  bin/quExec-test.sh <tag> <arguments to OSCommandExecutor>

to execute the tests. This script only uses dependencies that are bundled with quExec. In particular, it is based on a simple embedded ActiveMQ message broker.

If you want to use your own execution environment (JMS broker, JNDI), you can use the script bin/quExec.sh and setup your environment ($CLASSPATH) to point to all necessary resources. Adapting the start script to your specific environment should also be a very simple task.

bin/quExec-test.sh only works in a Unix environment. Writing a similar Windows script should be easy. However, since quExec is currently developed under Linux and is thus not regularly tested under Windows, such a script is not provided as part of the official quExec release.

The tests assume that the commands are run in the root directory of the quExec distribution.

Generated log files

quExec uses Jakarta Commons Logging and quExec-test.sh makes use of Log4J. The standard log4j.properties file that is bundled with quExec shows some messages (level INFO and above) at the command-line. Debug output that contains valuable information for identifying errors is printed to a file named quexecTAG.log.

The tag part is variable and can be defined through the system property quexec.tag such that multiple (possibly parallel) runs of quExec-test.sh do not write to the same log file. quExec-test.sh takes the tag as first argument.

quExec examples

Running simple commands

Of course, you would not need quExec to run a command locally on your computer. However, the following examples provide a gentle introduction to quExec and do not require a JMS environment.

Option -l runs a command locally.

> bin/quExec-test.sh Local -l echo "Hello world!"
Starting quExec process with tag 'Local'
Hello world!

> bin/quExec-test.sh Local -l ls *xml
Starting quExec process with tag 'Local'
build.xml
pom.xml

Option -f connects the I/O of the command executor process to the I/O of the executed native process. The following examples shows how cat is run as native command while input is entered in the terminal and the terminal reprints the same test as output by the cat process. The process is terminated by entering CTRL-d.

> bin/quExec-test.sh Local -l -f cat
Starting quExec process with tag 'Local'
aaaa
aaaa
ccccc
ccccc

For example, this suffices to start a shell and to execute some commands.

> bin/quExec-test.sh Local -l -f bash
Starting quExec process with tag 'Local'
ls *xml
build.xml
pom.xml

Running a command with a timeout

Even though it is easy to start a native process from a Java program, it may make sense to use quExec for that. Suppose you need to read the output of the subprocess and have to make sure that your Java program does not hang for too long if the subprocess does not terminate within a reasonable amount of time.

In such a case, running the subprocess through quExec with a suitably defined timeout will solve your problem. quExec takes special care not to block the execution thread when reading from the subprocess and is thus always responsive to interrupts. This helps to shield the Java process from instabie behavior of native subprocesses.

The following example demonstrates this feature at the command-line (even though you would never use it at the command-line since your shell has much provides much more powerful tools for controlling your processes).

> bin/quExec-test.sh Local -l -t 5000 -- bash -c 'while true; do echo foo; sleep 1;done'
Starting quExec process with tag 'Local'
foo
foo
foo
foo
foo
Timeout occurred while waiting for OS process to terminate.

Running a command in a remote execution server

Starting the JMS broker

The following examples show the actual business of quExec - starting commands remotely via an execution server that is accessed via JMS (or possibly other remote communication techniques) and communicating with the started native processes via JMS.

For this purpose we need an JMS broker. quExec comes with a test-level dependency on ActiveMQ. The starting the test ActiveMQ broker is done as follows.

  mvn activemq:run
Starting the execution server

Then, we need to start the command execution server

> bin/quExec-test.sh Server -s -q dynamicQueues/exec
Starting quExec process with tag 'Server'
448  INFO  (main) @[server.ProcessExecutorServiceBean] process executor
service initialized: launcherTimeout=10000, defaultTimeout=60000

Now we have an execution server running that listens at the dynamically created JMS queue 'exec'.

Starting simple commands

Let's use the execution server to launch a simple ls command remotely.

> bin/quExec-test.sh Remote -x -- ls -l *xml
Starting quExec process with tag 'Remote'
445  INFO  (main) @[main.OSCommandExecutor] Running command: '[ls, -l, build.xml, pom.xml]'
HANDLE: procInQueue='dynamicQueues/quexec0', procOutQueue='dynamicQueues/quexec1

The execution server has executed the ls-command and has connected the standard input and output of the ls-process to the dynamically created JMS queues quexec0 and quexec1. We will now connect to these queues and retrieve the output of the ls-command.

> bin/quExec-test.sh Remote -c -i dynamicQueues/quexec0 -o dynamicQueues/quexec1
Starting quExec process with tag 'Remote'
-rw-r--r-- 1 schickin users  1175 25. Okt 22:50 build.xml
-rw-r--r-- 1 schickin users 11214 26. Okt 22:11 pom.xml

Since it is common to execute a command and then to connect to its input and output, the options -x and -c can be combined.

> bin/quExec-test.sh Remote -x -c echo 'Hello world!'
Starting quExec process with tag 'Remote'
536  INFO  (main) @[main.OSCommandExecutor] Running command: '[echo, Hello world!]'
953  INFO  (main) @[main.OSCommandExecutor] Connecting to remote process via JMS
Hello world!
Using timeouts

Timeouts can also be specified for remotely executed commands.

> bin/quExec-test.sh Remote -x -c -t 2000 ping www.google.com
Starting quExec process with tag 'Remote'
453  INFO  (main) @[main.OSCommandExecutor] Running command: '[ping, www.google.com]'
11657 INFO  (main) @[main.OSCommandExecutor] Connecting to remote process via JMS
PING www.l.google.com (74.125.39.104) 56(84) bytes of data.
64 bytes from fx-in-f104.1e100.net (74.125.39.104): icmp_seq=1 ttl=56 time=74.1 ms
64 bytes from fx-in-f104.1e100.net (74.125.39.104): icmp_seq=2 ttl=56 time=76.6 ms 
Forwarding local input

Local input can be forwarded to the remote process through option -f.

> bin/quExec-test.sh Remote -x -c -f bash
Starting quExec process with tag 'Remote'
453  INFO  (main) @[main.OSCommandExecutor] Running command: '[bash]'
991  INFO  (main) @[main.OSCommandExecutor] Connecting to remote process via JMS
ls *xml
build.xml
pom.xml
exit