JMRI: Application Preferences
Author: Bob Jacobsen
Keywords: JMRI technical code

# JMRI Preferences

Preferences in JMRI are stored in two different formats within three different spaces.

The access structure for JMRI preferences is based upon the access structure for project properties (called the Auxiliary Configuration for a project) and preferences within the NetBeans IDE.

There are two APIs, PreferencesManager and PreferencesPanel that implement the access to and control of preferences within a JMRI application.

# Preference Spaces

## The Current Profile

Most preferences are stored together within a single Profile. There are two spaces within a Profile, the shared space and the private space.

A Profile has a location, a name, and an identity. The identity is formed of two parts, a "safe" version of the name that can be used in file or directory names, and a random number (8 hexidecimal digits) to ensure uniqueness.

It is recommended that Profile locations have the extension `.jmri` to allow Profiles to be assigned a Uniform Type Identifier (UTI) in iOS and macOS. JMRI applications default to including the extension in the Profile location when creating one, but use of this extension is not enforced. See Startup Scripts for more details on UTIs and their use.

### Shared

Shared preferences are preferences within a single profile that are shared across multiple computers running that profile. An example of a shared preference is the use of the LocoNet connection "LocoNet".

Shared preferences are stored in the root of the `profile` directory within the Profile.

### Private

Private preferences are preferences within a single profile that are only used on a single computer running that profile. An example of a private preference is the use of the port "COM3" to get to the system the connection "LocoNet" uses.

Private preferences are stored in directories named `jmri-UUID-ID` where "UUID" is a universally unique identifier for the computer and "ID" is the random digits in the identity of the profile. A unique private preferences directory is created for each user for each computer a profile is used on.

## Settings Directory

The settings directory is where JMRI retains preferences that are not stored within a single profile, and which are only used on a single computer. An example of a preference in the Settings Directory are the preferences for how a JMRI application selects the Profile to use.

The settings directory is per user account on a single computer.

There's more information on the Profile File Structure page.

# Preference Formats

Preferences within a JMRI application may be stored as XML elements or as Java Preferences.

## XML Elements

The XML elements for JMRI preferences are stored in two different files within a single space: `profile.xml` (or `preferences.xml`) and `user-interface.xml`. Within the two different files, preferences are stored using the same means and same structure, however the intention of the two files are different.

A manager for preferences using either of these files manages a type of preference, for example, connection configurations or table state. The manager uses the methods in AuxiliaryConfiguration to get, put, or remove these XML elements. These methods all require the Element or the Element's name and if the Element is shared or private.

XML elements for JMRI preferences must all have valid and resolvable namespaces. These namespaces do not have to be unique to or defined within the jmri.org web site.

### properties.xml

`properties.xml` should contain preferences that have been explicitly set by the user within the JMRI Preferences user interface. These preferences tend to have significant impact on how an application operates and may require that the application be restarted to take effect.

`preferences.xml` only contains preferences that apply to all profiles operating under a single user on a single computer.

### profile.xml

`profile.xml` should contain preferences that have been explicitly set by the user within the JMRI Preferences user interface. These preferences tend to have significant impact on how an application operates and may require that the application be restarted to take effect.

Shared and private versions of `profile.xml` are retained within a single profile.

### user-interface.xml

`user-interface.xml` contains implicit preferences, such as window size and location, table sort order, and other captured user-interface state that is automatically restored as needed.

`user-interface.xml` is only private within a single profile.

## Java Preferences

JMRI application preferences that a simple (a number, boolean, or string), or a list of simple preferences, and can only be meaningfully used once within a single Profile are candidates for storage as Java Preferences.

JMRI uses a Properties format for storing Java Preferences in the file `profile.properties` within both Shared and Private spaces in a single Profile. The file `preferences.properties` is used for storing preferences that apply to all profiles run by a single user within a single computer.

JMRI further constrains Java Preferences by putting them in separate "namespaces" based on package names. This allows preferences in two different packages to use the same name for a preference (for example, the JMRI Web Server and Simple Server both have a "port" property, but because these are in different packages, they do not conflict).

# Accessing Preferences

If another class is already controlling the preferences you want to access, use that class's methods to access that preference. If not, create a class to manage that preference on behalf of other classes within a JMRI application.

Direct access to the preferences files outlined above is strongly discouraged, as direct access could result in one preferences manager overwriting another manager's preferences. Use the methods in ProfileUtils to access the AuxiliaryConfiguration and Java Preferences for a single Profile or the application settings (pass null for the profile to get application settings).

# Preferences Managers

PreferenceManagers are the primary handlers of preference retrieval and storage in JMRI applications.

The JMRI library has a number of Preferences Managers. These managers all provide an implementation of the PreferencesManager interface to allow them to be discovered. Preferences managers are loaded by the JMRI configuration manager, which uses the ServiceLoader API to create an instance of every preferences manager (this allows third-party JARs to implement preferences managers; see the ServiceLoader API docs for details on how this works and the jmri.spi API docs for details of how JMRI meets the ServiceLoader API requirements).

Because the mechanism by which preferences managers are discovered has no deterministic order, the preferences managers API includes methods that allow a preference manager to require that other specific preference managers are correctly initialized before it is initialized.

# Preferences Panels

PreferencePanels are the user interface for interacting with Preferences Managers within the Preferences window of a JMRI application.

When the Preferences window is initialized, it uses the ServiceLoader API to create an instance of every preferences panel (this allows third-party JARs to implement preferences panels).

Preferences panels provide hints for grouping the panels together, but do not provide hints for ordering preferences panels. Some groupings of preferences panels are well know within the Preferences window, and are given in a specific order, after which every other group of preferences panels are ordered alphabetically.

# Scripts

The following scripts included in the JMRI distribution show how to access preferences within a script:

- PreferencesExamples.py - Jython sample code that also demonstrates using another Jython script (preferences.py) within a script
- zeroconf-preferences.js - Working JavaScript or ECMAScript script that controls some ZeroConf network preferences