Removing boilerplate (Lombok)

Java has a — probably deserved — reputation for being somewhat prone to boilerplate, obscuring the meaning of our code. There are however a couple of techniques we can use to reduce the boilerplate load.

One of the most common areas of boilerplate is the getters and setters. We can use project lombok to remove the necessity of writing these methods.

Solution

git checkout tags/030-removing-boilerplate-lombok
mvn clean package jetty:run

Exercise

  • in the pom.xml, add the following to <dependencies>:

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
        <scope>provided</scope>
    </dependency>
  • remove the getters and setters for Owner#name and add in its @lombok.Getter and @lombok.Setter annotations for its field instead:

    @javax.jdo.annotations.Column(allowsNull = "false", length = 40)
    @Property(editing = Editing.DISABLED)
    @Title(prepend = "Object: ")
    @Getter @Setter                 (1)
    private String name;
    1 added in, getName() and setName() removed
  • do likewise for the Owner#notes property

Removing boilerplate (Parameter names)

Apache Isis uses Java reflection to infer the names of domain object types and members. However, prior to Java 8 the name of method parameters was not accessible, and thus an annotation is required to provide this information.

For example, the Owners#findByName(…​) action is:

public class Owners {

    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
    @MemberOrder(sequence = "1")
    public Owner create(
            @Parameter(maxLength = 40)
            @ParameterLayout(named = "Name")
            final String name) {
        return repositoryService.persist(new Owner(name));
    }
    ...
}

The boilerplate here is the @ParameterLayout#named annotation.

Given we’re running on Java 8, though, we can remove this boilerplate. We do so by configuring the framework to also check for Java 8 metadata.

Solution

git checkout tags/040-removing-boilerplate--Paraname8
mvn clean package jetty:run

Exercise

  • in the IDE, ensure that the compiler specifies the -parameters flag.

    For example, in IntelliJ:

    intellij java compiler parameters
  • in the pom.xml, add the following to <dependencies>:

    <dependency>
        <groupId>org.isisaddons.metamodel.paraname8</groupId>
        <artifactId>isis-metamodel-paraname8-dom</artifactId>
        <version>1.16.2</version>
    </dependency>
  • in the isis-non-changing.properties file, add:

    isis.reflector.facets.include=\
        org.isisaddons.metamodel.paraname8.NamedFacetOnParameterParaname8Factory

    This extends the metamodel processing to use the new Java 8 reflection API.

    There’s further discussion on configuration properties in the next section.

  • Delete the @ParameterLayout(named=…​) attribute wherever it is now redundant.

Run the application and make sure it still runs fine.

Disable editing

The framework is configured using various properties files. The archetype splits these into an isis-non-changing.properties file, and an isis.properties file (under WEB-INF). The former is configuration properties that don’t vary by environment, the latter contains properties that will vary by environment (eg database connection parameters).

All of these configuration properties can be overridden using system properties.

The isis-non-changing.properties file includes this setting:

isis.objects.editing=false

This means that properties are non-editable by default, a good convention because in most cases we will want to use actions to mutate the state of the system. In Order we can therefore remove some boilerplate:

@javax.jdo.annotations.Column(allowsNull = "false", length = 40)
@Property(editing = Editing.DISABLED)
@Title(prepend = "Object: ")
@Getter @Setter
private String name;

Since editing is disabled by default, we can therefore remove the editing = Editing.DISABLED attribute.

Solution

git checkout tags/050-disable-editing
mvn clean package jetty:run

Exercise

  • remove the editing = Editing.DISABLED attribute:

    @javax.jdo.annotations.Column(allowsNull = "false", length = 40)
    @Property
    @Title(prepend = "Object: ")
    @Getter @Setter
    private String name;

    We could in fact remove @Property annotation entirely because the framework searches only for getters and setters. It’s a matter of taste.

Font Awesome Icons

While we are looking at isis-non-changing.prooperties, note that there is another configuration setting for font-awesome icons:

isis.reflector.facet.cssClassFa.patterns=\
                        new.*:fa-plus,\
                        add.*:fa-plus-square,\
                        create.*:fa-plus,\
                        update.*:fa-edit,\
                        delete.*:fa-trash,\
                        find.*:fa-search,\
                        list.*:fa-list

This is what causes the framework to automatically include icons for specifically named action names.

Similarly, bootstrap "btn" classes can also be associated with action names:

isis.reflector.facet.cssClass.patterns=\
                        delete.*:btn-danger

This UI hint can be specified using either annotations or the .layout.xml files, but specifying it centrally removes the boilerplate clutter and is a good way of ensuring consistent verbs are chosen for action names.

Implicit Action Annotations

Apache Isis has two ways to recognise actions, either by those that are explicitly annotated with @Action annotation, or alternatively implicitly as all public methods that do not otherwise represent properties, collections or supporting methods.

The archetype is configured to use the former, but we can switch to implicit actions and potentially save the need to add @Action annotation. Also (and probably more useful), in implicit mode the framework will tell us if we accidentally mis-spell any of the supporting methods (which we’ll see later in the tutorial).

Solution

git checkout tags/060-implicit-action-annotations
mvn clean package jetty:run

Exercise

  • in isis-non-changing.properties, set this configuration property to false:

    isis.reflector.explicitAnnotations.action=false