-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
209: Working with Environment Variables and System Properties
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).
There are no comments for now.