Code documentation
Development Tools
Code Structure
Techniques and Standards
Help and Web Site
How To
Functional Info
Background Info

JMRI Help:

Contents Index
Glossary FAQ

Donate to JMRI.org

JMRI Code: Recommended Practices

This page contains miscellaneous info and pointers for JMRI developers.

Class Library Preferences

Collections

Take a few moments to learn about the different types of Java collections that are available ( List, Deque, HashMap, etc) in the java.util package.

Code Format

The Java Code Conventions (if that link is broken, try this one from the Internet Archive) for names, formatting, etc are really useful. If you find that you can't read a piece of code, these will help make it better.

Note that we have a few local conventions beyond those in the Java recommendations. You'll find them on other pages in this section, but for example, we recommend that you define the logger reference at the bottom of each file.

Deprecating Code

As development proceeds, sometimes old ways of doing things have to be replaced by new ways. In many cases, you can just change all the using code in our repository, and move forward. For general interfaces that might be used externally to JMRI, such as in scripts and CATS, we prefer to leave the old interface in place for a while, marking it as "deprecated" so that people can discover that it will eventually go away. (The current list of those is available as part of the Javadocs) The sequence is then:
  1. Write the new methods.
  2. Mark the old methods with both @deprecated and @Deprecated (see below). The associated Javadoc comment should tell people where to find the replacement.

    Note that if you mark an interface or super-class (i.e. abstract) class or method as deprecated, you should also mark all implementations of it as deprecated. That way, they won't themselves show as deprecated usages to fix, but code that uses them will.

  3. Start generating warnings for users (especially scripting users) by adding:
                jmri.util.Log4JUtil.deprecationWarning(log, "myMethod()");
    
    to the start of each deprecated method. This will put a warning in the log if that method is used during normal operation.
  4. Remove all uses in JMRI code, except tests, of these deprecated classes and/or methods until the deprecations report during compilation is clean. Tests are kept to make sure the code keeps working until the code is removed.

    In rare cases, this might not be practical. In that case you can add a line like

                @SuppressWarnings("deprecation")  // Thread.stop
    
    just before the usage. The comment is required. It should indicate the name of the deprecated member being used.

    We also use lines like this when using deprecated Java methods and deprecated methods from external libraries to indicate we know that we're doing it, and to add them to the technical backlog tracked by Jenkins.

    If you have tests for that method (you should!) you may need to modify the direct tests of the deprecated method; see the instructions on the JUnit page.

  5. Don't forget to migrate the Jython sample scripts!
  6. These changes can then be included in the next test release. As soon as possible, you should PR them so that people get notice of the deprecations. Make sure that the PR includes something for the release note about the deprecations to draw people's attention.
  7. Traditionally, we wait for four production releases (two years) before removing major/public deprecated methods and their tests. Implementation methods can be removed earlier, perhaps after two production releases (one year). (Truely internal methods don't need to go through this process at all; just remove them) When you do remove the deprecated code, make sure that the PR to do this includes an update to the release note about the removal; if it's a significant change, consider adding something to the warnings section.

Note that a deprecated item is meant to still work. Deprecated should only mean that you can't count on the deprecated item working in the future, so that it would be good to code away from it while it's still working.

There are two forms of marking something as deprecated in the code: Javadoc @deprecated tag and the @Deprecated annotation. Both allow you to add additional information. A nice discussion of the technicalities is here with additional discussion here. We strongly recommend using both of them like this:

/**
 * (Other Javadoc comments)
 * @deprecated As of 5.3.1, use {@ link #foo()} instead
 */
@Deprecated(since="5.3.1")
where the comment and since parameter contain the version in which the deprecation is applied. That lets the user easily know how long ago it was deprecated.

You can add a forRemoval=true clause to @Deprecated. We encourage you to do that once there are no references in the JMRI code. It will make the deprecation much more visible to people who attempt to use it because it defeats @SuppressWarnings.

Exceptions

Throwing Exceptions

When checking inputs (i.e. for valid parameter values) and you find a problem, what should you do? Generally, JMRI developers tend to throw an unchecked exception, i.e. IllegalArgumentException or similar.

Catching Exceptions

SpotBugs will object to code like this:
  try {
     // do something here
  } catch (Exception e) {
  }
with a REC_CATCH_EXCEPTION and/or a DE_MIGHT_IGNORE (less often DE_MIGHT_DROP). This is an example of two problems: Let's discuss those separately:

Catching the Exception class

There are two subcases here:

Empty catch block

What's an empty catch block trying to say?

Use of Optional

The Optional class is the right way to provide "a method return type where there is a clear need to represent 'no result,' and where using null is likely to cause errors". There's some good background on use of Optional in an introductory Java Magazine article. There's more info about how null makes errors likely is dicussed in a related article. As Tony Hoare - one of the giants of computer science - wrote, "I call it my billion-dollar mistake. It was the invention of the null reference in 1965. I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement."