External Processes and how to Connect them to Oz

Joachim Niehren

provides
x-ozlib://niehren/external-process/connect.ozf
x-ozlib://niehren/external-process/input-output.ozf
requires
Open Application Browser

Purpose

The functor connect.ozf provides a simple way to connect external processes to an Oz process. The user can freely chose between socket or pipe connections. The implementation of both kinds of connections is hidden from the user so that the connection can be switched easily. This abstraction is typically useful for Oz written systems that run on various platforms or with various configurations.

The Function Connect

Calling the function Connect creates an external process and returns a connection to it. An external process is a process that runs outside the Oz and can be started from the command line, i.e. a shell. An external process may be written in an arbitrary programming language (C, Lisp, Oz, Shell, Perl, etc) as long as it can be started by executing a shell command. The function Connect has the following type:
   Connect: ShellCmd x unit(args:Args ...)  ->  unit(stream:String
                                                     kill:->
                                                     send:String->)
            ShellCmd = VirtualString
            Args     = list(VirtualString)
The external process receives its input on the stream Connect.stream and sends its output via Connect.send.

The Functor connect.ozf

The functor connect.ozf exports a record Connect containing two implementations of the function Connect:
    Connect.pipe 
    Connect.socket
Both functions can connect external processes but of different kind:

Connect.pipe connects external processes that send input and output to the standard input/output files (StdIn and StdOut).

Connect.socket connects Oz to external processes. Such external processes are connected via a socket with port PortNumber over which they send their input/output. To access the socket with PortNumber the external process requires an extra option when called from a shell:

     --port=PortNumber
The external process behaves as a client of a socket with the port number PortNumber (and not as a server). The external process is started, connects to the socket with this port, and sends its input and output to there.

Establish a Connection

To establish a connection to an external process you have to envoke either of the two connect functions (for pipes or sockets). All you need to know is the shell command by which to start the external process from a shell. If this command is
     ShellCmd Arg1 ... Argn [--port=PortNumber]
then you can also invoke it from Oz by executing the following statement (which does not require the port number):
     Kind = socket % pipe
     Process = {Connect.Kind ShellCmd unit(args:[Arg1 ... Argn])}
Note that both kinds of connections are established in analogy. The choice depends only on the value of the variable Kind. This makes it very simple for the user of this package to select and change the kind of connection required (either socket or pipe).

Send Input and Receive Output

The string output of the process can be accessed on the stream Process.stream. You can also send input to the process by calling Process.send.

Example

declare [Connect] = {Module.link ['x-ozlib://niehren/external-process/connect.ozf']}
declare Connection = {Connect.pipe cat unit(args:nil)}
{Browse Connection.stream}
{Connection.send hiho}
{Connection.send holladihi}
{Connection.kill}

Purpose

The functor input-output.oz provides a generic way to define input-output interfaces for Oz processes (which can then be used as external processes).

Oz-Processes as External Processes

We distinguish two kinds of external processes depending on their input-output behaviour. The first kind of processes use use standard input-output. The second kind sends input to and receives output form a socket.

If you want to write external processes with Oz, then you might want to do it such way that you create both kinds of input-output interfaces for your process. In this case you can frist define an abstract process and then apply it to wrap a concrete interface around.

An abstract procedure is a process that is abstracted over its input-output interface, i.e. over an input stream and a procedure to send output:

   AbstractProcess: ( string x (string -> ) ) ->

The Functor input-output.ozf

The functor input-output.ozf exports a record InputOutput which contains two procedures InputOutput.standard and InputOutput.toSocket of the following type:
    InputOutput.standard: AbstractProcess ->
    InputOutput.socket: AbstractProcess ->
The procedure InputOutput.standard turns an abstract process into a running process which uses standard input and output. The procedure InputOutput.socket turns an abstracted process into a process which accesses a port number when called from the shell via the following option:
    --port=PortNumber
  
It then sends input and output to the socket with that port number.

Example

Here comes an example where the usual Unix process cat process is turned into an external process written in Oz that is equipped with a socket interface for input output.
functor
import   
   InputOutput at 'x-ozlib://niehren/external-process/input-output.ozf'
   Connect     at 'x-ozlib://niehren/external-process/connect.ozf'
export
   Cat
define
proc{AbstractCat InputStream SendOutput}
   Process = {Connect.pipe cat unit(args:nil)}
in
   SendOutput = Process.send
   InputStream = Process.stream
end

Cat = {InputOutput.socket AbstractCat}
end

Installation

Download the package niehren-external-process-1.1.pkg to a file and execute the following command in a shell (in the same directory where the file resides): ozmake --install --package=niehren-external-process-x.x.x.pkg If you want to extract the source in the actual directory then call ozmake as follows: ozmake --extract --package=niehren-external-process-x.x.x.pkg
Joachim Niehren