Available in versions: Dev (3.19) | Latest (3.18) | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11 | 3.10 | 3.9
API validation using the Checker Framework or Error Prone
Applies to ✅ Open Source Edition ✅ Express Edition ✅ Professional Edition ✅ Enterprise Edition
Java 8 introduced JSR 308 (type annotations) and with it, the Checker Framework was born. The Checker Framework allows for implementing compiler plugins that run sophisticated checks on your Java AST to introduce rich annotation based type semantics, e.g.
// This still compiles @Positive int value1 = 1; // This no longer compiles: @Positive int value2 = -1;
jOOQ has two annotations that are very interesting for the Checker Framework to type check, namely:
-
org.jooq.Support
: This annotation documents jOOQ DSL API with valuable information about which database supports a given SQL clause or function, etc. For instance, only Informix and Oracle currently support the CONNECT BY clause. -
org.jooq.PlainSQL
: This annotation documents jOOQ DSL API which operates on plain SQL. Plain SQL being string-based SQL that is injected into a jOOQ expression tree, these API elements introduce a certain SQL injection risk (just like JDBC in general), if users are not careful.
Using the optional jooq-checker
module (available only from Maven Central), users can now type-check their code to work only with a given set of dialects, or to forbid access to plain SQL.
Example:
A detailed blog post shows how this works in depth. By adding a simple dependency to your Maven build:
<dependency> <!-- Use org.jooq for the Open Source edition org.jooq.pro for commercial editions, org.jooq.pro-java-8 for commercial editions with Java 8 support, org.jooq.pro-java-6 for commercial editions with Java 6 support, org.jooq.trial for the free trial edition --> Note: Only the Open Source Edition is hosted on Maven Central. Install the others locally using the provided scripts, or access them from here: https://repo.jooq.org --> <groupId>org.jooq</groupId> <artifactId>jooq-checker</artifactId> <version>3.9.6</version> </dependency>
... you can now include one of the two checkers:
SQLDialectChecker
The SQLDialect checker reads all of the org.jooq.Allow
and org.jooq.Require
annotations in your source code and checks if the jOOQ API you're using is allowed and/or required in a given context, where that context can be any scope, including:
- A package
- A class
- A method
Configure this compiler plugin:
<plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> <fork>true</fork> <annotationProcessors> <annotationProcessor>org.jooq.checker.SQLDialectChecker</annotationProcessor> </annotationProcessors> <compilerArgs> <arg>-Xbootclasspath/p:1.8</arg> </compilerArgs> </configuration> </plugin>
... annotate your packages, e.g.
// Scope: entire package (put in package-info.java) @Allow(ORACLE) package org.jooq.example.checker;
And now, you'll no longer be able to use any SQL Server specific functionality that is not available in Oracle, for instance. Perfect!
There are quite some delicate rules that play into this when you nest these annotations. Please refer to this blog post for details.
PlainSQLChecker
This checker is much simpler. Just add the following compiler plugin to deactivate plain SQL usage by default:
<plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> <fork>true</fork> <annotationProcessors> <annotationProcessor>org.jooq.checker.PlainSQLChecker</annotationProcessor> </annotationProcessors> <compilerArgs> <arg>-Xbootclasspath/p:1.8</arg> </compilerArgs> </configuration> </plugin>
From now on, you won't risk any SQL injection in your jOOQ code anymore, because your compiler will reject all such API usage. If, however, you need to place an exception on a given package / class / method, simply add the org.jooq.Allow.PlainSQL
annotation, as such:
// Scope: Single method. @Allow.PlainSQL public List<Integer> iKnowWhatImDoing() { return DSL.using(configuration) .select(level()) .connectBy("level < ?", bindValue) .fetch(0, int.class); }
The Checker Framework does add some significant overhead in terms of compilation speed, and its IDE tooling is not yet at a level where such checks can be fed into IDEs for real user feedback, but the framework does work pretty well if you integrate it in your CI, nightly builds, etc.
Feedback
Do you have any feedback about this page? We'd love to hear it!