Configuring Java Applications
Note: maven & Java 17 or higher are assumed.
Operating System Environment Variables
You can name your environment variables however you want, but they are typically in screaming snake case by convention (and in certain style guides).
Using export
#!/usr/bin/env bash
export SERVER_PORT=8080
java -jar app.jar
Note: Using the export keyword makes environment variables available for all child processes.
Using env
#!/usr/bin/env bash
env SERVER_PORT=8080 \
APPLICATION_NAME=sample \
FOO=BAR \
java -jar app.jar
The benefit of using the env command is that it will set the environment only for the duration of the "java" command. This means that any other processes running in the same shell will not see these variables.
Accessing Environment Variables using Java
From the Java side of things, you can access environment variables using System.getenv() or getenv(java.lang.String)
public class EnvironmentVariablesExample {
public static void main(String... ignore) {
Map<String, String> environmentVariables = System.getenv();
// Do something with environmentVariables
}
}
Note: While it is trivial to read environment variables, there isn't a straightforward way to update or add environment variables in Java unless you spawn a subprocess
System Properties
Prefix | Description | Documentation | Example |
---|---|---|---|
- | Standard JVM properties supported by most JVM implementations. | Standard options for Java | -verbose |
-D | User-defined system properties. | Hopefully you're using something like Spring's Configuration Metadata | -Dserver.port |
-X | General purpose options that are specific to the Java HotSpot Virtual Machine. | Extra Options for Java | -Xcheck:jni |
-XX | Advanced runtime options, JIT options, Serviceability options, and garbage collection options. | Advanced Options for Java | -XX:+UnlockExperimentalVMOptions |
Note: Refer to the Java command documentation for your version of Java and your JVM vendor documentation (e.g. Azul) for more information about what options are available.
Considering user-defined properties system properties, you'd supply them as follows:
java -Dserver.port=8080 -jar app.jar
Setting system properties via Environment Variables
Environment Variable | Documentation Reference |
---|---|
JDK_JAVA_OPTIONS |
Using the JDK_JAVA_OPTIONS Launcher Environment Variable |
JAVA_TOOL_OPTIONS |
JAVA_TOOL_OPTIONS |
JAVA_OPTS |
Not a standard environment variable and won't take effect automatically. Despite that, you'll find it used all over the place e.g. Jenkins, various application servers like Tomcat, JBoss etc. |
For example, instead of passing system properties directly, you can configure them via the JDK_JAVA_OPTIONS
environment variable:
export JDK_JAVA_OPTIONS="-Dserver.port=value"
java -jar app.jar
Accessing System Properties using Java
From the Java side of things, system properties can be accessed via System.getProperties() or System.getProperty(java.lang.String):
public class SystemPropertiesExample {
public static void main(String... ignore) {
Properties systemProperties = System.getProperties();
// Do something with systemProperties
}
}
Command Line Arguments
Passed at runtime to your application e.g.
#!/usr/bin/env bash
java -jar app.jar "server.port=8080" "application.name=foo" "service.apiKey=bar"
Accessing System Properties using Java
From the Java side of things, the CLI arguments can be accessed from the main method:
public class CommandLineArgumentsExample {
public static void main(String... commandLineArguments) {
// Do something with commandLineArguments
}
}
File Based Configuration
Configuration files can be:
- Bundled with the application as a classpath resource.
- Looked up from a well-known location e.g. in the user's home directory.
- Specified at runtime using CLI Args, System Properties, or Environment Variables.
Properties files are a standard way to define configuration for Java applications with built-in support.
Parsing other configuration formats
Including properties files, the jackson-dataformats-text libraries can be used to parse other formats such as XML, JSON, YAML/YML, INI/TOML, CSV etc.
Include the dependency for your desired format in your POM:
<dependencies>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-toml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-toml</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-csv -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-properties -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-properties</artifactId>
</dependency>
</dependencies>
Assuming that you have the following Java class modelling your configuration:
class ApplicationConfiguration {
public String environment;
public Metadata metadata;
public DatabaseConfiguration databaseConfiguration;
record Metadata(String name, String version) {
}
record DatabaseConfiguration(String host,
String port,
String database,
String username,
String password,
String jdbcUrl) {
}
}
And that the following YAML file represents your configuration:
environment: "learning"
metadata:
name: "My Application"
version: "1.0.0"
databaseConfiguration:
host: "localhost"
port: "3306"
database: "my_database"
username: "service-account"
password: "something-hopefully-strong"
jdbcUrl: "jdbc:mysql://localhost:3306/my_database"
Then you can read it from the file system as follows:
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;
class ReadFromFileSystem {
public static void main(String... commandLineArguments) throws Exception {
final var configurationFilePath = Paths.get(System.getProperty("user.home"), "config.yaml");
// or: ObjectMapper, JavaPropsMapper, TomlMapper, XmlMapper, CsvMapper
final var mapper = new YAMLMapper();
try (var inputStream = Files.newInputStream(configurationFilePath)) {
Objects.requireNonNull(inputStream, configurationFilePath + " not found!");
var configuration = mapper.readValue(inputStream, ApplicationConfiguration.class);
// do something with configuration
}
}
}
Alternatively, if it is available on the classpath, you can read it as follows:
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import java.util.Objects;
class ReadFromClasspath {
public static void main(String... commandLineArguments) throws Exception {
final var classpathResource = "config.yaml";
// or: ObjectMapper, JavaPropsMapper, TomlMapper, XmlMapper, CsvMapper
final var mapper = new YAMLMapper();
try (var inputStream = ReadFromClasspath.class.getClassLoader().getResourceAsStream(classpathResource)) {
Objects.requireNonNull(inputStream, classpathResource + " not found!");
var configuration = mapper.readValue(inputStream, ApplicationConfiguration.class);
// do something with configuration
}
}
}
References / Additional Reading
- "Screaming Snake Case", Wiktionary, en.wiktionary.org/wiki/screaming_snake_case
- “Shell Style Guide.”, Google, google.github.io/styleguide/shellguide.html#constants-and-environment-variable-names
- "Export(1P)", Linux Manual Page, www.man7.org/linux/man-pages/man1/export.1p.html
- "Env(1)", Linux Manual Page, man7.org/linux/man-pages/man1/env.1.html
- “Java Development Kit Version 17 API Specification.”, Oracle, docs.oracle.com/en/java/javase/17/docs/api/index.html
- “Environment Variables (The Java™ Tutorials > Essential Java Classes > The Platform Environment)”, Oracle, docs.oracle.com/javase/tutorial/essential/environment/env.html
- "Standard options for Java", Oracle, docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#standard-options-for-java
- "Configuration Metadata", SpringBoot, docs.spring.io/spring-boot/docs/current/reference/html/configuration-metadata.html
- "Extra Options for Java", Oracle, docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#extra-options-for-java
- "Advanced Options for Java", Oracle, /docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#advanced-options-for-java
- "java command", Oracle, java
- "Command-Line Options", Azul, docs.azul.com/prime/Command-Line-Options
- "Using the JDK_JAVA_OPTIONS Launcher Environment Variable", Oracle, docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#using-the-jdk_java_options-launcher-environment-variable
- "JAVA_TOOL_OPTIONS", Oracle, docs.oracle.com/en/java/javase/17/docs/specs/jvmti.html#tooloptions
- "How to add java arguments to Jenkins", Cloudbees, docs.cloudbees.com/docs/cloudbees-ci-kb/latest/client-and-managed-masters/how-to-add-java-arguments-to-jenkins#_running_jenkins_inside_docker
- "catalina.sh", Tomcat, github.com/apache/tomcat/blob/main/bin/catalina.sh#L65
- "Configuring JVM Settings", JBoss, access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html/configuration_guide/configuring_jvm_settings
- “Jackson Dataformats Text”, FasterXML, github.com/FasterXML/jackson-dataformats-text
- "Why do JVM arguments start with "-D"?", StackOverflow, stackoverflow.com/questions/44745261/why-do-jvm-arguments-start-with-d
- "How do I use the JAVA_OPTS environment variable?", StackOverflow, stackoverflow.com/questions/5241743/how-do-i-use-the-java-opts-environment-variable
- "That's a lot of YAML", ghuntley, noyaml.com
- "Using the JDK_JAVA_OPTIONS Launcher Environment Variable", Oracle, docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#using-the-jdk_java_options-launcher-environment-variable
- "Properties", Oracle, docs.oracle.com/javase/tutorial/essential/environment/properties.html
- "System Properties", Oracle, docs.oracle.com/javase/tutorial/essential/environment/sysprop.html
- "A Complete Guide To The Bash Environment Variables", www.shell-tips.com/bash/environment-variables/#gsc.tab=0