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.