283 lines
14 KiB
Plaintext
283 lines
14 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
|
|
<title>JMRI: Patterns and Structure</title>
|
|
<meta name="author" content="Bob Jacobsen">
|
|
<meta name="keywords" content="JMRI technical code patterns structure">
|
|
<!--#include virtual="/help/en/parts/Style.shtml" -->
|
|
</head>
|
|
<body>
|
|
<!--#include virtual="/help/en/parts/Header.shtml" -->
|
|
|
|
<div id="mBody">
|
|
<!--#include virtual="Sidebar.shtml" -->
|
|
|
|
<div id="mainContent">
|
|
<h1>JMRI Code: Patterns and Organization</h1>
|
|
|
|
<p>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.</p>
|
|
|
|
<p>This page attempts to describe the recommended structure and patterns, and point to
|
|
examples of current best practices.</p>
|
|
<a id="namedbeans"></a>
|
|
<h2 id="NamedBean">Names, NamedBeans, and Managers</h2>
|
|
The "NamedBean" concept is basic to JMRI. A <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/NamedBean.html">NamedBean</a> is a basic JMRI object that
|
|
represents something, typically something like a specific Sensor or Turnout.
|
|
<ul>
|
|
<li>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.</li>
|
|
|
|
<li>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 <a href="Names.shtml">page on Names</a> for more on this.
|
|
</li>
|
|
</ul>
|
|
Functionally, all the device object classes (Sensor, Turnout, ...) and their specific
|
|
implementations (LnSensor, XNetTurnout, ...) inherit from the base <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/NamedBean.html">NamedBean</a> class.
|
|
<h3 id="handles">Naming and Handles</h3>
|
|
|
|
<p>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 <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/TurnoutManager.html">TurnoutManager</a> for a specific
|
|
Turnout. In turn, you <a href="IntroStructure.shtml">access the managers through the common
|
|
InstanceManager</a>.</p>
|
|
|
|
<p>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 <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/NamedBeanHandle.html">NamedBeanHandle</a> objects to
|
|
handle references to NamedBeans. They automate the process of renaming.</p>
|
|
|
|
<p>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:</p>
|
|
<span class="wide">
|
|
<pre><code>
|
|
NamedBeanHandle<Sensor> handle =
|
|
InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, sensor);
|
|
</code></pre></span>
|
|
<p>where <code>name</code> is the String name that the user provided, either a system name or
|
|
user name, and <code>sensor</code> is the particular <code>Sensor</code> object being stored.
|
|
When you need to reference the sensor itself, just do</p>
|
|
<pre><code>
|
|
sensor = handle.getBean();
|
|
</code></pre>
|
|
<p>Please use <code>getBean()</code> every time you need to access the bean. Don't
|
|
cache the reference from <code>getBean()</code>. That way, if somebody does a "move" or "rename"
|
|
operation, the <code>NamedBeanHandle</code> will get updated and your next <code>getBean()</code>
|
|
call will get the right reference.</p>
|
|
|
|
<h3>Bean properties</h3>
|
|
|
|
<p>NamedBeans usually have state, for example a <code>Sensor</code> may be Active or Inactive
|
|
(or Unknown or Inconsistent). This state is represented by one or more Java Bean properties.
|
|
Code in Java and Jython can use the <a href=
|
|
"https://docs.oracle.com/javase/tutorial/uiswing/events/propertychangelistener.html">PropertyChangeListener
|
|
pattern</a> to get notified when a given property changes. As an example, when a turnout is
|
|
configured for a feedback sensor, the <code>Turnout</code> object registers itself as a
|
|
change listener when the <code>Sensor</code>'s state property changes, and updates the
|
|
<code>Turnout</code>'s <code>"KnownState"</code> property.</p>
|
|
|
|
<p>The available Bean properties are defined in the abstract base class usually, for example
|
|
<code>AbstractTurnout</code> defines <code>"CommandedState"</code>,
|
|
<code>"KnownState"</code>, <code>"feedbackchange"</code>, <code>"locked"</code> and some more
|
|
at the time of this writing. These properties are not system-dependent. Some of the
|
|
properties are run-time only (e.g. state -- is the turnout thrown or closed?), while others
|
|
(e.g. turnout feedback mode) are configuration settings, selected by the user and saved
|
|
between sessions.</p>
|
|
|
|
<h3>Editing and saving NamedBeans</h3>
|
|
|
|
<p>NamedBeans are created and configured by the user using explicit actions. Most of the UI
|
|
for these actions is in the <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/jmrit/beantable/package-summary.html">jmri.jmrit.beantable</a>
|
|
package, using the generic <code>BeanTable{Frame,Pane,Model}</code> classes specialized for
|
|
the particular type, for example in the <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/jmrit/beantable/TurnoutTableAction.html"><code>TurnoutTableAction</code></a>
|
|
class. The configuration options present in the table and the edit dialog are specific to the
|
|
type (<code>Turnout</code>) but not the system.</p>
|
|
|
|
<p>The beans with the configured options are persisted into the Configuration (and Panel) XML
|
|
file when the user saves those. The persistence is handled by the system- and object-specific
|
|
ManagerXml class, for example <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/loconet/configurexml/LnTurnoutManagerXml.html">LnTurnoutManagerXml</a>
|
|
or <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/openlcb/configurexml/OlcbTurnoutManagerXml.html">OlcbTurnoutManagerXml</a>,
|
|
which heavily rely on shared code in <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/managers/configurexml/AbstractTurnoutManagerConfigXML.html">
|
|
AbstractTurnoutManagerConfigXML</a>, but can introduce system-specific functionality and work
|
|
together with the system- and object-specific manager (e.g. <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/jmrix/openlcb/OlcbTurnoutManager.html">OlcbTurnoutManager</a>)
|
|
to achieve this.</p>
|
|
|
|
<p>The base class handles persisting the user settings that were entered via the
|
|
BeanTable.</p>
|
|
|
|
<h3>System-specific properties</h3>
|
|
|
|
<p>Adding a system-specific property requires using a generic API, because the code in the
|
|
<code>jmrit.beantable</code> package <a href="IntroStructure.shtml">cannot depend</a> on the
|
|
jmrix.system-specific packages. All NamedBeans have a <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/NamedBean.html#setProperty-java.lang.String-java.lang.Object-">
|
|
setProperty</a> and <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/NamedBean.html#getProperty-java.lang.String-">getProperty</a>
|
|
method where arbitrary values can be saved for any string key. These properties are persisted
|
|
into the XML file by the base class of the ManagerXml, so no code needs to be written for it.
|
|
A variety of basic types can be chosen for the property value, such as <code>Integer</code>
|
|
or <code>Boolean</code>, and will be correctly persisted and recovered upon load. Custom
|
|
types might work if they have a <code>toString()</code> method and a constructor that takes
|
|
only one <code>String</code> as argument and these correctly serialize and parse the data
|
|
value.</p>
|
|
|
|
<p>To allow the user to edit these system-specific properties, a specific
|
|
<code>Manager</code> can declare the set of supported properties by returning appropriately
|
|
filled <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/NamedBeanPropertyDescriptor.html">NamedBeanPropertyDescriptor</a>
|
|
objects from the <a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/Manager.html#getKnownBeanProperties--">getKnownBeanProperties</a>
|
|
method. This descriptor tells the BeanTable that additional columns need to be created, what
|
|
type of data those columns will hold and what should be the column names (printed in the
|
|
header). The system-specific columns are hidden by default from the user; the user needs to
|
|
click a checkbox in the bottom row to show them; the checkbox only appears if there are
|
|
system-specific properties. The column name has to be filled with a localized string that
|
|
should come out of the respective <code>Manager</code>'s <code>Bundle</code>.</p>
|
|
|
|
<h2 id="SPI">Service Providers</h2>
|
|
Java provides a capability, using a "Service Provider Interface", that allows us to reduce
|
|
the complexity of our code by having the code itself discover what pieces are available and
|
|
need to be installed.<br>
|
|
For background on this, see the tutorial sections on "<a href=
|
|
"https://docs.oracle.com/javase/tutorial/ext/basics/spi.html">Creating Extensible
|
|
Applications</a>" and "<a href=
|
|
"https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html">Introduction to the Service
|
|
Provider Interfaces</a>".
|
|
<p>For example, by annotating a class with</p>
|
|
|
|
<pre><code>
|
|
@ServiceProvider(service = PreferencesManager.class)
|
|
</code></pre>the JMRI Preferences System automatically will discover that the class uses the
|
|
preferences and should be hooked up. This means that we don't have to modify the Preferences
|
|
classes to look up each new class using them, and that we can (eventually) more incrementally build
|
|
and distribute JMRI.
|
|
<p>Available patterns (links are to the JavaDoc for the interface or class specifying the
|
|
functionality):</p>
|
|
|
|
<dl>
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/server/web/spi/WebManifest.html">ConnectionTypeList</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html">HttpServlet</a>
|
|
</dt>
|
|
|
|
<dd>(Note this is a Java-defined class, not a JMRI-defined interface)</dd>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/InstanceInitializer.html">InstanceInitializer</a>
|
|
</dt>
|
|
|
|
<dd>Provides a way for the JMRI InstanceManager to create an instance of the class when one
|
|
is requested</dd>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/spi/JsonServiceFactory.html">JsonServiceFactory</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/spi/PreferencesManager.html">PreferencesManager</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/swing/PreferencesPanel.html">PreferencesPanel</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/jmrit/beantable/signalmast/SignalMastAddPane.html">SignalMastAddPane</a>
|
|
</dt>
|
|
|
|
<dd>Provide a type-specific pane used to add/edit the information in a SignalMast concrete
|
|
object</dd>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/util/startup/StartupActionFactory.html">StartupActionFactory</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/util/startup/StartupModelFactory.html">StartupModelFactory</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/server/web/spi/WebManifest.html">WebManifest</a>
|
|
</dt>
|
|
|
|
<dt>
|
|
<a href=
|
|
"https://www.jmri.org/JavaDoc/doc/jmri/server/web/spi/WebServerConfiguration.html">WebServerConfiguration</a>
|
|
</dt>
|
|
</dl>
|
|
|
|
<p>To see more on the places where SPI can be used to insert functionality into
|
|
JMRI, see the
|
|
<a href="plugins.shtml">Plug-in page</a>.
|
|
|
|
<p>Classes that provide SPI also have to be registered with the system so they can be found.
|
|
JMRI does this with entries inside files in the
|
|
<code>target/classes/META-INF/services/</code> directory. These entries are created
|
|
automatically during the JMRI build process from the annotations in the source files. JMRI
|
|
then packages those into the appropriate level of <code>jmri.jar</code> file, where they will
|
|
eventually be found and acted on.</p>
|
|
|
|
<p>To access them:</p>
|
|
<div class="wide"><code>
|
|
<pre>
|
|
java.util.ServiceLoader.load(OurServiceClass.class).forEach((ourServiceObject) -> {
|
|
// access the service object via ourServiceObject
|
|
});
|
|
</pre></code></div>
|
|
|
|
<a id="javascript"></a>
|
|
<a id="typescript"></a>
|
|
<h2>JavaScript and TypeScript Code</h2>
|
|
|
|
<p>JavaScript code for use by the web server should be put in the
|
|
<code>web/js</code> directory, which is where our web pages are served from via the
|
|
<a href="../../web/index.shtml">JMRI web server</a>.
|
|
|
|
<p>TypeScript code for use by the web server should be put in the
|
|
<code>web/ts</code> directory. This will be compiled as needed
|
|
by the <code>ant typescript</code> target. The results are put in the
|
|
<code>web/js</code> directory.</p>
|
|
|
|
<p>To run the <code>ant typescript</code> target and compile the TypeScript files,
|
|
a TypeScript compiler needs to be installed on the computer. For more information
|
|
on TypeScript and how to install it, see the
|
|
<a href="https://www.typescriptlang.org/">TypeScript web page</a>.</p>
|
|
|
|
<p>JavaScript code can also be used for scripting. By default, these should be put
|
|
into the <code>jython/</code> directory, though of course people can run scripts from
|
|
any location they choose.</p>
|
|
|
|
<!--#include virtual="/help/en/parts/Footer.shtml" -->
|
|
</div>
|
|
<!-- closes #mainContent-->
|
|
</div>
|
|
<!-- closes #mBody-->
|
|
<script src="/js/help.js"></script>
|
|
</body>
|
|
</html>
|