< Zurück | Inhalt | Weiter >

4.3.3 Java and Environment Variables

If these environment variables are available to all Linux processes, then how do we get at them from a Java program? Well, we can’t do it quite as directly as you might think. In previous (1.2 and older) versions of Java, the System class


image

1. If you are using csh (the C-shell, another Linux command-line interpreter), then the syntax is slightly different. Instead of export name=value use setenv name value (note the different keyword and no equal sign).


image

Example 4.2 Java program to dump environment variables

/*

* simple environment examiner

*/

import java.util.*;


public class AllEnv

{

public static void main(String [] args)

{

Properties props = java.lang.System.getProperties();

for (Enumeration enm = props.propertyNames(); enm.hasMoreElements();)

{

String key = (String) enm.nextElement(); System.out.print(key); System.out.print(" = ");

System.out.println(props.getProperty(key));

}


} // main


} // class AllEnv


image


had a getenv() method. Its argument was a String name of an environment variable and it returned the environment variable’s value as a String. This has been deprecated. In fact, an attempt to use getenv() in more recent versions of Java will result in an exception. Sun decided that this was too platform- specific; not all platforms have environment variables.

Now (Java 1.3 and beyond) the preferred approach is to use the getProperties() and getProperty() methods of the System class. How are these different from the getenv() approach? To a Linux developer, getenv() was easy and straightforward—just not very portable. To accommo- date other systems, Java defines a set of properties that are reasonable to expect to be defined on any system, and provides a Java property name for each one. To see the entire list, call the getProperties() method. It returns a Properties class, which is an extension of the Hashtable class. From this class you can get an Enumeration of the names, as Example 4.2 demonstrates.

Now compile and run this example:


$ javac AllEnv.java

$ java AllEnv


and you will get a long list of properties—in no particular order. They are kept in a hashtable and thus not sorted. Of course it would be easier to use this list if they were sorted. Linux to the rescue.


$ java AllEnv | sort


It’s often in simple little steps like this that one begins to see the power of Linux. In Linux, not every desirable feature has to be crammed into every pos- sible place where it might be used. Instead, features can be written once and connected to one another as needed. Here what we need is to have the list of properties sorted. We don’t need to worry that our class didn’t sort its output. In Linux we just connect the standard output of the Java program with a sort utility that Linux provides.

So what are all these properties? Many of them have to do with Java- related information (java.version, and so on), but a few are more general. Those that parallel the typical Linux environment variables are:

file.separator is the file separator (“/” on Linux).

path.separator is the path separator (“:” on Linux).

line.separator is the line separator (“\n” on Linux).

user.name is the user’s account name.

user.home is the user’s home directory.

user.dir is the user’s current working directory.

But that leaves out so many environment variables, especially the applica- tion-specific ones (e.g., CVSROOT). How would a Java program get at these?

Because of this new, more portable way to describe the environment, there is no easy way to get at other environment variables. There are a few approaches, but they are all indirect.

First, you can add to the properties list by defining new properties on the command line when invoking the program, for example:


$ java -Dkey=value AllEnv


You can list several properties on the line by repeating the -D parameter:


$ java -DHOME=/home/mydir -DALT=other -DETC="so forth" AllEnv


Instead of typing those values, you’d probably want to let the Linux shell put in the values from its environment. So you’d use shell variables, for example:


$ java -DHOME="${HOME}" -DALT="${ALT}" -DETC="${ETC}" AllEnv


assuming that HOME, ALT, and ETC have already been defined in the shell’s environment.2

If there are only a few variables that you need to pass to Java, put them on the command line as shown above. Put that command line into a shell script and use the script to invoke the program so that the parameters are supplied every time.

But if you want to access many or all of the environment variables then you may want to do something a little more complex. Notice the syntax of the output of the env command. It is in the same format (name=value) as are properties. So if we use a shell script to invoke our program, we can have it place all these values into a file by redirecting output, then open this file as a Java properties file and thus make all the name/value pairs accessible.

The following commands in a shell script attempt to do just that:


env > /tmp/$$.env

java -DENVFILE=/tmp/$$.env MyClass rm /tmp/$$.env


image

where MyClass is the Java program that you wish to run.



TIP

The shell variable $$ is the numeric process ID of the running process. This provides a unique ID during each invocation of the program. Each run of the script will have its own process and thus its own process ID. Thus a single user could execute this script multiple times concurrently without fear of collision with himself or others.


image

2. The quotations around the shell variables keep any embedded spaces as part of the variable’s value. The curly braces are not strictly necessary in this use.

image

4.4 The Properties Class 109

We remove the temporary file with the rm command in the last line of the script to avoid cluttering our /tmp directory with lots of these files.

But now we have to add code to MyClass to open the file defined by ENVFILE and read the properties it contains. This leads us naturally to the Java Properties class, the subject of our next section, where we’ll talk more about this example.


4.4 THE Properties CLASS

The Javadoc page for the Properties class describes it as “a persistent set of properties . . . saved to . . . or loaded from . . . a stream.” In other words, it is a hashtable (a set of name/value pairs) that can be read from or written to a stream—which typically means a file. (Other things can be streams, but for now, think “file”.)

