Software documentation

Development tools

Structure

Techniques and Standards

How To

Functional Info

Background Info

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.

This page attempts to describe the recommended structure and patterns, and point to examples of current best practices.

Names, NamedBeans, and Managers

The "NamedBean" concept is basic to JMRI. A NamedBean is a basic JMRI object that represents something, typically something like a specific Sensor or Turnout. Functionally, all the device object classes (Sensor, Turnout, ...) and their specific implementations (LnSensor, XNetTurnout, ...) inherit from the base NamedBean class.

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);
      
where name is the String name that the user provided, either a system name or user name, and sensor is the particular Sensor object being stored. When you need to reference the sensor itself, just do

      sensor = handle.getBean();
      
Please use 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 NamedBeanHandle will get updated and you're next getBean() call will get the right reference.

Code Organization

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.
ResourceBundles
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.

The "checkdepends.csh" development tool script is meant to check some for inadvertant dependencies, but we're far from clearing it.