All Packages  This Package  Class Hierarchy  Class Search  Index

Class app.authkit.envoy.Envoy
java.lang.Object
   |
   +----app.authkit.envoy.Envoy

  Summary

public class  Envoy
     extends java.lang.Object
{
          // Fields 11
     public static final String PROTOCOL_BEGIN_OUTPUT;
     public static final String PROTOCOL_BEGIN_TASK;
     public static final String PROTOCOL_DELIM;
     public static final String PROTOCOL_END_OUTPUT;
     public static final String PROTOCOL_END_TASK;
     public static final String PROTOCOL_NEWLINE;
     public static final String PROTOCOL_STOP;
     public static final String TASKS_BETWEEN;
     public static final String TASKS_BETWEEN_PROP;
     protected int myTaskNumber;
     protected int myTaskStatus;

          // Constructors 1
     public Envoy();

          // Methods 20
     public static void main(String[]);

     protected Authorization buildAuth();
     public void explain(Throwable);
     public int finish();
     protected Authorization getAuthorization();
     protected void groupTaskArgs(Enumeration, Vector, String);
     protected Authorization newAuth();
     public void perform(String[]);
     public void prepare();
     protected void prepareTaskIO();
     protected void protocolBegins();
     protected void protocolEnds();
     protected InputStream protocolIn();
     protected PrintStream protocolOut();
     protected void taskBegins(int, String);
     protected void taskBeginsOutput(int, Task);
     protected void taskEnds(int, Task, int);
     protected void taskEndsOutput(int, Task);
     protected void taskFails(int, Task, int);
     protected int taskPerform(int, Vector);
}

Envoy performs Tasks in a presumably privileged process, reporting on their success.

The Envoy.main() method is the typical entry-point of a privileged process, executed via Authorization.execPrivileged(). As a result, its stdin and stdout (System.in and System.out) are presumed to be connected to a pipe going back to the parent process. The pipe's streams may be coupled together, so closing one may close both. This constrains how the Envoy can communicate results back to its parent. The Envoy is expected to return Task outcomes and data on its stdout, including its own termination status, since the privileged Process in the parent may not provide waitFor() or exitValue().

An Envoy's stderr stream is typically inherited from the parent process, and usually writes to the system console (or its log-file) in a double-clicked app. It writes to stderr when invoked at the command-line or in other-than-double-clicked launches. It may or may not be used by the Envoy protocol. By default, it is not.

The simplest communication protocol for an Envoy is to only return its own "exit status" on stdout, and to discard all output produced by Tasks or external commands. The provided protocol can do more than that, but it's still very simple, if not simple-minded.

Basic Envoy Communication Protocol

The current Envoy communication protocol is brutally simple. It uses the default System I/O streams for its own communications, and sets different streams into System.in, .out, and .err for use by Tasks. It uses the I/O streams as follows:
System.in (stdin)
Not used. For Tasks, replaced with an immediate-EOF stream using System.setIn().
System.out (stdout)
Send progress messages and result text back to client (the parent process). For Tasks, replaced by either an actual stream or a data sink stream, depending on whether the Task produces output or not. No Task should close System.out.
System.err (stderr)
Available for diagnostics, such as stack-traces, etc. This stream is not currently used by the Envoy protocol, though the default Envoy class will print failure stack-traces on it. Tasks may freely print() or println() or printStackTrace() to this stream. No Task should close System.err.
If a Task produces output that is sent back to the parent process, and the Task closes System.out, it will cause a protocol failure. So don't do that. In the future, a safer stream may be used that doesn't actually close the stream on close(), but only flushes it.

As each Task is undertaken, the Envoy sends a task-begin message. As each Task is completed, the Envoy sends a task-end message containing that Task's completion-code. Between task-begin and task-end, an output-producing Task's output data is produced. The output data is bracketed by text-begin and text-end messages.

When all Tasks are complete, the Envoy sends a stop message containing the Envoy's status code, i.e. its exit-code. An EOF on the Envoy stream before a stop message appears is an indication of failure.

The default Envoy behavior is to stop performing Tasks as soon as one of them fails. That is, as soon as a Task returns a non-zero completion code or throws an uncaught exception. The appropriate text-end, task-end, and stop messages are sent when a Task fails.

All task and stop messages are in plain ASCII text, one byte per char. Each message consists of an LF-terminated line, regardless of the host platform's local line-ending convention. Each message contains multiple parameters separated by :'s.

