Suite/P Toolkit: Software Overview

Last revised: 17Mar2004 GLG

Table of Contents

Introduction

The Suite/P Toolkit provides an extensible suite of Posix-style facilities for Java programs. It is intended to replace an earlier design, the Easy Posix Toolkit, though either one can be defined in terms of the other.

The Easy Posix Toolkit was designed as a hierarchy of abstract but specialized classes. Each Posix-style facility was represented with specific classes and methods. The overall design emphasized unity and tight cohesion through nested factory relationships: specific objects made other specific objects which returned the specific value of interest. An implementation for a particular platform could omit certain facilities, but it was difficult working with such a fractured result, because managing the gaps ultimately depended on detailed "guilty knowledge" of a particular implementation's omissions.

The design for the Suite/P Toolkit took a completely different approach. Instead of representing every Posix-style capability with its own class or classes, simple existing Java classes are used whenever possible: String, Number, etc. Also, the unifying principle of the Suite/P Toolkit is not a factory, but a set or suite of name/value pairs. The idea of a factory making different kinds of feature-heavy Posix-specific objects has been replaced with the idea of asking for a capability by name and receiving an object in return that is as simple and lightweight as possible. Furthermore, the named capabilities themselves are fine-grained. For example, there is a specific name String for the current process's effective userid, which returns a simple Integer as the result. Contrast this with the Easy Posix Toolkit approach of a central Posix factory (which must first be properly initialized), from which one obtains a UserID instance, from which an 'int' userid is ultimately obtained by calling the getEffective() method.

In addition to the Suite/P Toolkit's simple representations and fine-grained access mechanism, the infrastructure for creating new implementations is also simple and fine-grained. This makes it easier to create a new implementation for a small number of capabilities, or just a single capability, and seamlessly integrate it with any other capabilities one already has. You can even override parts of existing capabilities while keeping other capabilities provided by the same implementation, all accomplished without subclassing. As a result, programs using the Suite/P Toolkit always perceive a single coherent collection of named capabilities, regardless of the number or nature of the underlying implementations.

Design Summary

The foundation of the Suite/P Toolkit's design is very simple -- named capabilities. You send a name (a String) to something and receive a capability (an Object) in return. If the named capability isn't available, then null is returned instead.

The "something" that you send a name to and receive a capability from is called a suite, and there is a class named Suite at the very heart of the Suite/P Toolkit. However simple or complex, each Object returned by a Suite represents a capability, and we always call it a capability-Object., even when it provides more than one method or member that can be acted upon.

At this point you may be thinking that a Suite provides the platform-specific implementation, but it doesn't. Instead, a Suite gathers together and integrates the much simpler capabilities of multiple Suitelet implementations (imps), and that is where specific capabilities are provided: in the Suitelet. A Suitelet provides specific capabilities, and a Suite integrates Suitelets into a coherent whole.

Since a Suite may contain many Suitelets, and we don't want to search them all when evaluating every capability name, a Suite partitions the space of capability-names by suite-name. A suite-name is nothing more than the starting segment of a capability-name up to a delimiting "dot" (period, full stop). For example, in the capability-name "some.capability", the suite-name is "some." and only Suitelets registered under that suite-name will be evaluated when "some.capability" is passed to the Suite. If the capability-name were longer, as in "some.arbitrary.significant.thing", the suite-name would still be "some.". This means that suite-names are not treated like Java-language package-names. It also means that all capability-names must contain at least one dot, otherwise a Suite won't know which Suitelet to use to get the capability-Object.

The basic design of a Suite is like a simplified Map or Hashtable. A Suite, however, is defined to work only with name-Strings as keys, not arbitrary Objects as keys. Also, where a Map and Hashtable provide many methods for adding, removing, and otherwise managing or manipulating its contents, a Suite is much more limited. You can add only Suitelets to a Suite, never any other type. And you can only add Suitelets to a Suite, never remove nor replace them. These limits are intentional, because once a Suite has been given its Suitelet imps, there is very little reason to remove or rearrange those Suitelets within the same Suite. Essentially, once a Suite has been built, you only get things from it. As a result, Suite does not implement Map nor is it a Hashtable subclass, and neither is Suitelet.

The last remaining aspect of a Suite is how it is populated with Suitelet implementations. Of course, there is an add() method that takes a Suitelet arg, along with the suite-name under which the Suitelet will be registered. But this method is only a building-block, since it doesn't create a Suitelet. For populating a Suite, we turn to two other methods:

There are more classes and interfaces in the Suite/P Toolkit than just Suite and Suitelet, as explained below. The Suite class, however, is the center around which everything else revolves.

Package Summary

If you only use the existing Suitelet imps and don't write new ones, then these are the important Suite/P Toolkit packages: If you run your program on Mac OS X under J2SE 1.3 or 1.4, then you will use this package: If want to write your own Suitelet imps, or subclass existing imps, you may find the classes in these packages useful: A number of test and example programs are found here:

Class and Interface Summary

This section describes the major classes, interfaces, and methods of the Suite/P Toolkit. It is not an exhaustive list, only a summary of the things most programs will use. For details, refer to the API documentation, or read the source.

