#!/usr/bin/env bash # Facts about the Jakarta EE 11 API jars, read from Maven Central artifacts and Spring Boot's own BOM. No running application. set -uo pipefail cd "$(dirname "$0")/.." R="$HOME/.m2/repository"; W=$(mktemp -d); CEN=https://repo1.maven.org/maven2 get() { mvn -B -q dependency:get -Dartifact="$1" >/dev/null 2>&1; } jar() { echo "$R/$(echo "$1" | cut -d: -f1 | tr . /)/$2/$3/$2-$3.jar"; } # --- 01: what Spring Boot 4.1.1 manages ------------------------------------------------------------------- for i in 1 2 3 4 5; do code=$(curl -s -o "$W/bom.pom" -w '%{http_code}' $CEN/org/springframework/boot/spring-boot-dependencies/4.1.1/spring-boot-dependencies-4.1.1.pom); [ "$code" = 200 ] && break; sleep $((i*3)); done { echo "# 01-boot-managed-jakarta-apis (spring-boot-dependencies 4.1.1)" grep -E '<(jakarta-[a-z-]+|tomcat|jetty|hibernate|hibernate-validator|spring-data-bom|spring-framework)\.version>' "$W/bom.pom" | sed 's/^ *//; s/<\([^>]*\)>\(.*\)<\/.*/\1 = \2/' | sort echo "--- artifacts NOT in the BOM (0 = Spring Boot does not manage them)" for a in jakarta.data-api jakarta.enterprise.concurrent-api jakarta.enterprise.cdi-api jakarta.security.enterprise-api jakarta.faces-api jakarta.ws.rs-api; do printf ' %-36s %s\n' "$a" "$(grep -c "$a" "$W/bom.pom")" done } > output/01-boot-managed-jakarta-apis.txt # --- 02: API differences between the EE 10 and EE 11 versions of four specs --------------------------------- diffspec() { # name group artifact old new get "$2:$3:$4"; get "$2:$3:$5" for v in $4 $5; do j=$(jar "$2" "$3" "$v"); rm -rf "$W/x-$v"; mkdir -p "$W/x-$v"; unzip -q -o "$j" '*.class' -d "$W/x-$v" 2>/dev/null ( cd "$W/x-$v" && find . -name '*.class' ! -name 'module-info.class' | sed 's#^\./##; s#\.class$##; s#/#.#g' | sort > "$W/cls-$v.txt" javap -public -cp . $(cat "$W/cls-$v.txt" | grep -v 'package-info') 2>/dev/null | grep -E '^\s+(public|protected)|^(public|protected)' | sed 's/^ *//' | sort -u > "$W/mem-$v.txt" ) done echo "--- $1: $3 $4 -> $5" echo " classes added : $(comm -13 "$W/cls-$4.txt" "$W/cls-$5.txt" | wc -l) removed: $(comm -23 "$W/cls-$4.txt" "$W/cls-$5.txt" | wc -l)" echo " members added : $(comm -13 "$W/mem-$4.txt" "$W/mem-$5.txt" | wc -l) removed: $(comm -23 "$W/mem-$4.txt" "$W/mem-$5.txt" | wc -l)" comm -13 "$W/cls-$4.txt" "$W/cls-$5.txt" | sed 's/^/ + class /' | head -12 comm -23 "$W/cls-$4.txt" "$W/cls-$5.txt" | sed 's/^/ - class /' | head -12 } { echo "# 02-api-diff (javap -public over every class of the two jars; members are signature strings, so a changed signature counts as one removed and one added)" diffspec Servlet jakarta.servlet jakarta.servlet-api 6.0.0 6.1.0 diffspec Persistence jakarta.persistence jakarta.persistence-api 3.1.0 3.2.0 diffspec Validation jakarta.validation jakarta.validation-api 3.0.0 3.1.0 diffspec Annotations jakarta.annotation jakarta.annotation-api 2.1.1 3.0.0 diffspec Concurrency jakarta.enterprise.concurrent jakarta.enterprise.concurrent-api 3.0.0 3.1.1 } > output/02-api-diff.txt # --- 03: the new Persistence 3.2 EntityManager methods, by name -------------------------------------------- { echo "# 03-entitymanager-3.2 (methods on jakarta.persistence.EntityManager that 3.1.0 did not have)" for v in 3.1.0 3.2.0; do javap -public -cp "$(jar jakarta.persistence jakarta.persistence-api $v)" jakarta.persistence.EntityManager 2>/dev/null | grep '(' | sed 's/^ *//; s/jakarta.persistence.//g; s/java.lang.//g; s/java.util.//g' | sort > "$W/em-$v.txt"; done comm -13 "$W/em-3.1.0.txt" "$W/em-3.2.0.txt" } > output/03-entitymanager-3.2.txt # --- 04: Jakarta Data 1.0 API surface and the virtual-thread switch in Concurrency 3.1 --------------------- get jakarta.data:jakarta.data-api:1.0.2 { echo "# 04-data-and-concurrency" echo "--- jakarta.data-api 1.0.2: types in jakarta.data.repository" unzip -l "$(jar jakarta.data jakarta.data-api 1.0.2)" | awk '{print $4}' | grep -E '^jakarta/data/repository/[A-Z][A-Za-z]*\.class$' | sed 's#jakarta/data/repository/##; s#\.class##' | sort | sed 's/^/ /' for v in 3.0.0 3.1.1; do echo "--- jakarta.enterprise.concurrent-api $v: attributes of @ManagedExecutorDefinition" javap -public -cp "$(jar jakarta.enterprise.concurrent jakarta.enterprise.concurrent-api $v)" jakarta.enterprise.concurrent.ManagedExecutorDefinition 2>/dev/null | grep '()' | sed 's/^ *//; s/public abstract //; s/java.lang.//g' | sed 's/^/ /' done } > output/04-data-and-concurrency.txt # --- 06: does the Spring Data version Boot manages contain any Jakarta Data code? --------------------------- { echo "# 06-spring-data-and-jakarta-data (classes mentioning jakarta/data inside Spring Data jars from Spring Data 2026.0.1)" for a in spring-data-commons spring-data-jpa; do v=$(curl -s $CEN/org/springframework/data/$a/maven-metadata.xml | grep -o '4\.[0-9.]*' | tail -1 | sed 's/<[^>]*>//g') get org.springframework.data:$a:$v j="$R/org/springframework/data/$a/$v/$a-$v.jar" n=$(unzip -l "$j" 2>/dev/null | grep -c 'jakarta/data/') m=$(unzip -p "$j" 2>/dev/null META-INF/MANIFEST.MF | grep -c 'jakarta.data') echo " $a $v: entries under jakarta/data/: $n; manifest lines mentioning jakarta.data: $m" done } > output/06-spring-data-and-jakarta-data.txt # --- 07: Spring Boot's own switch for virtual threads (read from the shipped configuration metadata) ---------- get org.springframework.boot:spring-boot-autoconfigure:4.1.1 { echo "# 07-virtual-threads-property (spring-boot-autoconfigure 4.1.1 configuration metadata)" unzip -p "$(jar org.springframework.boot spring-boot-autoconfigure 4.1.1)" META-INF/spring-configuration-metadata.json \ | python3 -c "import json,sys; [print(p['name'], '(' + p.get('type','?') + ', default ' + str(p.get('defaultValue','none')) + '):', p.get('description','')) for p in json.load(sys.stdin)['properties'] if p['name']=='spring.threads.virtual.enabled']" } > output/07-virtual-threads-property.txt