task-begin:N:classname
is the task-begin message:
task-begin
is the literal text "task-begin".
N
is a decimal sequence number, the task-number, starting from 1 and increasing for each task.
classname
is the fully qualified classname of the Task about to be instantiated and performed.
The Task has not yet been loaded or created when this message is sent. Therefore, errors arising from Task instantiation appear as errors belonging to the failing Task, not as errors between Tasks.

text-begin:N
is the optional text-begin message:
text-begin
is the literal text "text-begin".
N
is a decimal sequence number matching the current task-number.
The task's output text follows, until a text-end message. If the task does not produce output, this message is not sent for that task.

text-end:N
is the optional text-end message:
text-end
is the literal text "text-end".
N
is a decimal sequence number matching the current task-number.
This message is sent even when the task produced no output, but only if a text-begin message was sent before.

task-end:N:status
is the task-end message:
task-end
is the literal text "task-end".
N
is a decimal sequence number matching the current task-number.
status
is a decimal number, the Task's status code, which is 0 for success, non-0 for failure.
If the task threw an uncaught exception, the status will be -1.

stop:0:status
is the stop message:
stop
is the literal text "stop".
0
is the apparent task-number, and is always zero.
status
is a decimal number, the Envoy's exit code, which is 0 for success, non-0 for failure.
In general, the Envoy's status is 0 when all Tasks perform() successfully. If the Envoy status is not 0, it is generally the status of the last Task, which presumably failed.

Subclasses of Envoy can use a different protocol, such as one based on XML. The messages are sent by specific methods, which can be overridden. If you use a different protocol, be sure to change the relevant Tool methods, too.

System Properties Used

"file.encoding"
implicitly used anywhere a PrintStream is created or parsed, such as captureUntilEOF(), schlepUntilEOF(), or waitForEnvoy(); or anywhere encoded bytes are turned into a String, such as translateEnvoyMessage().
"os.name"
in buildAuth() to determine which Authorization class to instantiate.
"envoy.tasks.between"
a string value, in perform() to mark a task-name when parsing Task-groups from the args.

See Also: Task, Tool, Authorization




  Fields

· TASKS_BETWEEN

Summary  |  Top

   public static final String TASKS_BETWEEN

The default marker between task-groups in the args received by main() and perform().


· TASKS_BETWEEN_PROP

Summary  |  Top
   public static final String TASKS_BETWEEN_PROP

The property name of an override for TASKS_BETWEEN.


· myTaskNumber

Summary  |  Top
   protected int myTaskNumber

Task-number in perform() etc.


· myTaskStatus

Summary  |  Top
   protected int myTaskStatus

Completion status of most recently performed Task: 0 = success, !0 = failure-code.


· PROTOCOL_DELIM

Summary  |  Top
   public static final String PROTOCOL_DELIM


· PROTOCOL_NEWLINE

Summary  |  Top
   public static final String PROTOCOL_NEWLINE


· PROTOCOL_BEGIN_TASK

Summary  |  Top
   public static final String PROTOCOL_BEGIN_TASK


· PROTOCOL_BEGIN_OUTPUT

Summary  |  Top
   public static final String PROTOCOL_BEGIN_OUTPUT


· PROTOCOL_END_OUTPUT

Summary  |  Top
   public static final String PROTOCOL_END_OUTPUT


· PROTOCOL_END_TASK

Summary  |  Top
   public static final String PROTOCOL_END_TASK


· PROTOCOL_STOP

Summary  |  Top
   public static final String PROTOCOL_STOP


  Constructors

· Envoy

Summary  |  Top

   public Envoy() 

Creates, but does not prepare() it.



  Methods

· main

Summary  |  Top
   public static void main(String[] args) 

Static entry point, when run as an application. It just creates an Envoy instance and lets it do everything.

Normally, this method calls System.exit() with the value returned from finish(). If the Envoy throws an exception, this method calls System.exit(1), after printing a stack-trace to stderr and flushing both output streams.



· explain

Summary  |  Top
   public void explain(Throwable failure) 

Emit stack trace on stderr, flushing protocolOut() first.



· prepare

Summary  |  Top
   public void prepare() 

Prepare this Envoy for subsequent use.

This method should prepare the System I/O streams for use with Tasks, and arrange to "do protocol" on the default streams, which are presumed to be pipes to a parent process.



· perform

Summary  |  Top
   public void perform(String[] args) 

Work through the args and the Tasks, communicating results to parent using Envoy protocol over presumed pipe. The protocol is performed by taskBegins(), taskEnds(), etc., which are called from taskPerform().



· finish

Summary  |  Top
   public int finish() 

Finish doing protocol and anything else. Return exit-code that will be passed to System.exit().

