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 defineThis 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 propertyUSERas the user's login name, while some defineLOGNAMEto have this value, and some (e.g., Linux) define both.
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(Note again the use of theUSERvariable, but instead have aUSERNAMEone. So assuming the presence ofUSERin a Java application will not work in a system that doesn't support it.
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.