-
Notifications
You must be signed in to change notification settings - Fork 6
Setup
Make sure you have the following ready.
- Java 8
- Maven
- a text editor or IDE you’re comfortable with
Clone this repository and check out the initial commit. I have tagged it as
initial
.
git clone https://github.com/glts/safer-spring-petclinic.git
cd safer-spring-petclinic
git checkout initial
This is our codebase. Now is a good time to check whether we’re starting out all green.
Before we do, let’s upgrade to Java 8. In the
POM,
change java.version
to 1.8
. I’ve made that my first commit
e7ab16a
.
- <java.version>1.7</java.version>
+ <java.version>1.8</java.version>
By the way, if you cannot be bothered to re-enact all of my code commits, just
use git checkout
to jump directly to a commit in this repository. (At this
point, you would run git checkout e7ab16a
.)
In the project root, run the install
Maven goal.
mvn install
This can take quite a while the first time, because all the project’s dependencies need to be downloaded. At some point you should see the ‘BUILD SUCCESS’ message.
Now start the web server:
mvn tomcat7:run
Open a browser at http://localhost:9966/petclinic/
. This is the Pet Clinic.
You can browse around a little to get familiar with the app. Hit Control-C to stop the server when you’re done.
Note: If you’re on Linux, you may encounter the occasional server error as you explore the Pet Clinic. This is an unfortunate known issue but won’t matter for the rest of the tutorial.
The annotations
@Nullable
and
@NonNull
are provided by the Checker Framework in the package
org.checkerframework.checker.nullness.qual
. In this tutorial I am going to
deviate a little and use the equivalent annotations from the JSR
305 effort instead. Nullness annotations
have been reinvented a couple of times.
‘JSR 305: Annotations for Software Defect Detection’, was an attempt to
standardise such annotations. Unfortunately, this effort fizzled out. Still,
the JSR 305 annotations are the closest thing to a standard that we have
today and the Checker Framework understands them without trouble.
The JSR 305 annotations have a nicer package name (javax.annotation
) than the
ones from the Checker Framework, but bear in mind that they are less powerful.
Only the Checker Framework annotations can be applied to the use of a type.
The following, ‘a non-null List of nullable Strings’ cannot be expressed with
the JSR 305 annotations.
private @NonNull List<@Nullable String> list;
Anyway, I’m adding the appropriate <dependency>
in the POM in my second
commit
7d039ae
:
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<!-- no version necessary, provided by io.spring.platform:platform-bom -->
</dependency>
Should you decide to use the Checker Framework annotations substitute the following dependency instead:
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>1.9.13</version>
</dependency>
The next step is to integrate the Checker Framework into our build.
I think it is best to keep this stuff separate from the original build, so I will create a separate Maven profile for compiling with the Checker Framework.
Keeping the Checker Framework compilation separate makes sense for a number of reasons. The most important reason is that the class files that the compiler produces when an annotation processor like the Nullness Checker is attached are different from the class files produced without it. This is usually not an issue, but with annotation-heavy frameworks like Spring and Hibernate there is a small risk of unwanted interaction.
Here is the checker
profile. You can see in my third commit
877d2cc
that I’ve added it at the bottom of the POM, just before the final <url>
tag.
<profiles>
<profile>
<id>checker</id>
<dependencies>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>1.9.13</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>jdk8</artifactId>
<version>1.9.13</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessors>
<annotationProcessor>org.checkerframework.checker.nullness.NullnessChecker</annotationProcessor>
</annotationProcessors>
<compilerArgs>
<arg>-Xbootclasspath/p:${org.checkerframework:jdk8:jar}</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
All this does is define a new profile checker
, add the Checker Framework
dependencies, and register the NullnessChecker
annotation processor.
The Checker Framework comes with an ‘annotated JDK 8’, to be put on the
bootclasspath
for the annotation processor. The annotated JDK contains the
method signatures of the JDK, annotated as appropriate with @Nullable
and
@NonNull
. The annotated JDK isn’t strictly necessary but it does help reduce
false positives, so there goes.
With that set up, the checker
profile is ready to go.
Kick off the build with mvn -Pchecker compile
and turn the page.