By default, does not release() or detach() the Authorization. Therefore, the default action occurs on process termination, which is equivalent to release().



· groupTaskArgs

Summary  |  Top
   protected void groupTaskArgs(Enumeration argsEnum, 
                                Vector taskArgs, 
                                String between) 

Pull one Task-name and a group of args from the given Enumeration, placing the String representation of each into the Vector. Upon return, the Vector may be empty.

Task-groups are separated by the 'between' String. The Vector will not contain either a leading or trailing 'between' String.



· taskPerform

Summary  |  Top
   protected int taskPerform(int taskNum, 
                             Vector taskGroup) 

Perform a Task and return its status code: 0 for success, non-zero for all others. This method is responsible for all protocol messages regarding the task and its status.

This method calls taskBegins() before it runs the Task, followed by taskEnds() or taskFails() afterwards. It also calls taskBeginsOutput() and taskEndsOutput() as appropriate.



· getAuthorization

Summary  |  Top
   protected Authorization getAuthorization() 

Return an Authorization, or null if before prepare()'d.



· buildAuth

Summary  |  Top
   protected Authorization buildAuth() 

Called from prepare(), build an Authorization for possible subsequent use. If unsuccessful, throw an UnauthorizedException.

The returned Authorization should be attached to a session, if that's appropriate for what the Envoy is doing. Since an Envoy typically operates with an effective uid of 'root', and is typically a child process via execPrivileged(), the typical attachment is done with attachPrivileged(). This implementation does that.

This implementation calls newAuth() to make the concrete Authorization, then calls attachPrivileged() to attach it. Therefore, an Envoy by default will only run in a child process via execPrivileged(). This is a reasonable precaution to help prevent accidents, such as running Envoy.main() from a normal shell command-line.



· newAuth

Summary  |  Top
   protected Authorization newAuth() 

The returned Authorization is unattached to any underlying session. The available concrete imps are hard-wired here, for security reasons.

If the "os.name" property is "Mac OS X", then a MacOSXAuthorization is returned. All other situations, including failure to instantiate a MacOSXAuthorization, return a DenyAllAuthorization.

The concrete classes are referenced by name, using Class.forName(). This avoids invoking an imp's static initializers unless it's actually chosen.



· protocolIn

Summary  |  Top
   protected InputStream protocolIn() 

Return the stdin InputStream piped from controlling parent process, or null if before prepare()'d.



· protocolOut

Summary  |  Top
   protected PrintStream protocolOut() 

Return the stdout PrintStream piped to controlling parent process, or null if before prepare()'d.



· protocolBegins

Summary  |  Top
   protected void protocolBegins() 

Set up the streams that will be used to "do protocol" in taskBegins(), tasksEnds(), and taskFails(), and which will be shut down in an orderly fashion by finish().

If any initial text should be emitted on the protocol streams, do it here. This ensures it will occur before the first task begins.



· prepareTaskIO

Summary  |  Top
   protected void prepareTaskIO() 

Called from prepare(), replace the System I/O streams with ones appropriate for a Task's use. This process's default streams, which are piped to another process, have already been harnessed to "do protocol" by protocolBegins().



· protocolEnds

Summary  |  Top
   protected void protocolEnds() 

Called from finish(), send any Envoy-termination messages, and then shut down the I/O streams used to "do protocol". "Shut down" doesn't necessarily mean the streams are closed. It may just mean the streams are flushed, as here.



· taskBegins

Summary  |  Top
   protected void taskBegins(int taskNum, 
                             String taskName) 

Send a BEGIN_TASK protocol message.



· taskBeginsOutput

Summary  |  Top
   protected void taskBeginsOutput(int taskNum, 
                                   Task task) 

Send a BEGIN_OUTPUT protocol message, then redirect System.out to a stream ready for the Task to produce its output on. If the Task does not write anything to System.out, then its results will be empty. This is acceptable as far as the protocol is concerned.



· taskEndsOutput

Summary  |  Top
   protected void taskEndsOutput(int taskNum, 
                                 Task task) 

Shut down the System.out used by the Task, sending any protocol messages to mark the end of Task output.



· taskEnds

Summary  |  Top
   protected void taskEnds(int taskNum, 
                           Task task, 
                           int status) 

Send an END_TASK message.

The Task should not be null.



· taskFails

Summary  |  Top
   protected void taskFails(int taskNum, 
                            Task task, 
                            int status) 

Send an END_TASK message.

The Task may be null, so qualify before using it.



All Packages  This Package  Class Hierarchy  Class Search  Index
Freshly brewed Java API Documentation automatically generated with polardoc Version 1.0.7