The great thing about name/value pairs is how readable and usable they are. When they are written to a file, there’s no fancy formatting, no fixed width fields, no unreadable encryptions and special characters; it’s just name=value. You could say that the “=” and the newline are the special characters that pro- vide all the formatting you need. It means that you can type up a properties file with the simplest of editors, or even generate one quickly as we saw in the pre- vious example (here we use a simple filename):


$ env > propertyfile


Properties are also easy to use. Since they’re based on hashtables, there is no searching code to write. You call a method giving it the name, it returns the value.

If we pass in the name of the file via the -D parameter, then we can get that filename in Java with:


System.getProperty("ENVFILE");


where ENVFILE is a name that we made up and used on the command line:


$ java -DENVFILE=propertyfile MyClass


We could also have used:


$ java MyClass propertyfile


so that args[0]3 in the Java code to get the name of the file (see Section 4.2.1), but since we want to learn about properties, we’ll use the property methods here.

Now let’s open that property file (Example 4.3).


image

Example 4.3 Demonstrating the Properties class

import java.io.*; import java.util.*;


public class EnvFileIn

{

public static void main(String [] args)

throws IOException

{

String envfile = System.getProperty("ENVFILE", ".envfile");


BufferedInputStream bis = new BufferedInputStream(

new FileInputStream(envfile)); Properties prop = new Properties();

prop.load(bis); bis.close();


prop.list(System.out); // dumps the whole list to System.out


} // main


} // class EnvFileIn


image


Notice the way that we got the value for the environment file’s name. This form of the getProperty() call provides not only the name we are looking up (ENVFILE) but also lets us specify a default value in case the name is not found in the properties list. Here our default value is .envfile.

Just as it was a simple matter of using the load() method to read up an entire file of properties, so you can write out the entire list of properties to the


image

3. In C language, the arg[0] is the command being invoked; not so in Java. In Java, the first element of the array is the first argument of the command line (propertyfile in our example).

4.5 The Runtime Class 111

image


screen with the list() method. The argument to list() is either a PrintStream or a PrintWriter. System.out is a PrintStream, so that will work.

The format of the properties file is name=value. But it is also possible to put comments in a properties file. Any line beginning with a “#” is ignored. Try it.

It’s also easy to (re)write a file of properties with the store() method. The parameters are an OutputStream and a String; the latter will serve as a label for the parameters, written to an opening comment in the properties file.

If your program needs to examine the list of property names, you can get an Enumerator of the entire list via the propertyNames() method. Modify Example 4.3 to replace the list() call with a do-it-yourself version that uses the Enumerator returned from propertyNames() to list all the names and values. Hint: Use getProperty() on each name retrieved via the enumeration. The Java Properties class extends the java.util.Hashtable class.

This means, in part, that all the other Hashtable methods are available to a Properties class. Methods such as containsKey() or containsValue() can be helpful, as can isEmpty(). One caution, though. You should use setProperty() if you want to add values to Properties, rather than the Hashtable’s put() method. They do largely the same thing, but setProperty() enforces that its parameters are Strings. This is important if you want to write out the properties to a file, as it’s meant for Strings only.


4.5 THE Runtime CLASS

Let’s discuss one last way to get to the underlying (Linux) system information. Be warned, though, that this is the least portable approach of all we have mentioned.


4.5.1 exec()

Familiar to C/C++ programmers, the exec() call in the Java Runtime class does much the same thing. It gives you a way to start another program outside of the current Java Virtual Machine. In doing so, you can connect to its stan- dard in/out/err and either drive it by writing to its standard in, or read its results from its standard out. (Yes, that’s correct—we write to its input and read from its output. If that sounds wrong, think it through. Our Java code is


on the opposite side of the I/O fence. The external program’s output becomes our input.)

Example 4.4 shows a Java program that can invoke an arbitrary Linux program. The output of the program is displayed.



image

Example 4.4 Java program to execute any Linux program

import java.io.*;


public class Exec

{

public static void main(String [] args)

throws IOException

{

String ln;

Process p = Runtime.getRuntime().exec(args); BufferedReader br = new BufferedReader(

new InputStreamReader( p.getInputStream()));


while((ln = br.readLine()) != null) { System.out.println(ln);

}

System.out.println("returns:" + p.exitValue());


} // main


} // class Exec


image


The command-line arguments are taken to be the command to be execut- ed and its arguments. For example:


$ java Exec ls -l


Be aware that in this example, only the standard output is captured and displayed from the invoked process. Error messages written to standard err will be lost, unless you modify the program to handle this. We leave that as an exercise for the reader.

Check your Linux knowledge—see if you understand the distinction. If you invoke the sample Exec program as:

4.7 What You Still Don’t Know 113

image


$ java Exec ls -l *.java


the shell does the wildcard expansion before invoking the Java runtime. The

*.java becomes many files listed on the command line (provided that you have .java files in this directory). If you try to pass the *.java through liter- ally to exec(ls '*.java') it will likely return an error (which won’t be dis- played using our example code) and you’ll see a nonzero return status (e.g., 1). That’s because ls doesn’t expand the *. The shell does that. So ls is looking for a single file named *.java, which we hope doesn’t exist in your directory.