Manual:Coding conventions/Java

From mediawiki.org

This page describes the coding conventions used within files of the MediaWiki codebase written in Java. See also the general conventions that apply to all program languages, including Java.

Build[edit]

Maven is used as a build tool. All projects should inherit from the discovery-parent-pom. It configures a number of static analysis tools and linters that will help in keeping projects coherent. Code conventions are embodied in the checkstyle configuration used by that pom.xml and will not be repeated here. The README file in that project describes how to use this parent pom.

Java projects hosted on Gerrit should be built by Jenkins by using the -maven-java8-docker template. Other templates are available to publish documentation and to manage the release process.

Java projects should be analyzed by SonarCloud.

Java version[edit]

Most of our projects are at the moment targeting Java 8. We are in the process of transitioning to Java 11.

Libraries[edit]

A number of libraries are commonly used throughout our various Java projects. Using the same libraries for similar use cases can help coherence across projects. That being said, use the list below as suggestions, not as a hard rule.

Production Code[edit]

Guava[edit]

Guava is used on a number of projects. Note that in some cases, we have dependencies that themselves rely on outdated Guava versions, which prevents us from using the latest version.The features of Guava that we use the most are:

Lombok[edit]

While Lombok is not strictly a library, it provides very useful syntactic sugar to Java. Since Lombok is an annotation processor executed at compile time, Lombok has no runtime dependencies and is thus not creating compatibility issues.

Lombok can be further configured by adding a lombok.config file at the package level, which might be particularly useful to make it play nice with Jackson. An example of such configuration:

lombok.equalsAndHashCode.callSuper = call
# disable SpotBugs, it does not like generated code by lombok
lombok.extern.findbugs.addSuppressFBWarnings = true
lombok.addLombokGeneratedAnnotation = true
# useful for jackson that will be able to use the all args constructor when deserializing
lombok.anyConstructor.addConstructorProperties = true

A few particularly useful features:

  • @Data: makes it easy to generate DTO / DO, taking care of all this getters / setters and having a proper, valid equals() and hashCode().
  • @SneakyThrows: a better way of dealing with checked exceptions than wrapping them in an unchecked exception.

Depending on your IDE, you might need to install a lombok plugin. See documentation for your specific IDE.

JSR 305[edit]

JSR 305 provides annotations to better document expected behaviour and help detect bugs. Those annotations are only compile time dependencies and not required at runtime, they don't generate dependency issues.

In particular:

JSON / XML[edit]

Jackson is used for most JSON and XML parsing / serialization, with the use of annotations.

HTTP client[edit]

Apache HttpComponents is used as an HTTP client.

As much as possible a single instance of HttpClient should be shared within an application. A custom User Agent string should be configured.

Testing[edit]

Unit Testing Framework[edit]

We mostly use JUnit as a testing framework. Some projects are using JUnit 4, while others are using JUnit 5. Some projects have dependencies on component specific testing libraries that don't support JUnit 5 yet. For projects that don't have this limitation, JUnit 5 should be preferred.

Mocking[edit]

Mockito is used as a mocking framework where needed.

HTTP Mocking[edit]

WireMock is used as an HTTP mock server, which allows testing code that relies on HTTP interactions. Note that WireMock makes it easy to also test various faults (delays, timeout, etc...).

Assertions[edit]

AssertJ is our main assertion framework. In almost all cases AssertJ should be preferred over plain assert, JUnit assertions or Hamcrest.

Various conventions[edit]

Unit vs Integration Tests[edit]

We follow the usual naming conventions to identify unit tests vs integration tests.

Managing null and defensive programming[edit]

As much as possible, null values should be avoided. Null Objects should be preferred. Parameters should be expected to be non null and not explicitly checked, unless they come from an untrusted caller. Parameters should be marked with @Nonnull / @Nullable to allow static analysis tools to validate nullity.