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.
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 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 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-MAC-ID where "MAC" is the MAC Address of the first network port on the computer and "ID" is the random digits in the identity of the profile. A unique private preferences directory is created for each computer a profile is used on.
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.
Preferences within a JMRI application may be stored as XML elements or as Java Preferences.
The XML elements for JMRI preferences are stored in two different files within a single space:
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.
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 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.
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.
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).
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).
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.
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.
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