Managing Environment Variables in Java

Introduction

Variables are named memory locations. Their values are saved in memory, which we can't typically remember as they're not human-friendly and shift around. Though, if we name the memory location, such as a, it's a lot easier to remember.

Environment variables are a lot like usual programming variables, except that they're set somewhere outside the program. That can be used by the operating system, JVM, a microservice our program is using, etc.

More precisely, they're key/value pairs, where the key is what can be thought of as the name of the environment variable and value is, well, value. Their values are always strings.

When people refer to environment variables they usually mean those set by the operating system. You've likely had to deal with PATH and JAVA_HOME in the past - those are environment variables.

Environment variables vary across operating systems and it can sometimes be tough to make portable programs that rely on them, but nothing is making them inherently difficult to work with.

The operating system's environment variables have its analog in the JVM world - Properties. They're beyond the scope of this article, but bear mentioning as they are quite a similar concept at a smaller scale.

Querying Environment Variables

Your operating system stores its environment variables as key/value pairs. You can use System.getenv() to retrieve those values. If you use it without an argument, you'll get a Map object as the return value:

Map<String, String> env = System.getenv();

for(String envName : env.keySet()){
    System.out.println(String.format("%s : %s", envName, env.get(envName)));
}

Here's a truncated view of the results:

PROCESSOR_ARCHITECTURE : AMD64
MIC_LD_LIBRARY_PATH : C:\Program Files (x86)\Common Files\Intel\Shared Libraries\compiler\lib\mic
PSModulePath : C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
SystemDrive : C:
AWE_DIR : D:\Awesomium\1.6.6\
FPS_BROWSER_USER_PROFILE_STRING : Default
PATHEXT : .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
DriverData : C:\Windows\System32\Drivers\DriverData
HerokuPath : E:\Heroku
ProgramData : C:\ProgramData
ProgramW6432 : C:\Program Files

You can also pass it a String corresponding to the variable's name (key) and it will return the respective variable's value as a String:

System.out.println(System.getenv("NUMBER_OF_PROCESSORS"));
8

ProcessBuilder and Environment

Java has a Process class for dealing with operating system processes. To make it simpler to create a process, there's a ProcessBuilder class and you can simply "add" commands to its instance to run.

Each process can have its own environment. Your program will have its environment set by the operating system, but programs you start as processes can have a modified "hand-made" environment.

To edit an environment, you have to fetch its reference from your ProcessBuilder object by using the environment() getter. Like with reading environment variables from System, you'll get a Map and can then modify it with usual Map operations.

After creating an environment, we'll create a command. This depends on the operating system. Here, we have a rudimentary check which alters the command adequately:

// Setting up the environment...
ProcessBuilder processBuilder = new ProcessBuilder();
Map<String, String> env = processBuilder.environment();
env.put("PING_WEBSITE", "stackabuse.com");

if (System.getProperty("os.name").startsWith("Windows")) {
    processBuilder.command("cmd.exe", "/c", "ping -n 3 %PING_WEBSITE%")
} else {
    processBuilder.command("/bin/bash", "-c", "ping $PING_WEBSITE$");
}

try {
    // Starting the process...
    Process process = processBuilder.start();

    // Reading the output of the process
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream()))) {

        String line;

        while ((line = reader.readLine()) != null) {
             System.out.println(line);
        }
    }

    // Catch the exit code of our process
    int ret = process.waitFor();

    System.out.printf("Program exited with code: %d", ret);

} catch (IOException | InterruptedException e) {
    // Handle exception...
    e.printStackTrace();
}
Pinging stackabuse.com [172.67.218.223] with 32 bytes of data:
Reply from 172.67.218.223: bytes=32 time=12ms TTL=57
Reply from 172.67.218.223: bytes=32 time=12ms TTL=57
Reply from 172.67.218.223: bytes=32 time=15ms TTL=57

Ping statistics for 172.67.218.223:
    Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 12ms, Maximum = 15ms, Average = 13ms
Program exited with code: 0
Process finished with exit code 0

We've created a new environment variable called PING_WEBSITE with a fixed value. You could modify this program to set the value of PING_WEBSITE to user input, a command-line argument, or read the value from a file.

If you'd like to read on How to Parse and Map Command-Line Arguments in Java or How to Get Uset Input in Java, we've got you covered!

Conclusion

We have introduced the concept of environment variables, explained what they're used for and analogous concepts, as well as their system-dependent nature.

Then, we've printed the environment variables using Java's System.getEnv() method, as well as created processes using ProcessBuilder and executed commands that rely on environment variables.

Author image
Belgrade, Serbia