JMRI: Patterns and Organization
JMRI has grown and evolved with time, and you can't always see the currently-preferred structure and patterns by looking at older code pieces.
Names, NamedBeans, and ManagersThe "NamedBean" concept is basic to JMRI. A NamedBean is a basic JMRI object that represents something, typically something like a specific Sensor or Turnout.
- They're called a "Bean" because they're a unit of interaction: Multiple pieces of code can work with one, it can be loaded and stored, etc.
- They're "Named" to make sure they're unique and retrievable: There's only one Turnout NamedBean with called "LT01", and it represents a specific addressed (named) layout object. See the page on Names for more on this.
To get access to a specific object (a NamedBean of a specific type with a specific name), you make requests of a manager: You ask a TurnoutManager for a specific Turnout. In turn, you access the managers through the common InstanceManager.
A user might want to reference a NamedBean via a user name, and in turn might want to change the specific NamedBean that user name refers to. "Yard East Turnout" might be "LT12" at one point, and later get moved to "CT5". To handle this, your code should use NamedBeanHandle objects to handle references to NamedBeans. They automate the process of renaming.
To do this, when you want to store a reference to a NamedBean, e.g. to remember a particular Sensor, Turnout, SignalMast, etc ask (through the InstanceManager) the NamedBeanHandlerManager to give you a NamedBeanHandle:
NamedBeanHandle<Sensor> handle = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBean(name, sensor);
nameis the String name that the user provided, either a system name or user name, and
sensoris the particular
Sensorobject being stored. When you need to reference the sensor itself, just do
sensor = handle.getBean();
getBean()every time you need to access the bean. Don't cache the reference from
getBean(). That way, if somebody does a "move" or "rename" operation, the
NamedBeanHandlewill get updated and you're next
getBean()call will get the right reference.
At the highest level, we separate test code from distributed code by putting both in separate directories within the development directory: "test" and "src". This makes it easy to compile a version without test code, to apply different tools to the two types of code, etc.
Within the source code itself, we separate out several types:
- The jmri package
- contains the basic interfaces and certain core implementations for the JMRI layout management concepts. It should contain minimal implementation code, and no non-JMRI references, esp. Swing code.
- The jmri.jmris package
- contains all the code for the server implementation for the JMRI interfaces.
- The jmri.jmrit package
- contains all the code for various non-system-specific JMRI tools and extensions. Most of the tools are in subpackages, but some appear directly in the package.
- The jmri.jmrix package
- contains all the code for connecting to specific layout hardware.
- The jmri.managers and jmri.implementations packages
- These provide default implementations for managers and other classes. This moves code out of the primary jmri package. These should not reply on apps, jmri.jmrix or jmri.jmrit.
- The jmri.util package
- Other common implementations of general use. Should not depend on jmri.jmrit or jmri.jmrix packages. The jmri.util.swing subpackage provides Swing utilities.
- Apps package
- For putting together an application, this can depend on anything.
- configurexml subdirectories
- These contain code for the XML configuration system. top-level packages, esp util & dependencies, apps
- swing subdirectories
- Contain Swing specific tools. Particularly outside the jmri.jmrit package, we are trying to separate Swing code from normal operational code. See the Swing page for more information.
- Help files
- The file structure for Help files echos the structure of the code. For more information, see the Help page on JavaHelp pages.
We use resource bundles for internationalization. The are colocated
with the code that references them, but we are moving to
a new naming convention. To reduce loading burden, we are
moving to a pattern where the
jmri.foo.FooBundle.properties file is addressed via a
static element in the jmri.foo.FooBundle class, separate
from the properties file itself. This reduces loading
time a lot!
Note that there are also a few resource bundles that are used for other purposes, indicated in their header comments.