diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0f73c175a..f5a7b15b8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,19 +11,27 @@ env:
MVN_CMD: ./mvnw --no-transfer-progress -B
jobs:
+ # Oracle only provides JDK 17+, so test against 17, 21, and 25
oracle:
- strategy:
- matrix:
- java: [ '17' ]
runs-on: 'ubuntu-latest'
- name: jdk-${{ matrix.java }}-oracle
+ name: oracle
steps:
- uses: actions/checkout@v4
+ # Install test-only JDKs first; each call appends an entry to ~/.m2/toolchains.xml
- name: Set up JDK
- uses: actions/setup-java@v4.7.0
+ uses: actions/setup-java@v4
+ with:
+ distribution: oracle
+ java-version: |
+ 21
+ 25
+ # Build JDK last so it ends up as the active JAVA_HOME / PATH entry
+ - name: Set up JDK 17 (build)
+ uses: actions/setup-java@v4
with:
distribution: oracle
- java-version: ${{ matrix.java }}
+ java-version: '17'
+ cache: 'maven'
- name: Install softhsm2
run: sudo apt-get install -y softhsm2
- name: Install opensc
@@ -35,51 +43,29 @@ jobs:
- name: Build
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
- run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
+ run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true -P jdk-17,jdk-21,jdk-25
temurin:
- strategy:
- matrix:
- java: [ '8', '11', '17', '18' ]
runs-on: 'ubuntu-latest'
- name: jdk-${{ matrix.java }}-temurin
+ name: temurin
steps:
- uses: actions/checkout@v4
+ # Install test-only JDKs first; each call appends an entry to ~/.m2/toolchains.xml
- name: Set up JDK
uses: actions/setup-java@v4
with:
- java-version: ${{ matrix.java }}
distribution: 'temurin'
- cache: 'maven'
- check-latest: true
- - name: Install softhsm2
- run: sudo apt-get install -y softhsm2
- - name: Install opensc
- run: sudo apt-get install -y opensc
- - name: Ensure SoftHSM user configuration
- run: impl/src/test/scripts/softhsm configure
- - name: Populate SoftHSM with JJWT test keys
- run: impl/src/test/scripts/softhsm import
- - name: Build
- # run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
- # to sign artifacts, since we don't want to mess with storing signing credentials in CI:
- run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
-
- zulu:
- strategy:
- matrix:
- java: [ '7', '8', '9', '11', '12', '13', '14', '15', '16', '17', '18', '21' ]
- runs-on: 'ubuntu-latest'
- env:
- JDK_MAJOR_VERSION: ${{ matrix.java }}
- name: jdk-${{ matrix.java }}-zulu
- steps:
- - uses: actions/checkout@v4
- - name: Set up JDK
+ java-version: |
+ 8
+ 11
+ 21
+ 25
+ # Build JDK last so it ends up as the active JAVA_HOME / PATH entry
+ - name: Set up JDK 17 (build)
uses: actions/setup-java@v4
with:
- java-version: ${{ matrix.java }}
- distribution: 'zulu'
+ java-version: '17'
+ distribution: 'temurin'
cache: 'maven'
check-latest: true
- name: Install softhsm2
@@ -93,9 +79,7 @@ jobs:
- name: Build
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
- run: |
- if [ "$JDK_MAJOR_VERSION" == "7" ]; then export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=128m"; fi
- ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
+ run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true -P jdk-8,jdk-11,jdk-17,jdk-21,jdk-25
# ensure all of our files have the correct/updated license header
license-check:
@@ -107,8 +91,8 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v4
with:
- distribution: 'zulu'
- java-version: '8'
+ distribution: 'temurin'
+ java-version: '17'
cache: 'maven'
check-latest: true
- name: License Check
@@ -117,14 +101,14 @@ jobs:
${{env.MVN_CMD}} license:check
code-coverage:
- needs: [oracle, temurin, zulu]
+ needs: [oracle, temurin]
runs-on: 'ubuntu-latest'
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
- distribution: 'zulu'
+ distribution: 'temurin'
java-version: '17'
cache: 'maven'
check-latest: true
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/JcaTemplateTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/JcaTemplateTest.groovy
index bf1037276..549df50f6 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/JcaTemplateTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/JcaTemplateTest.groovy
@@ -266,12 +266,8 @@ class JcaTemplateTest {
template.generatePrivate(new X509EncodedKeySpec(invalid))
fail()
} catch (SecurityException expected) {
- boolean jdk11OrLater = Classes.isAvailable('java.security.interfaces.XECPrivateKey')
- String msg = 'KeyFactory callback execution failed: key spec not recognized'
- if (jdk11OrLater) {
- msg = 'KeyFactory callback execution failed: Only PKCS8EncodedKeySpec and XECPrivateKeySpec supported'
- }
- assertEquals msg, expected.getMessage()
+ String msg = expected.getMessage()
+ assertTrue msg, msg.startsWith('KeyFactory callback execution failed:')
}
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/TestProvider.groovy b/impl/src/test/java/io/jsonwebtoken/impl/security/TestProvider.java
similarity index 50%
rename from impl/src/test/groovy/io/jsonwebtoken/impl/security/TestProvider.groovy
rename to impl/src/test/java/io/jsonwebtoken/impl/security/TestProvider.java
index 197a253ec..ce8d18aca 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/TestProvider.groovy
+++ b/impl/src/test/java/io/jsonwebtoken/impl/security/TestProvider.java
@@ -13,17 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.jsonwebtoken.impl.security
+package io.jsonwebtoken.impl.security;
-import java.security.Provider
+import java.security.Provider;
-class TestProvider extends Provider {
+/**
+ * Test-only {@link Provider} subclass. Defined in Java (not Groovy) because Groovy 4 cannot resolve
+ * the {@code protected} {@code Provider} constructor via its meta-class on JDK 17+.
+ */
+public class TestProvider extends Provider {
- TestProvider() {
- this('test')
+ public TestProvider() {
+ this("test");
}
- TestProvider(String name) {
- super(name, 1.0d, 'info')
+ public TestProvider(String name) {
+ //noinspection deprecation - double constructor used for Java 8 source compatibility;
+ // the (String, String, String) replacement was added in Java 9
+ super(name, 1.0d, "info");
}
}
diff --git a/install-test-jdks.sh b/install-test-jdks.sh
new file mode 100755
index 000000000..4aa0dc2c2
--- /dev/null
+++ b/install-test-jdks.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# install-test-jdks.sh
+#
+# Installs all JDK versions needed to run the full multi-JDK test matrix locally via SDKMAN,
+# then generates ~/.m2/toolchains.xml from the discovered JDKs.
+#
+# Usage:
+# ./install-test-jdks.sh
+#
+# After running this script, activate each profile you want to test against, e.g.:
+# ./mvnw verify -P jdk-8,jdk-11,jdk-21,jdk-25
+#
+# The build JDK must be JDK 17+. If you are not already on JDK 17:
+# sdk use java <17.x.x-tem>
+
+set -euo pipefail
+
+# ============================================================
+# JDK versions to install.
+# Update these when new patch releases are available.
+# Find current versions with: sdk list java
+#
+# NOTE: Temurin does not provide JDK 8 on all platforms (e.g.
+# macOS/ARM). Zulu is used for JDK 8 as a reliable fallback.
+# ============================================================
+JDKS=(
+ "8.0.492-zulu" # JDK 8 - minimum supported runtime
+ "11.0.31-tem" # JDK 11
+ "17.0.19-tem" # JDK 17 - required build JDK
+ "21.0.11-tem" # JDK 21
+ "25.0.3-tem" # JDK 25
+)
+# ============================================================
+
+if ! command -v sdk &>/dev/null; then
+ echo "ERROR: SDKMAN not found. Install it from https://sdkman.io" >&2
+ exit 1
+fi
+
+# Source SDKMAN so 'sdk' commands work in this script
+# shellcheck disable=SC1090
+source "${SDKMAN_DIR:-$HOME/.sdkman}/bin/sdkman-init.sh"
+
+echo "Installing test JDKs via SDKMAN..."
+echo "(Already-installed versions will be skipped)"
+echo
+
+for jdk in "${JDKS[@]}"; do
+ sdk install java "$jdk" || true
+done
+
+echo
+echo "Done. Generating ~/.m2/toolchains.xml from discovered JDKs..."
+./mvnw --no-transfer-progress -q \
+ org.apache.maven.plugins:maven-toolchains-plugin:3.2.0:generate-jdk-toolchains-xml \
+ -Dtoolchain.file="${HOME}/.m2/toolchains.xml"
+
+echo
+echo "To run the full multi-JDK test matrix:"
+echo " ./mvnw verify -P jdk-8,jdk-11,jdk-17,jdk-21,jdk-25"
+echo
+echo "To run a single JDK profile (e.g., JDK 8 only):"
+echo " ./mvnw verify -P jdk-8"
diff --git a/pom.xml b/pom.xml
index e3124e814..92d56ec90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,16 +97,27 @@
3.3.0
3.11.0
- 3.1.1
+ 3.6.3
3.2.1
3.1.0
- 1.6
- 0.13.1
- 1.6.1
+ 3.1.0
+ 0.15.6
+ 3.2.0
+ 4.2.0
4.2.rc3
true
- 7
+
+ 8
+
+ 8
+
+
+ false
+ false
${user.name}-${maven.build.timestamp}
2.12.7.1
@@ -114,22 +125,24 @@
2.11.0
-
1.78.1
bcprov-jdk18on
bcpkix-jdk18on
- 2.5.16
- 3.6
+ 4.0.31
+ 4.2
4.12
- 2.0.0-beta.5
- 3.0.0-M5
- 3.0.0-M5
- 4.3.1
+ 2.0.7
+ 3.1.2
+ 3.1.2
+ 4.3.1
${jjwt.root}/target/clover/clover.db
-
+
+ false
+ ${test.addOpens}
--add-opens java.base/java.lang=ALL-UNNAMED,
--add-opens java.desktop/java.beans=ALL-UNNAMED,
@@ -211,7 +224,7 @@
- org.codehaus.groovy
+ org.apache.groovy
groovy
${groovy.version}
test
@@ -321,7 +334,8 @@
**/*.test.override
**/*.bnd
LICENSE
- **/mvnw
+ **/mvnw
+ **/install-test-jdks.sh
**/lombok.config
.gitattributes
**/genkeys
@@ -362,16 +376,8 @@
${jdk.version}
true
false
- ${maven.javadoc.additionalOptions}
+ -html5 ${maven.javadoc.additionalOptions}
-
-
-
- commons-lang
- commons-lang
- 2.6
-
-
org.apache.maven.plugins
@@ -496,7 +502,7 @@
org.apache.maven.plugins
maven-enforcer-plugin
- 1.4.1
+ 3.4.1
enforce-banned-dependencies
@@ -515,49 +521,30 @@
true
+
+ enforce-java-17
+
+ enforce
+
+
+
+
+
+ [17,)
+ Build requires JDK 17 or later. Use SDKMAN: sdk install java 17.x.x-tem
+
+
+ true
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
org.apache.maven.plugins
maven-compiler-plugin
${maven.compiler.version}
- ${jdk.version}
- ${jdk.version}
+ ${jdk.release.version}
${project.build.sourceEncoding}
@@ -582,7 +569,7 @@
- org.codehaus.groovy
+ org.apache.groovy
groovy
${groovy.version}
@@ -594,6 +581,7 @@
${surefire.plugin.version}
${surefire.argLine}
+ once
@@ -681,91 +669,157 @@
+
+
+
- jdk7
-
- 1.7
-
-
- 3.2.2
- 3.8.1
- 20230618
- bcprov-jdk15to18
- bcpkix-jdk15to18
-
+ jdk-8
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${surefire.plugin.version}
+
+
+ test-jdk-8
+ test
+
+ ${module.skip.jdk8.tests}
+
+ [1.8,1.9),[8,9)
+
+
+
+ once
+
+
+
+
+
+
+
+
- jdk8AndLater
-
- [1.8,)
-
-
- 3.0.2
- 3.0.19
- 4.2
- 2.0.7
- 0.15.6
- 3.1.2
- 3.1.2
-
+ jdk-11
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${surefire.plugin.version}
+
+
+ test-jdk-11
+ test
+
+ ${module.skip.jdk11.tests}
+
+ [11,12)
+
+ ${test.addOpens}
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
- jdk9AndLater
-
- [1.9,)
-
-
- 3.11.0
- false
- -html5
- ${test.addOpens}, --illegal-access=debug
-
+ jdk-17
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${surefire.plugin.version}
+
+
+ test-jdk-17
+ test
+
+
+ [17,18)
+
+ ${test.addOpens}
+
+
+
+
+
+
+
+
- jdk17AndLater
-
- [17,)
-
-
- -html5
- ${test.addOpens}
-
+ jdk-21
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${surefire.plugin.version}
+
+
+ test-jdk-21
+ test
+
+
+ [21,22)
+
+ ${test.addOpens}
+
+
+
+
+
+
+
+
- jdk21AndLater
-
- [21,)
-
-
-
- 8
-
+ jdk-25
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${surefire.plugin.version}
+
+
+ test-jdk-25
+ test
+
+
+ [25,26)
+
+ ${test.addOpens}
+
+
+
+
+
+
+
docs