Heart containing Coding Chica Java 101

Global State of the Server! Java Environment Variables

TIP: References Quick List

Table of Contents

  1. Table of Contents
  2. Introduction
  3. Use Case
  4. Anti-Patterns
    1. Complex Structures
    2. Too Many Environment Variables
    3. Not Part if Infrastructure as Code / SCM
    4. Sensitive Values
    5. Testing Considerations
  5. Java Syntax
  6. Checking State
  7. Summary

Introduction

If simple string values need to be passed to a Java application and they do not contain sensitive data, then environment variables may be an option.

Use Case

Non-sensitive data needs to be looked up during the deployment, and is therefore, not a good fit for a configuration file.

  • Infrastructure as code (Terraform, Ansible, etc.) needs to query a back-end to retrieve:
    • Database or other back-end endpoint that doesn’t follow some predefined domain name format consistently,
    • Settings that change outside our application’s deployments, like:
      • Testing users / applications ID
      • Availability status of optional back-ends
  • Configuration file path or name
  • Etc.

Anti-Patterns

Complex Structures

Environment variables must contain Strings. Complex structures are not well suited to environment variables. For example, a YAML file might be a better fit.

Too Many Environment Variables

Having lots of environment variables can make finding issues more difficult. Especially if more than one environment variable can produce similar undesirable behavior in an application, spotting which environment variable is to blame may be difficult.

This problem can be doubly hard if the way the environment variables are being defined doesn’t prevent duplicate declarations.

Not Part if Infrastructure as Code / SCM

For both troubleshooting perspective and disaster recovery needs, if environment variables are not created & updated using infrastructure as code deployments, then they should be used with caution.

Setting up environment variable values for an environment should not require tribal knowledge or reference outdated documentation. If all servers in a cluster went down, how quickly could your team recover the expected names and values?

Sensitive Values

How are the environment variables being sent to your application, are their values:

  • Present in the source code (infrastructure or application repos)
  • Available to anyone logged into the server?
  • Displayed unmasked in the server configuration?
  • Logged during process startup?

Something else to ask yourself:

  • Could a disgruntled team member gain access to resources or mask their own malicious actions using the environment variable values? How many people have access to this secret?

Use caution when storing sensitive values as environment variables. Look into alternative, secret-specific storage mechanisms.

For example, GitHub Actions has separate handling for secrets used during those builds/deployments.

Testing Considerations

Java assumes that the environment will be setup before the process is started and remain consistent while running the process.

While it is possible (although in a hacky-way) to vary environment variable values during testing, it is not as common and can be a bit problematic:

  • Test performance impacts, as modifying environment variables during a test means that is not thread safe and cannot be run in parallel with other tests,
  • The environment variable value update within a running JVM may not take effect immediately or at all, which may lead to flaky tests, even if not run in parallel.

Instead, consider configuration files, which are often marshaled into plain old Java objects (POJOs) and can often vary between tests in a parallel execution.

Also, you can write unit tests that confirm the business logic for the values within your configuration files, so that testing the configuration file values can shift left – into the build.

I am not going into how the hacky-approach may update Java environment variables, as it really isn’t expected for a running Java process.

Java Syntax

To retrieve an environment variable in Java, simply:

String javaHome = System.getenv("JAVA_HOME");

This retrieves the JAVA_HOME environment variable.

public static String getenv(String name)

Gets the value of the specified environment variable. An environment variable is a system-dependent external named value.

If a security manager exists, its checkPermission method is called with a RuntimePermission("getenv."+name) permission. This may result in a SecurityException being thrown. If no exception is thrown the value of the variable name is returned.

System properties and environment variables are both conceptually mappings between names and values. Both mechanisms can be used to pass user-defined information to a Java process. Environment variables have a more global effect, because they are visible to all descendants of the process which defines them, not just the immediate Java subprocess. They can have subtly different semantics, such as case insensitivity, on different operating systems. For these reasons, environment variables are more likely to have unintended side effects. It is best to use system properties where possible. Environment variables should be used when a global effect is desired, or when an external system interface requires an environment variable (such as PATH).

On UNIX systems the alphabetic case of name is typically significant, while on Microsoft Windows systems it is typically not. For example, the expression System.getenv("FOO").equals(System.getenv("foo")) is likely to be true on Microsoft Windows.

Parameters: name – the name of the environment variable

Returns: the string value of the variable, or null if the variable is not defined in the system environment

Throws:

NullPointerException – if name is null

SecurityException – if a security manager exists and its checkPermission method doesn’t allow access to the environment variable name

See Also:

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#getenv(java.lang.String)

The Javadoc above talks about how environment variables are expected to be globally available. Be aware that any processes that are spawned also receive a copy of the environment variables.

Checking State

In addition to the possible exceptions above, remember to sanity check the value of the variable after it is retrieved.

  • Is it null or populated?
  • Does it match the expected format/valid values?
  • etc.

Summary

Environment variables can be used for simple, non-sensitive configuration in Java. However, they should still be part of version control, such as via infrastructure as code. Over use, lack of version control, poor documentation or accidentally overwriting environment variables can lead to issues while troubleshooting undesirable behavior or in disaster recovery scenarios.

Global State of the Server! Java Environment Variables

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.