The weakness of Sun's case

First, some history. After java.lang.System.getenv was disabled a bug report (4199068) was raised on Sun's Java Developer Connection website calling for it to be reinstated. This bug was closed two and a half years after it was raised with the annotation 'will not be fixed'. This despite the fact that it had gathered 200 votes and appeared in the Top 25 list. More recently a Request For Enhancement (4642629) was opened asking Sun to review this situation, and the good new is that they have: 4199068 was reopened as a Request For Enhancement and is now listed as 'Closed, fixed'. JDK 1.5 has a functioning java.lang.System.getenv!

Many Java users have added cogent comments to bug 4199068 and it's well worth taking a look at these.

In the evaluation section of the bug report an anonymous reviewer has written:

Many operating systems support the notion of a process environment that maps identifiers to string values, but no two systems define the contents of this environment in the same way. Even amongst Unixes there are significant differences; some define USER as the user's login name, while some define LOGNAME to have this value, and some (e.g., Linux) define both.
This is true, but it misses the point. In paticular, the example chosen is an especially poor one. Yes, the login name is held in different environment variables on different Unixes, but there is no need to use an environment variable to get the user name when the system property user.name is provided for just that purpose.

There are, in fact, very few common environment variables available across multiple systems, and on the whole it's not these that programmers are interested in accessing.

Why do I want to access environment variables from Java? The main reason is to interoperate with non-Java applications. Our software product is written in C, C++ and Fortran; it makes extensive use of environment variables to share information between applications. The main one is TIGDIR, which contains the top-level directory in which our software has been installed. Obviously this varies from platform to platform and from installation to installation. But the key point is that in a properly configured Tigress system the TIGDIR variable is always set.

Likewise, our software uses third-party libraries and integrates with third-party programs. Each of these may have environment variables which need to be set to communicate information about the configuration. And again we can guarantee that these variables will be present in any given installation.

The reviewer continues:

So while we could easily reinstate the getenv() method to provide access to the process environment, any code that invokes this method would inherently be dependent upon the operating system(s) upon which it is written and tested. One of the primary goals of the Java platform is to provide a set of APIs that work in essentially the same way across a wide variety of operating systems, so that developers can write portable programs. Providing access to information that is inherently OS-specific is not consistent with this goal.

Again, this misses the point. If we were writing Java applications in isolation there would be no need to use environment variables. But we aren't. In many cases we're writing Java applications to integrate with systems written in other languages which already use environment variables and which are already, by their nature, OS-dependent. Removing the functionality of getenv because it wouldn't be necessary in an ideal, Java-centric world is of no help to those of us who have to work in the real world of multi-platform, multi-language development.

Another reason cited for not supporting a getenv method is that not all platforms have environment variables. For example, in an article on the Java Developer Connection web site, we see:

Why this behavior? The fundamental reason is simply that the concept of environment variables is not portable. There's no guarantee that a given operating system supports them, and even if it does, the settings might not be uniform. For example, a particular Windows system might not have a USER variable, but instead have a USERNAME one. So assuming the presence of USER in a Java application will not work in a system that doesn't support it.
(Note again the use of the USER/USERNAME/LOGNAME example.) But there are many instances where the purity of the platform has been sacrificed for platform-dependent features. Look at java.io.File.listRoots. This is pointless on Unix systems. If Sun were to be consistent listRoots would be deprecated as an unnecessary platform dependency and some convoluted workaround would be proposed to replace it. Or what about the command line arguments that are passed to main? Not all systems support command line arguments, so by Sun's reasoning they are a platform dependency which should be removed.

Sun are arguing from the particular to the general. Because some environment variables vary from system to system we aren't allowed to use any. And because some platforms don't support environment variables Sun won't support their use on any platform. Furthermore, they are inconsistent even in applying their own flawed logic. Why disable getenv but not command line arguments?

The same article at the JDC proposes some workarounds for the lack of a functioning getenv. The first is to use the Java Native Interface to reimplement the functionality that Sun stole from us. This is fairly simple to do, though it is, of course, platform dependent. My own implementation is here. Having everyone implement their own version of getenv is one sure way to increase the amount of platform-dependentness in the world, something we thought Sun were agin.

The second proposed workaround is to pass environment variables to Java applications using the -D flag on the command line. Again this leads to a net increase in the amount of platform-dependentness in the universe, as each programmer has to write scripts to invoke their applications and ensure that the right environment variables are passed down to them.

The author then goes on to suggest a further 'workaround':

This approach works, but it's still dependent on environment variables. There is however a better way. In this approach, you use standard system properties. For example, to get the name of the current user, you can say:
This is then followed by a program to demonstrate how to use System.getProperty to get the user.name property, thus showing the irrelevance of the argument about USER and LOGNAME. Again, though, this misses the point. A few of the standard system properties (user.name and user.home, for example) are replacements for environment variables, but this is of no use to those of us who need to access non-standard environment variables.

The author also demonstrates how system properties can be placed in files and read in by applications. This is certainly an approach to getting information into and out of applications, but the author doesn't then make the link with environment variables, namely that on many platforms there is a command (set or env) that dumps the current environment in exactly the format that the Properties class uses to load properties from a file. This is a technique I've used to get environment variables into applications: dump the environment to a file, pass the name of the file to the application using a -D flag on the command line, and have the application read the contents of the file into a Properties object. (This approach is not without its problems, but it can have advantages over passing all the required variables on the command line.)

Finally, the author presents the use of Runtime.exec(String, String[]) as a way of passing environment variables to a command. There are none of the customary dire warnings that this might be a platform-dependent sort of thing to do. On the one hand we're told that getenv is platform-dependent, so Sun have kindly protected us from it by disabling it. But in the same breath Runtime.exec(String, String[]) is held up for our approbation as a jolly useful technique.

Environment variables are evil and environment variables are good. And for an encore he proves that black is white and gets killed on the next zebra crossing. Douglas Adams.

An aside. The documentation for java.lang.System.getenv has been incorrect for as long as I can remember. In all versions of the documentation, up to and including 1.4.2, the function description clearly states that getenv 'gets an environment variable' and that the return value of getenv is 'the value of the variable, or null if the variable is not defined'. This is not true: getenv does no such thing. All it does is throw an exception. I tried to raise this fault in the documentation as a bug, but the report was rejected as a duplicate of 4199068.

Section 4 of the Supplemental License Terms for the J2SDK reads, in part:

In the event that you create an additional class and associated API(s) ... you must promptly publish broadly an accurate specification for such API for free use by all developers.
Given the state of the documentation for getenv, this is clearly a case of 'do as we say, not as we do'.

I don't think Sun have a valid case for disabling getenv. Not having a functioning version of getenv results in additional work for programmers who need to interact with native applications on many platforms. This can only result in an increase in the amount of platform-dependent code. By striving to keep the Java platform 'pure' Sun are pushing system-dependent code out of Java and into other parts of applications where it's more fragile and less maintainable.

java.lang.System.getenv should be restored forthwith. In the meantime my native implementation of getenv is available here.


Ron Yorston
28th February 2005