Overview of the Easy Posix Toolkit for Java

Last revised: 09Apr2002 GLG
THIS SOFTWARE IS OBSOLETE
The newer Suite/P Toolkit provides most of its features.
These files are available mainly for historical purposes.
Table of Contents

Introduction

Imagine if there were Java classes representing all the fun things you can do with Posix (Unix) system-calls and library functions? OK, not all the fun things, because you can already do many fun things with java.io.File and java.io.Socket, and why bother reinventing those classes when there's plenty of fun to be found in other areas. Yes, I know, some of this fun stuff is Unix-specific. Or at least Posix-specific. Or maybe it's specific to certain OSes and platforms. Still, wouldn't it be fun to have a Java Posix API?

That's what the Easy Posix Toolkit is: an API for Posix functions, written in Java. I didn't provide every system-call and every library function, so you are limited in your opportunities for fun. But the toolkit's architecture is easily extensible into other areas, so you can expand your fun horizons with relative ease.

If you're on Mac OS X, the Easy Posix Toolkit also gives you a concrete implementation. So maximal fun on Mac OS X is now possible. OK, maybe not maximal fun, since I didn't do every system-call and every library function possible under the Mac OS X APIs. Hey, one person can only provide so much fun.

In designing the Easy Posix Toolkit, I concentrated on things I think matter most, and where there is currently nothing available in Java or in JSR's. For example, I didn't do sockets, because Java already has network sockets. I didn't do high-performance async I/O, either, because there's already a JSR for that. Instead, I focused on simple everyday things like file permissions and symlinks, real and effective user ID, signals and signal-handling, process resource limits, and so on.

Java is currently unusable for some tasks because it doesn't have any representations for many of these simple, mundane things. For example, some system-admin tasks require setuid-root permission on the program. But Java knows nothing about this, nor about user IDs in general, so it may not be a good choice for writing such a program. As another example, some server programs written in Java suck because they hit limits like the number of open file-descriptors, or they die ungracefully when certain signals arrive. Many facilities of the Easy Posix Toolkit are even useful to ordinary user programs, like the amount of free space on a disk volume, or whether a disk is read-write or read-only, or creating a hard-link or a symlink, or even just changing the permission bits (access modes) of files.

Design Summary

It's the Factory pattern. Nested over and over.

There are Abstract Factory patterns and Factory Method patterns. Nested over and over.

Why so many factories? Because the individual functional areas interconnect better that way. Factories makes extensibility easier, because there are fewer interdependencies and less overlap, making it easier to add new facilities later. Factories makes implementations easier to do, because you can do some parts without doing other parts, and the parts you provide will still work. Factories also makes subsetting easier, so you can eliminate the pieces you don't want but the parts you keep will still work.

Why don't the Toolkit's classes and methods correspond directly to the POSIX system-calls and library functions? Because it sucks that way. Trust me. I tried a few things that way, and it was amazingly awful. If you think you have a better design than this API, go for it. If you're on Mac OS X, you can even rip out the entire API except for the Ten class, and make a completely new API.

I'm not saying this is the best API for Posix-like capabilities, though I think it's pretty good. There are many ways to provide these capabilities (I went through several design variations), and there is plenty of room for reasonable designs (and designers) to disagree. You may have your own ideas. Maybe there's a particular object-oriented design for Posix calls that you'd like to try. Go for it. You can build whatever you want as Adapters or Facades or whatever on top of the Easy Posix Toolkit classes. You can even make the whole thing look almost exactly like the C functions in one big static class, if that's what you hunger for.

Despite being called the Easy Posix Toolkit, you can implement many of these facilities on platforms that are not POSIX-compliant. If some facility, say GroupID, is meaningless on your desired platform, you can either omit it or provide a do-nothing implementation.

Package and Class Summary

These are the packages in the Easy Posix Toolkit: If your program doesn't use any of the classes from app.posix.test or glguerin.xio, you can safely omit these packages from your redistributions.

In the beta release of the Easy Posix Toolkit, the classes in glguerin.xio are modified versions of some classes from my MacBinary Toolkit for Java, but in a different package. This was done so that these two toolkits don't conflict. This is a temporary expedient only. In the final release of the Easy Posix Toolkit, I will revise all the classes shared with the MacBinary Toolkit so they are identical in both toolkits. These classes will then reside once again in the glguerin.io package. The MacBinary Toolkit will also undergo a maintenance release to accomodate these changes, and they will live happily ever after.

Below are the principal Easy Posix Toolkit classes and interfaces, along with the system-calls and library functions they correlate to.

Posix, PosixImps:
    no system calls, but provides factory methods for all the other interesting classes.

Environs, MountInfo, ResourceLimit, ResourceUsage:
    getenv(3), getfsstat(2), getpid(2), getppid(2), getpriority(2), getrlimit(2), getrusage(2), setpriority(2) setrlimit(2), umask(2)

