/
plugins.shtml
301 lines (270 loc) · 16.1 KB
/
plugins.shtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for Mac OS X (vers 31 October 2006 - Apple Inc. build 15.17), see www.w3.org">
<title>JMRI: Plug-in mechanisms</title>
<meta name="author" content="Bob Jacobsen">
<meta name="keywords" content=
"JMRI technical code plugins extension plug-in">
<!-- The combination of "Define" and {Header,Style, Logo and Footer} comments -->
<!-- are an arbitrary design pattern used by the update.pl script to -->
<!-- easily replace the common header/footer code for all the web pages -->
<!-- delete the following 2 Defines if you want to use the default JMRI logo -->
<!-- or change them to reflect your alternative logo -->
<!--#include virtual="/Style.shtml" -->
</head><!--#include virtual="/Header.shtml" -->
<body>
<div id="mBody">
<!--#include virtual="Sidebar.shtml" -->
<div id="mainContent">
<h1>JMRI: Extending the JMRI Programs</h1>
<p>The original goal of the JMRI project was to produce a library upon
which people could use to build their own applications. Although some
people do that, more use the existing applications such as DecoderPro
and PanelPro.<br>
We want to make this more flexible by providing a way to
extend those programs without having to rebuild them from scratch.
</p>
<p>There are three supported mechanisms that can be used to plug
additional capabilities into JMRI:
</p>
<ul>
<li><a href="#script">Script JMRI</a></li>
<li><a href="#service">Implement a Service Provider</a></li>
<li><a href="#jmriPlugin">Override jmri.JmriPlugin</a></li>
</ul>
See also the separate pages on
<a href="NewSystem.shtml">adding a new system</a> (i.e. another set of hardware that implements Turnouts, Sensors, clocks, etc)
and
<a href="NewType.shtml">adding a new type</a> (e.g. something in addition to Turnouts, Sensors, clocks, etc).
<h2 id="script">Script JMRI</h2>
<p><a href="../../tools/scripting">Scripting JMRI</a> is often the easiest
way to extend JMRI, however there are limitations to that which are
covered by the other mechanisms.
</p>
<p>The principal limitations to scripting JMRI are:
</p>
<ul>
<li>scripts can only be run late in the application start process</li>
<li>scripts cannot be used to define new connection types</li>
<li>scripts cannot be used to add items to the preferences window</li>
</ul>
<p>The details of scripting are <a href="../../tools/scripting">covered
elsewhere</a>.
</p>
<p>Examples of scripts that modify JMRI behavior are:</p>
<ul>
<li><a href="http://jmri.org/jython/AddButton.py">AddButton.py</a>
sample script adds a script button to the main window.
</li>
<li><a href="http://jmri.org/jython/DisableOpsMode.py">DisableOpsMode.py</a>
shows how how to modify the main window to remove the ops-mode
programming button.
</li>
<li><a href="http://jmri.org/jython/ReporterFontControl.py">ReporterFontControl.py</a>
sample script is an even more advanced example that changes
the appearance of items on panel screens.
</li>
</ul>
<h2 id="add">Adding Java Code</h2>
If you want to add a function that'll need significant code, ideally eventually as a
part of JMRI itself, the usual sequence is to write Java code
<ol>
<li>that creates objects to run as part of the
<a href="IntroStructure.shtml">usual JMRI structures</a>
<li>which are stored and loaded via
<a href="XmlPersistance.shtml">configurexml</a>
classes that load and store those objects into standard panel files
<li>optionally has a GUI that starts from an
<a href="Swing.shtml">action class</a>
fired from some button or menu item,
<li>optionally can fire that action at startup to open the GUI by selecting
it under "Peform action.." in the Startup pane in Preferences,
<li>optionally can have its own preferences pane to store more info, and
<li>eventually has
<a href="JUnit.shtml">CI unit tests</a>,
<a href="Javadoc.shtml">documentation</a> and
<a href="Help.shtml">help pages</a>.
</ol>
Operationally, that's often the best order to develop new function: First, write the
code (item 1) so that it runs inside JMRI, and use a script to create and start those objects.
There are two places to put it:
<ul><li>In a top level package, i.e. a new <code>java/src/mycooltool</code> directory
alongside <code>java/src/jmri</code> and <code>java/src/apps</code>. Your
Java files will start with "import mycooltool;" as a package declaration.
<li>In a new tools package within the
<a href="IntroStructure.shtml">JMRI code structure</a>,
i.e. a <code>java/src/jmri/jmrit/cooltool</code>
directory with your java files starting with
<code>import jmri.jmrit.coolltool;</code>.
</ul>
<p>Next, write the
<a href="XmlPersistance.shtml">configurexml</a>
load and store classes, so that once you've got the objects, you can store and reactivate them.
You still need the script (or an XML editor if the info is simple enough) to create
them the first time, though, so as a third step
<a href="Swing.shtml">write a GUI</a>
to create that.
That can be invoked by a one-line script at first, and eventually attached to a menu button.
<p>Once those first three steps are working and you've created a
<a href="Swing.shtml#display">GUI action class</a>,
you can connect that to "Peform action.." and "Add button to main window .."
in the Startup pane in Preferences
by having it extend
<a href="#StartupActionFactory"><code>apps.startup.StartupActionFactory</code></a>.
<p>
The
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/jmri/jmrit/sample">jmri.jmrit.sample</a>
package is an example of this.
(See <a href="http://jmri.org/JavaDoc/doc/jmri/jmrit/sample/package-summary.html">Javadoc</a>)
If contains:
<ul>
<li>A single functional class,
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/jmri/jmrit/sample/SampleFunctionalClass.java">SampleFunctionalClass</a>
who's only role is to save a sample string. Classes like this
would be built out to do the work of your project.
<li>A
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/jmri/jmrit/sample/configurexml/SampleFunctionalClassXml.java">configurexml.SampleFunctionalClassXml</a>
class that stores and loads the SampleFunctionalClass object
contents to a panel file.
<li>A
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/jmri/jmrit/sample/swing/SampleConfigPane.java">swing.SampleConfigPane</a>
class to provide the basis of a cGUI configuration pane.
This one just shows a label in it's window, but you
can build it out with whatever else is needed.
It's connected to the rest of JMRI so that you
can access configure connections to it in the Preferences.
<li>
A complete set of basic test classes. They just
check the constructors now, but can be built out as needed.
</ul>
We encourage you to
<a href="gitdeveloper.shtml">contribute your code to for inclusion in JMRI</a>.
That way, lots of people benefit. But if you don't want to do that,
you can package it up as a separate .jar file which can
just be dropped into the JMRI lib/ directory. By using the
approach listed above (and the services listed below), JMRI
will automatically pick it up and use it.
<h2 id="service">Implement a Service Provider</h2>
Sometimes what you want to add provides a very specific technical function.
Many of those can be (though historically, perhaps weren't) written as
Service Provider classes. When they can be done that way, they should be, because
it simplifies their connection to the rest of the code.
<p>Java contains a <a href="https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html">Service Loader</a>
that allows classes implementing a specific API to provide a service to
a Java application without requiring that the application have prior
dependencies defined for that service.
</p>
<p>Services are provided by creating a JAR for that service and appending
it to the JMRI classpath. See <a href="StartUpScripts.shtml">Startup Scripts</a>
for details on appending a JAR to the classpath and the
<a href="https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html">Service Loader</a>
documentation concerning what needs to be in that JAR.
</p>
<p>JMRI uses Service Loaders to allow a JMRI application to be extended
in specific ways:
</p>
<dl>
<dt id="StartupActionFactory"><a href="http://jmri.org/JavaDoc/doc/apps/startup/StartupActionFactory.html">StartupActionFactory</a>
<a href="images/StartUpActionExample.png"><img src="images/StartUpActionExample.png" align="right" height="148" width="256"></a>
</dt>
<dd>Startup Actions can be run at application start or attached to the
application's main window as a button. Implementations of this factory class
appear as possible selections for the Perform Action..
and Attach Action to Button.. selections in the Add button on the Startup pane in Preferences.
<p>
One example is
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/jmri/jmrit/roster/swing/RosterFrameStartupActionFactory.java">the RosterFrameStartupActionFactory class</a>
which opens the DecoderPro roster window.
They can also expose additional startup actions that can be selected by the user,
i.e. to select one of several possible connections to act on.
</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/apps/startup/StartupModelFactory.html">StartupModelFactory</a>
<a href="images/StartUpModelExample.png"><img src="images/StartUpModelExample.png" align="right" height="182" width="197"></a>
</dt>
<dd>Startup Models provide a mechanism to define optional items to be
automatically run during the startup process itself.
They can take user-specified arguments.
Implementations of this class appear under the "Add" button
in the Startup pane of the Preferences.
<p>
One example is
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/apps/startup/PerformActionModelFactory.java">the PerformActionModelFactory class</a>
which provides the Perform Action... item.
PerformActionModelFactory makes the
<a href="#StartupActionFactory">StartupActionFactory</a>
implementations available for the user to select. A PerformActionModelFactory object then remembers that
selection, and during JMRI startup invokes that StartupActionFactory item to do that particular thing.
Similarly,
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/apps/startup/CreateButtonModelFactory.java">the CreateButtonModelFactory class</a>
will take a user StartupActionFactory selection and attach it to a button at startup, for
execution later.
<p>
Implementations of this factory class provide the hooks so that the
Startup preferences can allow a user to set the parameters for a
given action.
</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/jmrix/ConnectionTypeList.html">ConnectionTypeList</a></dt>
<dd>Every manufacturer selectable when creating a configuration is
defined by a ConnectionTypeList service. Implement this (and other
required classes) to create a new system connection type. See
<a href="NewSystem.shtml">Adding a New System</a> for details.
</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/InstanceInitializer.html">InstanceInitializer</a></dt>
<dd>Add new factories for creating default instances of objects
managed by the <a href="http://jmri.org/JavaDoc/doc/jmri/InstanceManager.html">InstanceManager</a>.
</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/spi/JsonServiceFactory.html">JsonServiceFactory</a></dt>
<dd>The JMRI JSON services used in the JMRI web services can be extended
using service implementations of this class. See the
<a href="http://jmri.org/JavaDoc/doc/jmri/spi/JsonServiceFactory.html">JsonServiceFactory Javadocs</a> for details.
</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/swing/PreferencesPanel.html">PreferencesPanel</a></dt>
<dd>Additional preferences can be displayed in the preferences window by
providing an implementation of this class.</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/spi/PreferencesManager.html">PreferencesManager</a></dt>
<dd>Add a new preferences manager to JMRI. Preferences managers store,
retrieve, and validate preferences within a JMRI configuration
profile. If a plugin needs to take action very early in the JMRI
application startup sequence, it would need to provide a
PreferencesManager service.
</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/jmrit/beantable/signalmast/SignalMastAddPane.SignalMastAddPaneProvider.html">SignalMastAddPaneProvider</a></dt>
<dd>Provides the Add/Edit pane for a new type of SignalMast.
<p>If you define a new type of SignalMast in your code,
also define a service class of this type. It will automatically
be used to add or edit signals of your new type in the
<a href="../../../package/jmri/jmrit/beantable/SignalMastTable.shtml">SignalMast Table</a>.
<p>
See the
SignalMastAddPaneProvider class nested within the
<a href="https://github.com/JMRI/JMRI/blob/master/java/src/jmri/jmrit/beantable/signalmast/DccSignalMastAddPane.java">DccSignalMastAddPane</a>
class for an example.
</dd>
<dt><a href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html">HttpServlet</a>
with <a href="http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebServlet.html">WebServlet</a> annotation</dt>
<dd>Additional servlets in the web server can be added using these
mechanisms. Note that the WebServlet annotation needs to provide a
name and urlPatterns.</dd>
<dt><a href="http://jmri.org/JavaDoc/doc/jmri/server/web/spi/WebServerConfiguration.html">WebServerConfiguration</a></dt>
<dd>Additional file paths, redirections, explicitly blocked paths in the
JMRI web server can be specified by providing a service that
implements this.</dd>
</dl>
<h2 id="jmriPlugin">Override jmri.JmriPlugin</h2>
<p>The first instance of
<a href="http://jmri.org/JavaDoc/doc/apps/startup/StartupModelFactory.html">JmriPlugin</a>
will have its
<a href="http://jmri.org/JavaDoc/doc/jmri/JmriPlugin.html#start-javax.swing.JFrame-javax.swing.JMenuBar-">start</a>
method called while the JMRI application launches. This makes it
possible to allow the application's main window and main menu to be
extended. Use of this mechanism is limiting because JmriPlugin can only
be overridden once. Generally, the mechanisms further up are better.
</p>
<!--#include virtual="/Footer.shtml" -->
</div><!-- closes #mainContent-->
</div><!-- closes #mBody-->
</body>
</html>