Last revised: 17Mar2004 GLGTable of Contents
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.
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:
- addProvider() takes a String argument representing the fully qualified classname of a concrete Suitelet implementation. It loads the named class, instantiates it, and then tells the Suitelet to add itself to the Suite under whatever suite-names it supports.
This method is appropriate when you want to write Java code that adds specific Suitelets by name, in a specific order, or at a specific time. It is not appropriate when you want to isolate your program from knowledge of any specific Suitelet implementation.
- addProviders() also takes a String argument, but here it represents a property-name. Specifically, the String is the base property-name for a numbered series of system properties, each property's value being the fully qualified classname of a concrete Suitelet implementation. The Suitelet imps are loaded, instantiated, and added as with addProvider(), but since the Suitelet names are defined by properties, the program is independent of any specific Suitelet implementation.
The numbering sequence defines more than just a series of Suitelets -- it also defines their precedence. The LOWEST-NUMBERED Suitelet always has the first opportunity for acting on a given capability-name in a suite. The first Suitelet to return a non-null Object has its Object returned. For example, a Suitelet numbered "1" registering itself under any suite-name(s) will be asked to evaluate a capability-name before another Suitelet numbered "2" or "3" or "12" registered for that same suite-name. And "2" acts before "3", and "3" before "12". In this way, you can change Suitelet precedence simply by renumbering property names.
This method is appropriate when you want to isolate your program from all knowledge of any specific implementation, including the class names, their precedence, or even the suite-names the Suitelets register under. It does require that the program invocation define system properties to designate the implementations and their precedence, but this is easily accomplished. Most programs that use the Suite/P Toolkit will want this behavior.
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.
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:
- glguerin.suitep.* -- The principal classes and interfaces, centered on the Suite class. Supplemented by the Lookup, Mutater, Parameter, Processing, Signaller, SignalReceiver, and Suitelet interfaces.
- glguerin.suitep.posix.* -- Interfaces that define suite-names and capability-names for typical Posix-like facilities. You pass these names to a Suite instance in order to get the relevant capability-Object. These interfaces only define name-Strings. They have no methods at all.
- glguerin.suitep.imp.un.* -- Defines the UnSuite Suitelet, which is a plain-Java "unimplementation" useful as a non-null fallback imp or when doing testing.
If want to write your own Suitelet imps, or subclass existing imps, you may find the classes in these packages useful:
- glguerin.suitep.imp.macosx.* -- Defines the XSuite Suitelet, which is a fairly comprehensive implementation for Mac OS X 10.0 or higher. It uses a JNI-library for its native-code support, so is unusable in applets, but can be web-deployed using Java Web Start (JWS/JNLP) or made available as a simple download.
A number of test and example programs are found here:
- glguerin.suitep.imp.* -- Defines ParamCore, SimpleLookup, and convenient utilities for working with low-level string representations.
- glguerin.util.* -- Utility classes, also used by the Suite class and the XSuite imp.
- glguerin.suitept.* -- The classes whose names start with Test are tests, and the ones whose names start with Typical are examples. The API docs and the source itself explain each class.
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.
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