Skip to Content
Course content

209: Working with Environment Variables and System Properties

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Look, if you're still hardcoding API keys or database passwords directly into your Java classes, we need to have a serious talk. Not only is it a security nightmare, but it makes your code incredibly rigid. You don't want to recompile your entire project just because you moved from a staging server to a production server.

Today, we're going to build a simple DatabaseConnectionManager. The goal is to make it "environment aware," meaning it figures out where to connect and how to authenticate based on the system it's running on, rather than what's written in the source code.

Pulling secrets from the environment

First, we need the database password. This is a secret, so it belongs in an environment variable. These are managed by the operating system, and Java can grab them using System.getenv(). I prefer this for secrets because it keeps the sensitive data completely out of the JVM startup arguments, where anyone running ps -ef on a Linux box could see them.

public class DatabaseConnectionManager {
    public void connect() {
        String dbPassword = System.getenv("DB_PASSWORD");
        
        if (dbPassword == null || dbPassword.isEmpty()) {
            throw new IllegalStateException("DB_PASSWORD environment variable is not set!");
        }
        
        System.out.println("Connecting to database using password: " + "****");
        // Connection logic would go here
    }
}

Tuning the app with System Properties

Now, unlike a password, something like a "connection timeout" isn't a secret. It's a configuration preference. For this, I use System Properties. These are passed to the JVM at startup using the -D flag. They are more flexible than environment variables for application-level tuning because they are specific to that JVM instance.

public void connect() {
    String dbPassword = System.getenv("DB_PASSWORD");
    
    // I'll provide a default value of "30" if the property isn't set
    String timeout = System.getProperty("db.timeout", "30");
    
    System.out.println("Connecting with a timeout of " + timeout + " seconds...");
    // ... remaining logic
}

Where I messed up: Properties vs. Env Vars

I'll be honest: when I first wrote this logic, I tried to run the app by setting export db.timeout=60 in my terminal and then calling System.getProperty("db.timeout"). It returned null, and I spent ten minutes wondering why Java was ignoring me.

Here is the mistake: I treated a System Property like an Environment Variable. System.getenv() looks at the OS shell variables, while System.getProperty() looks at the flags passed to the Java executable. If I want to use a System Property, I have to launch the app like this:

java -Ddb.timeout=60 -jar myapp.jar

If I want to use an environment variable, I do this:

export DB_PASSWORD=supersecret
java -jar myapp.jar

Putting it all together

To wrap this up, let's look at the final version of our manager. I've added a little bit of validation to ensure the app fails fast if the environment isn't configured correctly. There's nothing worse than a production app that starts up but fails silently the first time a user hits a database-dependent feature.

public class DatabaseConnectionManager {
    public static void main(String[] args) {
        DatabaseConnectionManager manager = new DatabaseConnectionManager();
        manager.connect();
    }

    public void connect() {
        // Secret: Environment Variable
        String password = System.getenv("DB_PASSWORD");
        if (password == null) {
            System.err.println("Error: DB_PASSWORD environment variable missing.");
            System.exit(1);
        }

        // Config: System Property (with default)
        String timeout = System.getProperty("db.timeout", "30");
        String url = System.getProperty("db.url", "jdbc:mysql://localhost:3306/mydb");

        System.out.println("Connecting to " + url);
        System.out.println("Timeout set to: " + timeout + "s");
        System.out.println("Authentication successful.");
    }
}



📋 Practical Task

Build a Secure Cloud Storage Client

Your task is to create a class called CloudStorageClient that simulates connecting to a cloud provider (like AWS or Azure). Your implementation must meet the following requirements:

  • API Key: Retrieve an API key from an environment variable named CLOUD_API_KEY. If it is missing, print an error message and terminate the program.
  • Region: Retrieve the storage region from a System Property named cloud.region. If this property is not provided, it should default to "us-east-1".
  • Debug Mode: Retrieve a System Property named cloud.debug. If this is set to "true", print a detailed "Debugging connection..." message; otherwise, remain silent.

Write the code and test it by simulating the JVM launch: java -Dcloud.region=eu-west-1 -Dcloud.debug=true CloudStorageClient (while ensuring your CLOUD_API_KEY is set in your environment).

Rating
0 0

There are no comments for now.

to be the first to leave a comment.