FileRef, FileState, MountInfo:
    chflags(2), chmod(2), chown(2), link(2), lstat(2), mkfifo(2), stat(2), statfs(2), symlink(2)

GroupID:
    getegid(2), getgid(2), getgrgid(3), getgroups(2), setegid(2), setgid(2)

Signals, SignalQueue:
    kill(2), sigaction(2), signal(3), sigprocmask(2)

SysTime:
    adjtime(2), gettimeofday(2), settimeofday(2),

UserID, UserInfo:
    geteuid(2), getlogin(2), getpwent(3), getpwuid(3), getuid(2), seteuid(2), setuid(2),

The Factory Tour

The central factory class is glguerin.posix.Posix. It has a configurable static factory method: GetPosix(). You tell it which concrete Posix subclass to instantiate using SetFactory(), and it does the rest. You give SetFactory() a fully qualified class name, typically retrieved from the PosixImps class.

The list of known Posix implementations is maintained by glguerin.posix.PosixImps. You usually call its selectImpName() method to retrieve an implementation's class-name, but if you need to manipulate the list of known imps there are additional methods: getImps() and setImps().

The responsibilities for managing the different aspects of various implementations are divided between PosixImps and Posix. Basically, the PosixImps class knows the class-names and platform-identifiers for the various platform-specific Posix factories, while the Posix class runs the chosen factory. This division of responsibility means that the Posix class and the list of known implementations are more loosely coupled. It also means that you don't have to use PosixImps to retrieve the factory name. You can use any scheme you want, as long as it produces the fully qualified class name of a concrete Posix implementation.

The Posix class is abstract. In addition to its static GetPosix() factory method, it has a number of abstract factory methods, which an implementation provides. These subsidiary factory methods return instances of the other interesting classes, such as UserID, Environs, Signals, and FileRef. Thus, we have factories within factories, reaching into each area of functionality. The Posix class thus acts as the central factory or nexus of production for all the other interesting classes that make up the Easy Posix Toolkit.

Some of the interesting subsidiary classes also have factory methods. For example, Signals has makeSignalQueue(). Yet again, we have factories within factories.

The implementation-provided factory methods in the Posix class are:

Environs, MountInfo, etc.

Environs represents the environs or surroundings of the current process. It's a bit of a catchall for things that didn't fit elsewhere, but didn't merit their own class. Environs uses MountInfo to describe a mounted file-system. It uses ResourceLimit to represent settable resource limits, and ResourceUsage to represent cumulative resource usage by the current process or its terminated children. Environs has the following methods:

MountInfo describes a mounted volume or file-system with the following methods:

UserID and GroupID

UserID represents the process's real and effective user ID, and also provides access to other things related to user IDs. The UserInfo interface represents user account information, as returned by UserID. UserID has the following methods:

GroupID represents the process's real and effective group ID, and also provides access to other things related to group IDs. GroupID has the following methods:

FileRef and FileState

FileRef provides access to the file-system. It only provides features that you can't get from java.io.File. FileRef creates and returns instances of FileState, which represent information like permissions, owner and group ID, node-type, and link-count.

FileRef has a simple yet powerful way of representing pathnames. You simply provide an Object whose toString() method returns a pathname. The Object can be any Object at all, including a String, and it can return any pathname at all. For example, if it's a glguerin.xio.Pathname, then the attached FileRef will follow changes you make to the underlying Pathname.

FileRef has the following methods:

FileState has the following methods:

Signals and SignalQueue

Signals provides access to signal-handling and signal-sending facilities. It is somewhat more dependent on the platform OS and implementation than some of the other Easy Posix Toolkit classes. Still, any OS that provides signal-sending and signal-handling capabilities that are even remotely Unix-like should be able to implement Signals and SignalQueue.

Signals is used to send signals, or to discover the current signal-handling behavior of the process. Signals is also used to arrange for the ignoring, defaulting, or exiting behavior of signals. A SignalQueue is created by Signals, which you then use to receive signals. You can create one SignalQueue or many. You can arrange for each distinct signal number to be delivered to its own SignalQueue, or you can tell one SignalQueue to receive two or more distinct signals.

All signal values are initially referenced by name. This is so platforms and implementations have the necessary flexibility in assigning the meaning of the signals they support. Signal names are defined in the SignalNames interface, which the Signals class then "implements" to provide convenient access to the names. The Signals class returns a platform-dependent signal-name Dictionary listing the sendable and receivable signals. You can also ask Signals to map a signal-name to a signal-number, or vice versa, without retrieving the intermediate Dictionary object.

Signals has the following methods:

SignalQueue has the following methods:

SysTime

SysTime provides access to time-setting facilities, and to a microsecond-resolution timebase. Time-setting is limited to processes that have an effective user ID of the superuser. All processes can read the microsecond timebase.

SysTime has the following methods:


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