J% Ports
Real Java applications with their embedded SQL and regular expressions rewritten as J% external types.
A language extension is only worth the trouble if it survives contact with code someone else wrote. These are existing Java applications with their embedded SQL and regular expressions — the string literals — converted into J% external types, so the claim that the queries could be checked at compile time could be tested on programs that were not written to make it true.
- applications
- 5
- lines of Java
- 18,738
- source files
- 107
- external types
- 61
- typed parameters
- 142
Measured on 2026-09-02 against e07be3b (external link, opens in a new tab) by npm run check:ports. Size figures cover the five applications; blank and comment-only lines are not counted. The external types include the benchmark's four.
Each port keeps the original application beside the converted one, so the cost of the conversion is visible rather than asserted. Across the five applications it comes to 25 files changed out of 107 — the rest of the code never learns that anything moved.
The applications
RUBiS
An auction-site benchmark modelled on eBay, from Rice University.
The biggest port, and the one that matters most: RUBiS is a research benchmark that other people wrote and that a lot of middleware papers measure themselves against, so its SQL was not arranged to make anything convenient. Every servlet builds its queries inline as strings — a bid, a comment, a category listing, the four separate updates an item can need — and each one became an external type sitting beside the servlet that uses it. It is also the port that shows the conversion scaling past a handful of queries: most of the servlets change by a few lines each, and the queries themselves are copied across unchanged.
- Java files
- 37
- lines
- 8,797
- external types
- 28
- typed parameters
- 68
- files changed
- 17
- lines changed
- +104 / −197
ExamJ
An examination system: students upload code, examiners retrieve and mark it.
A small internal application, and a good test precisely because nobody wrote it carefully. Its queries select and delete uploaded submissions by four different combinations of date, student and project, so the same table is addressed a dozen slightly different ways — the shape real code takes when it grows a query at a time. One of its columns is named group, which is a reserved word in SQL; the port keeps the statement exactly as it was written, and it compiles.
- Java files
- 15
- lines
- 3,838
- external types
- 12
- typed parameters
- 34
- files changed
- 5
- lines changed
- +48 / −111
jcrontab
A cron-style scheduler for Java, storing its job table in a database.
The clearest illustration of how little a port has to disturb. jcrontab is the largest application here by file count and all of its SQL is in one class, the data-access layer for the events table, so a single file changes and every other one is untouched — the scheduler itself never learns that its queries stopped being strings.
- Java files
- 43
- lines
- 3,354
- external types
- 5
- typed parameters
- 13
- file changed
- 1
- lines changed
- +30 / −61
Address book
Sun's demo address-book application, shipped as a JDBC sample.
The smallest complete application, and the one whose queries carry the most parameters: a contact has eleven fields, so its insert binds eleven values and its update binds twelve — those fields plus the row's id, and the widest statement in the corpus. That makes it the case where writing the parameters out as named, typed holes earns the most, since the PreparedStatement it replaces leaves twelve anonymous question marks to be filled in the right order.
- Java files
- 8
- lines
- 1,237
- external types
- 5
- typed parameters
- 25
- file changed
- 1
- lines changed
- +37 / −83
sdriver
A JDBC driver wrapper that detects SQL injection by comparing query signatures.
The only port that uses both modules, which makes it the one worth reading first. sdriver strips a query down to its shape and compares that signature against a stored one, so it holds regular expressions as well as SQL — patterns for comments, quoted strings, numbers and punctuation. In the port each Pattern.compile("…") becomes a type: new TriviaRegex() instead of Pattern.compile("\\+|-|\\."). A pattern that will not compile now stops the build rather than throwing the first time the driver is asked to inspect a query.
- Java files
- 4
- lines
- 1,512
- external types
- 7
- split
- 3 SQL · 4 regex
- typed parameters
- 2
- file changed
- 1
- lines changed
- +25 / −35
Benchmarking
Not an application: one query in four compiler configurations.
Checking a query costs build time, and how much depends on how hard you ask the compiler to look. This is the measurement: the same statement — select * from customer — compiled four ways, as plain syntax, against a schema file, against a live database connection, and with the SQL-security checks on. It is here because the honest version of "the compiler checks your queries" includes what that checking costs.
- Java files
- 9
- lines
- 176
- external types
- 4
What the ports demonstrate is mostly negative, which is the point: the queries are the same queries, the applications behave the same way, and the conversion is mechanical. What changes is when a mistake surfaces. A column renamed in the schema stops the build instead of throwing SQLException the first time that page is opened.
They double as the compiler's test corpus, which is a better test than any hand-written fixture: these are queries somebody actually shipped, with the formatting, joins and vendor quirks that come with real code — a column called group, a reserved word, among them. All 61 of them compile on every test run: 60 individually, one skipped because it wants a database server to be listening, and the five applications compiled again as whole source trees, which is the check that the queries still fit the programs they came out of.
Highlighting a port in your own pages
A port turns string literals into .jmod files, so writing about one means showing J% source — and no syntax highlighter ships a grammar for it. The stock ones get the notation that matters exactly wrong: each reads the # of a splice as the start of a line comment and greys out the rest of the line, which on ported SQL is usually the value that was being bound. The J% page carries a small highlight.js (external link, opens in a new tab) grammar to copy; register it and label the blocks:
import hljs from "highlight.js/lib/core";
import jmod from "./jmod-highlight.js";
hljs.registerLanguage("jmod", jmod);
document.querySelectorAll("pre code.language-jmod")
.forEach((el) => hljs.highlightElement(el));Label the blocks rather than leaving them to highlightAuto. A ported file is mostly SQL — the Java around it is four lines of package, imports and a declaration — so it scores poorly against every real grammar, and the dependable signal is its shape: an external … extends … line, which no other language has.
Source
The repository holds the ported sources. The original applications are their authors' work and are not redistributed here.