Many capabilities are represented by a Number or a String, so the specialized interfaces defined by the Suite/P Toolkit are only used to represent specialized capabilities. Things like "the current effective userid" or "the current process id" are not represented by any specific Suite/P Toolkit class or interface.

glguerin.suitep.Suite
This class is the heart of the Suite/P Toolkit. Its principal method is get(), which receives a name-String and returns a desired capability-Object. Convenience methods getNumber(), getString(), getParameter(), and getLookup() also receive name-Strings, returning either a specific type of capability object or null.

If you wish to use a program-wide singleton Suite (a "global variable"), the static methods getSuite() and setSuite() provide a thread-safe way to accomplish this. If no Suite has yet been assigned when getSuite() is called, it will automatically make a new Suite and load its Suitelets from the numbered sequence of property-names having the base property-name "glguerin.suitep.imp.".

If you want a specific base property-name for the numbered sequence, use either the new Suite(String) constructor or the addProviders(String) method.

If you want a specific Suitelet to be loaded and instantiated by name, use the addProvider(String) method.

glguerin.suitep.Lookup
This interface represents a bidirectional name/number mapping. It defines two simple methods that map between names and primitive ints: nameFor() and numberFor(). It also defines a method that returns an Enumeration of all available names, names(), so a complete catalog of mappings can be enumerated.

Several Posix-style facilities typically use name/number mappings, including user-ID's, group-ID's, and signals. Other facilities may also provide name/number mappings with Lookup, if so desired.

glguerin.suitep.Mutater
This interface is for changing a process's real and effective userid or groupid, in a controlled and constrained manner designed to reduce mistakes. The as() method changes the effective id temporarily, so the process acts as someone but the saved id can be restore()'d at any time. The become() method changes all ids permanently, so the process irrevocably becomes someone.

glguerin.suitep.Parameter
This interface represents access to a single mutable underlying numeric value. It can represent process parameters, such as a process's resource-limit or its scheduling priority, or any other mutable numeric value. A Parameter's value() is a primitive long, which is large enough to represent any reasonable parametric value. A Parameter can be set() to a given long value, but may require special privileges, depending on the nature of the Parameter and the specific value being assigned. Every Parameter also has an identifying name() String, which may be the same as the capability-name passed to Suite or may be different, depending on the implementation and the purpose of the Parameter.

glguerin.suitep.Processing
This interface contains process-related methods that didn't fit anywhere else. The getProcessID() method returns a process ID for a java.lang.Process. This is useful when you're using other Suite/P Toolkit classes to operate on child Processes returned by Runtime.exec().

You can manage process group affiliation with getProcessGroup() and setProcessGroup(). For Java programs, these are used mostly in conjunction with Signaller.signalProcessGroup().

glguerin.suitep.Signaller
This interface provides access to Posix-style inter-process signal sending. It extends Lookup because signals are identified mainly by name, but are sent by number. Using signal-names allows for cross-platform portability, without regard to the actual signal-numbers that any implementation ultimately uses.

Signals can be sent to a single process with signalProcess(), where the receiving process can be the current process, or any other process whose process-ID is known.

Signals can also be sent to a process group with signalProcessGroup(), or to all processes having the same userid with signalAll().

As a convenience, a SignalReceiver corresponding to the Signaller can be obtained from getReceiver().

glguerin.suitep.SignalReceiver
This interface provides access to Posix-style signal receiving, signal ignoring, and related signal-reception management. It extends Lookup because signals are identified mainly by name, but are received and managed by number.

The process's current response for signals can be determined by isIgnoring() and isReceiving(). The canReceive() identifies whether a given signal can be received or ignored. The response for signals can be changed with customarily(), ignore(), and receive(). The next signal received can be obtained from awaitSignal(), which allows a timeout interval.

The signal-receiving queue is managed with getPending(), getOverruns(), and reset().

As a convenience, a Signaller corresponding to the SignalReceiver can be obtained from getSignaller().

glguerin.suitep.Suitelet
This interface defines the building-blocks by which Suite manages all capability-Objects of any name or type. The get() method takes a name-String and returns either a capability-Object or null. The addTo() method asks the Suitelet to add itself or other Suitelets to a Suite under whatever suite-names the Suitelet itself knows it supports.

Available Implementations Summary

This section describes the principal Suitelet implementations, the applicable platforms, and the suite-names they support. For a complete list of every capability name and the returned Object type, see Available Implementations.
glguerin.suitep.imp.un.UnSuite
This Suitelet imp works on any platform, and is written entirely in plain Java. It provides elements from the "userid." and "groupid." suites, and some of the "process." suite. All elements are essentially placeholders, and represent typically "invalid" but non-null values. For example, the "userid.real" capability is a Number whose value is -1, which is an invalid userid on most Posix-like or Unix-like platforms. This implementation is useful for testing, or as a non-null fallback in the the highest-numbered position (i.e. having the least-significant precedence).

glguerin.suitep.imp.macosx.XSuite
This Suitelet imp, whose name is pronounced "ten sweet", works on Mac OS X, and is written with native-code in a JNI library. It provides a fairly comprehensive range of capabilities under the following suite-names: "env.", "process.", "userid.", "groupid.", "limit.", "hw.", "kern.", and "user.".


To Greg's Home Page
To Greg's Software Page