1. File Preferences
UserPreferences.file(String) provides a file-based implementation of the Java Preferences API that removes the restrictive length limitations of the default implementation.
1.1. Motivation
The default Java Preferences API imposes the following restrictions:
-
Maximum key length: 80 characters
-
Maximum value length: 8,192 characters (8 KB)
-
Maximum node name length: 80 characters
These limits can be problematic when storing configuration data such as serialized table column preferences or other structured data that may exceed these limits.
1.2. Usage
// Then use preferences normally
Preferences prefs = UserPreferences.file("my.config.file");
prefs.put("my.very.long.key.name.that.exceeds.80.chars", "my huge value...");
prefs.flush(); // Writes to ~/.codion/my.config.file.json
1.3. File Storage
Preferences are stored in a JSON file at a platform-specific location:
-
Windows:
%LOCALAPPDATA%\Codion{filename}.json
-
macOS:
~/Library/Preferences/Codion/{filename}.json
-
Linux:
~/.config/codion/{filename}.json
(follows XDG Base Directory specification) -
Other:
~/.codion/{filename}.json
The file uses the following JSON format:
{
"normal.key": "normal value",
"very.long.key.that.exceeds.eighty.characters": "value",
"key.with.large.value": "... 100KB of text ...",
"key.with.newlines": "Line 1\nLine 2\nLine 3"
}
Note
|
When storing JSON data as a preference value (such as serialized column preferences), the JSON content is properly escaped and stored as a JSON string value. This double-encoding is handled automatically - you store and retrieve your JSON strings normally through the Preferences API. |
1.4. Features
-
No length restrictions - Keys and values can be of any length
-
JSON format - Human-readable and easily editable
-
Thread-safe - Safe for concurrent access within a single JVM
-
Multi-JVM safe - File locking ensures safe concurrent access from multiple JVMs
-
Atomic writes - Changes are written atomically to prevent corruption
-
Drop-in replacement - Uses the standard Java Preferences API
-
Full hierarchy support - Create nested preference nodes with paths
1.5. Hierarchy Support
The file preferences implementation supports the full Java Preferences node hierarchy:
Preferences root = UserPreferences.file("my.config.file");
// Create nested preference nodes
Preferences appNode = root.node("myapp");
Preferences uiNode = appNode.node("ui");
Preferences dbNode = appNode.node("database");
// Store preferences at different levels
uiNode.put("theme", "dark");
uiNode.put("font.size", "14");
dbNode.put("connection.url", "jdbc:postgresql://localhost/mydb");
dbNode.put("connection.pool.size", "10");
// Navigate to nodes using paths
Preferences ui = root.node("myapp/ui");
String theme = ui.get("theme", "light"); // "dark"
// List child nodes
String[] appChildren = appNode.childrenNames(); // ["ui", "database"]
// Remove entire node and its children
dbNode.removeNode();
root.flush();
The hierarchical structure is stored as nested JSON objects:
{
"myapp": {
"ui": {
"theme": "dark",
"font.size": "14"
},
"database": {
"connection.url": "jdbc:postgresql://localhost/mydb",
"connection.pool.size": "10"
}
}
}
1.6. Concurrency and Multi-JVM Access
The file preferences implementation is designed to be safe for concurrent access:
-
Within a single JVM: All operations are synchronized using internal locks
-
Across multiple JVMs: File locking ensures only one JVM can write at a time
-
Atomic writes: Changes are written to a temporary file and atomically moved
-
External changes: The
sync()
method reloads the file if modified externally
// JVM 1
Preferences prefs1 = UserPreferences.file("my.config.file");
prefs1.put("shared.value", "from JVM 1");
prefs1.flush();
// JVM 2
Preferences prefs1 = UserPreferences.file("my.config.file");
prefs2.sync(); // Reload to see changes from JVM 1
String value = prefs2.get("shared.value", null); // "from JVM 1"
The implementation uses a 5-second timeout for acquiring file locks to prevent deadlocks.