-
Notifications
You must be signed in to change notification settings - Fork 5
Setup
I’m assuming you have the following ready.
- Java 8
- Maven
- a text editor or IDE you're comfortable with
I’m running the command-line commands in a Unix-like environment – give or take some minor adjustments this tutorial should also work on Windows.
Clone this repository, then check out the initial commit. I have tagged it as
initial.
git clone https://github.com/glts/safer-spring-petclinic.git
git checkout initial
This is our codebase. Now is a good time to check if we're starting out all
green. Before we do, let's make a first commit
e7ab16a
and upgrade to Java 8.
Run the install Maven goal to see if the build is fine. (This compiles the
production and test source, runs the tests, bundles up the WAR artefact, and
installs it in the local Maven repository.)
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.
Then 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. (‘Franklin’ is a name you can enter on the ‘Find Owners’ page.) Hit Control-C to stop the web server when you're done.
The annotations
@Nullable
and
@NonNull
are provided by the Checker Framework in the package
org.checkerframework.checker.nullness.qual. However, I am going to use the
equivalent annotations from the JSR 305
effort. JSR 305, ‘Annotations for Software Defect Detection’, was an attempt to
standardise annotations like @Nullable. Unfortunately, this effort fizzled
out. Still, it is the closest thing to a standard that we have today. (The
official repository is at https://code.google.com/p/jsr-305/.)
The JSR 305 annotations have a nicer package name than the ones from the Checker
Framework (javax.annotation), but bear in my mind that they are less powerful.
Only the Checker Framework annotations can be applied to type use. 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 relevant JAR in the <dependencies> section in the POM
in my second commit
4f7f00c.
(Don't worry about the missing <version>, Spring fills in a sensible value.)
The next step is to integrate the Checker Framework into our build, and activate the Nullness Checker.
It's best to keep this stuff separate from the original build, so I am putting all Checker Framework stuff in a separate Maven profile.
Keeping the Checker Framework separate is advisable for a number of reasons. First, the class files that the compiler produces when an annotation processor like the Nullness Checker is attached are different from the class files output without annotation processing. This may not be an issue depending on your situation – with annotation-heavy frameworks like Spring and Hibernate there is a possibility of unwanted interaction. Second, it goes without saying that in many professional environments compiling with a different Java compiler is not an option.
Here is the checker profile. ... section at the bottom of the POM (pom.xml),
right above the final <url> tag.
<profiles>
<profile>
<id>checker</id>
<dependencies>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>1.9.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>jdk8</artifactId>
<version>1.9.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<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>This is the checker profile. As you can see all this does is hook into the
compilation process by adding an annotation processor. The NullnessChecker is
the annotation processor that will perform the checking.
The Checker Framework comes with an annotated JDK, and we're putting it on the
classpath for the annotation processor. The annotated JDK has all method
signatures in the JDK, but annotated with things like @Nullable. The annotated
JDK isn't strictly necessary but it does help reduce false positives.
With that set up, the checker is ready. Run mvn -Pchecker compile and
turn the page.