Properties loads textual key-value configuration; the application must still validate required keys, value types and allowed ranges.
Java Properties: validate configuration before changing application state
This program targets Java 8 without preview flags. Use a full JDK for compiler-based examples.
Loading is not validation
A receipt importer needs a bounded batch size and an output mode. Properties can parse both lines, but it has no knowledge that batch size zero is invalid. Parse into an immutable application configuration only after checking every required value, rather than changing a running service one property at a time.
The fixture uses StringReader so its input is already decoded text. The byte-stream overload has a different encoding contract. Duplicate property keys replace earlier values during loading; rejecting duplicate keys requires an input parser that preserves that evidence before Properties discards it.
Reject ambiguity at the boundary
The batch size must be between one and five hundred. The mode must be one of the declared strings. Unknown fields are rejected because a misspelled option should not quietly act like a default. Numeric overflow and nonnumeric input fail the whole configuration operation.
Properties also inherits operations that accept arbitrary objects. Inserting non-string keys or values through put can break assumptions made by getProperty and storage methods. Keep the mutable Properties instance inside the loading boundary, then pass typed fields to the importer.
Working program
import java.io.StringReader;
import java.util.Properties;
public class ValidatedImportConfig {
static int batch(String text) throws Exception {
Properties values = new Properties(); values.load(new StringReader(text));
for (String key : values.stringPropertyNames())
if (!key.equals("batch") && !key.equals("mode")) throw new IllegalArgumentException("Unknown setting");
String raw = values.getProperty("batch");
if (raw == null) throw new IllegalArgumentException("Missing batch");
int size = Integer.parseInt(raw);
if (size < 1 || size > 500) throw new IllegalArgumentException("Batch outside bounds");
String mode = values.getProperty("mode");
if (!"summary".equals(mode) && !"detail".equals(mode)) throw new IllegalArgumentException("Invalid mode");
return size;
}
public static void main(String[] args) throws Exception {
System.out.println(batch("batch=80\nmode=summary"));
try { batch("batch=0\nmode=detail"); }
catch (IllegalArgumentException rejected) { System.out.println("range rejected"); }
try { batch("batch=80\nmode=summary\nmod=detail"); }
catch (IllegalArgumentException rejected) { System.out.println("unknown rejected"); }
}
}Output
80
range rejected
unknown rejectedCosts and boundaries
Loading requires work proportional to the input size, plus retained keys and values. This fixture bounds the permitted batch value but does not bound the size of an arbitrary input stream. Production loading needs a byte or character budget as well. Keep credentials out of diagnostic dumps of configuration maps.
Common Mistakes
- Do not treat a successful load as proof of valid application values.
- Do not silently ignore misspelled configuration keys.
- Properties cannot recover duplicate-key evidence after replacement.
Read next
Validation exercises, Text file boundaries, Immutable data.
Apply this contract in Spring
Spring Boot configuration properties: bind values and reject bad startup input, Spring Boot profiles and property precedence: test the selected environment. These lessons keep framework assembly separate from the Java contract.
