Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 249 additions & 0 deletions .github/workflows/java-module-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
name: Java 9+ Module Support

on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]

jobs:
# Test Java 9+ module support (JPMS)
# Verifies that module-info.java is properly compiled and the resulting
# JAR can be used with jlink to create custom Java runtimes.
module-test:
strategy:
matrix:
os: [ 'ubuntu-latest' ]
jdk_version: [ '11', '17', '21' ]
runs-on: ${{ matrix.os }}
name: Module Test (JDK ${{ matrix.jdk_version }})

steps:
- uses: actions/checkout@v4

- name: Cache JUnit dependencies
uses: actions/cache@v4
id: cache-junit
with:
path: junit
key: junit-jars-v1

- name: Download junit-4.13.2.jar
if: steps.cache-junit.outputs.cache-hit != 'true'
run: wget --directory-prefix=$GITHUB_WORKSPACE/junit https://repo1.maven.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar

- name: Download hamcrest-all-1.3.jar
if: steps.cache-junit.outputs.cache-hit != 'true'
run: wget --directory-prefix=$GITHUB_WORKSPACE/junit https://repo1.maven.org/maven2/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar

- name: Build native wolfSSL
uses: wolfSSL/actions-build-autotools-project@v1
with:
repository: wolfSSL/wolfssl
ref: master
path: wolfssl
configure: --enable-jni
check: false
install: true

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: ${{ matrix.jdk_version }}

- name: Set JUNIT_HOME
run: |
echo "JUNIT_HOME=$GITHUB_WORKSPACE/junit" >> "$GITHUB_ENV"

- name: Set LD_LIBRARY_PATH
run: |
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib" >> "$GITHUB_ENV"

- name: Copy makefile
run: cp makefile.linux makefile

- name: Build JNI library
run: PREFIX=$GITHUB_WORKSPACE/build-dir make

- name: Build JAR with module support (ant)
run: ant build-jce-release

- name: Verify module-info.class exists in JAR
run: |
echo "Checking for module-info.class in wolfcrypt-jni.jar..."
if jar tf lib/wolfcrypt-jni.jar | grep -q "module-info.class"; then
echo "SUCCESS: module-info.class found in JAR"
else
echo "FAILURE: module-info.class not found in JAR"
echo "JAR contents:"
jar tf lib/wolfcrypt-jni.jar | head -20
exit 1
fi

- name: Verify module descriptor with jar --describe-module
run: |
echo "Describing module in wolfcrypt-jni.jar..."
jar --describe-module --file=lib/wolfcrypt-jni.jar
echo ""
echo "Verifying module name is 'com.wolfssl.wolfcrypt'..."
MODULE_NAME=$(jar --describe-module --file=lib/wolfcrypt-jni.jar 2>&1 | head -1 | cut -d' ' -f1)
if [ "$MODULE_NAME" = "com.wolfssl.wolfcrypt" ]; then
echo "SUCCESS: Module name is correct: $MODULE_NAME"
else
echo "FAILURE: Expected module name 'com.wolfssl.wolfcrypt', got '$MODULE_NAME'"
exit 1
fi

- name: Verify module exports correct packages
run: |
echo "Verifying module exports..."
EXPORTS=$(jar --describe-module --file=lib/wolfcrypt-jni.jar 2>&1)
echo "$EXPORTS"
echo ""
if echo "$EXPORTS" | grep -q "exports com.wolfssl.wolfcrypt"; then
echo "SUCCESS: exports com.wolfssl.wolfcrypt"
else
echo "FAILURE: missing 'exports com.wolfssl.wolfcrypt'"
exit 1
fi
if echo "$EXPORTS" | grep -q "exports com.wolfssl.provider.jce"; then
echo "SUCCESS: exports com.wolfssl.provider.jce"
else
echo "FAILURE: missing 'exports com.wolfssl.provider.jce'"
exit 1
fi

- name: Test jlink can create runtime with module
run: |
echo "Testing jlink integration..."
jlink \
--module-path lib/wolfcrypt-jni.jar:$JAVA_HOME/jmods \
--add-modules com.wolfssl.wolfcrypt \
--output test-jlink-runtime \
--no-header-files \
--no-man-pages
echo ""
echo "SUCCESS: jlink created custom runtime"
echo "Runtime modules:"
./test-jlink-runtime/bin/java --list-modules
echo ""
echo "Verifying com.wolfssl.wolfcrypt module is present..."
if ./test-jlink-runtime/bin/java --list-modules | grep -q "com.wolfssl.wolfcrypt"; then
echo "SUCCESS: com.wolfssl.wolfcrypt module found in custom runtime"
else
echo "FAILURE: com.wolfssl.wolfcrypt module not found in custom runtime"
exit 1
fi

- name: Clean up jlink test runtime
run: rm -rf test-jlink-runtime

- name: Run standard tests to verify module doesn't break functionality
run: ant test

- name: Clean ant build for Maven test
run: ant clean

- name: Maven build and verify module-info in JAR
run: |
echo "Building with Maven..."
mvn package -DskipTests -q
echo ""
MAVEN_JAR=$(ls target/wolfcrypt-jni-*.jar)
echo "Maven JAR: $MAVEN_JAR"
echo ""
echo "Checking for module-info.class in Maven-built JAR..."
if jar tf "$MAVEN_JAR" | grep -q "module-info.class"; then
echo "SUCCESS: module-info.class found in Maven JAR"
else
echo "FAILURE: module-info.class not found in Maven JAR"
jar tf "$MAVEN_JAR" | head -20
exit 1
fi
echo ""
echo "Verifying Maven JAR module descriptor..."
jar --describe-module --file="$MAVEN_JAR"

- name: Clean Maven build
run: mvn clean -q

- name: Show logs on failure
if: failure() || cancelled()
run: |
cat build/reports/*.txt 2>/dev/null || echo "No test reports found"

# Verify Java 8 builds do NOT include module-info.class
# This ensures the conditional compilation works correctly for Java 8 users
java8-no-module-test:
runs-on: ubuntu-latest
name: Java 8 No Module Test

steps:
- uses: actions/checkout@v4

- name: Cache JUnit dependencies
uses: actions/cache@v4
id: cache-junit
with:
path: junit
key: junit-jars-v1

- name: Download junit-4.13.2.jar
if: steps.cache-junit.outputs.cache-hit != 'true'
run: wget --directory-prefix=$GITHUB_WORKSPACE/junit https://repo1.maven.org/maven2/junit/junit/4.13.2/junit-4.13.2.jar

- name: Download hamcrest-all-1.3.jar
if: steps.cache-junit.outputs.cache-hit != 'true'
run: wget --directory-prefix=$GITHUB_WORKSPACE/junit https://repo1.maven.org/maven2/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar

- name: Build native wolfSSL
uses: wolfSSL/actions-build-autotools-project@v1
with:
repository: wolfSSL/wolfssl
ref: master
path: wolfssl
configure: --enable-jni
check: false
install: true

- name: Setup Java 8
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '8'

- name: Set JUNIT_HOME
run: |
echo "JUNIT_HOME=$GITHUB_WORKSPACE/junit" >> "$GITHUB_ENV"

- name: Set LD_LIBRARY_PATH
run: |
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib" >> "$GITHUB_ENV"

- name: Copy makefile
run: cp makefile.linux makefile

- name: Build JNI library
run: PREFIX=$GITHUB_WORKSPACE/build-dir make

- name: Build JAR with Java 8 (ant)
run: ant build-jce-release

- name: Verify module-info.class is NOT in JAR (Java 8)
run: |
echo "Checking that module-info.class is NOT in wolfcrypt-jni.jar (Java 8 build)..."
if jar tf lib/wolfcrypt-jni.jar | grep -q "module-info.class"; then
echo "FAILURE: module-info.class should NOT be in Java 8 JAR"
exit 1
else
echo "SUCCESS: module-info.class correctly absent from Java 8 JAR"
fi

- name: Run standard tests to verify Java 8 compatibility
run: ant test

- name: Show logs on failure
if: failure() || cancelled()
run: |
cat build/reports/*.txt 2>/dev/null || echo "No test reports found"
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,51 @@ on the current release):
```


### Java 9+ Module Support (JPMS)
---------

wolfCrypt JNI/JCE supports the Java Platform Module System (JPMS) introduced
in Java 9. This enables use with `jlink` for creating custom, minimal Java
runtimes.

**Module Information:**

- Module name: `com.wolfssl.wolfcrypt`
- Exported packages: `com.wolfssl.wolfcrypt`, `com.wolfssl.provider.jce`
- Service provider: `java.security.Provider` (WolfCryptProvider)

**Conditional Compilation:**

The `module-info.java` is conditionally compiled based on the JDK version used
to build:

| JDK Used to Build | Resulting JAR |
|-------------------|---------------|
| Java 8 | Standard JAR (no module-info.class) |
| Java 9+ | Modular JAR (includes module-info.class) |

When building with Java 8, the `module-info.java` is automatically excluded
from compilation, and the resulting JAR works as a standard classpath JAR.

**Using with jlink:**

When built with Java 9+, the wolfCrypt JNI/JCE JAR can be used with `jlink` to
create a custom Java runtime that includes the wolfCrypt module:

```
$ jlink \
--module-path lib/wolfcrypt-jni.jar:$JAVA_HOME/jmods \
--add-modules com.wolfssl.wolfcrypt \
--output custom-runtime \
--no-header-files \
--no-man-pages

$ ./custom-runtime/bin/java --list-modules
```

**Note:** The native wolfCrypt JNI shared library (`libwolfcryptjni.so/dylib`)
must still be available on the native library path at runtime.

### Example / Test Code
---------

Expand Down
34 changes: 32 additions & 2 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

<!-- set properties for this build -->
<property name="src.dir" value="src/main/java/" />
<property name="src.java9.dir" value="src/java9/" />
<property name="jni.dir" value="jni/include/" />
<property name="lib.dir" value="lib/" />
<property name="build.dir" value="build" />
Expand All @@ -48,6 +49,18 @@

<property environment="env" />

<!-- Detect Java 9+ for module-info.java compilation -->
<condition property="isJava9Plus">
<not>
<or>
<equals arg1="${ant.java.version}" arg2="1.5"/>
<equals arg1="${ant.java.version}" arg2="1.6"/>
<equals arg1="${ant.java.version}" arg2="1.7"/>
<equals arg1="${ant.java.version}" arg2="1.8"/>
</or>
</not>
</condition>

<!-- Detect if running on Windows host -->
<condition property="isWindows">
<os family="windows" />
Expand Down Expand Up @@ -161,8 +174,24 @@
</copy>
</target>

<!-- Compile module-info.java for Java 9+ module support.
Only runs when building with Java 9 or later. When building with
Java 8 or earlier, this target is skipped and the resulting JAR
will be a standard (non-modular) JAR file. -->
<target name="compile-module-info" if="isJava9Plus"
description="Compile module-info.java for Java 9+ (skipped on Java 8)">
<javac srcdir="${src.java9.dir}"
destdir="${build.dir}"
release="9"
modulepath="${build.dir}"
includeantruntime="false">
<include name="module-info.java"/>
</javac>
<echo message="Compiled module-info.java for Java 9+ module support"/>
</target>

<!-- create JAR with ONLY JNI classes, not to be used with JCE -->
<target name="jar-jni" depends="compile-nativeheaderdir, compile-javah">
<target name="jar-jni" depends="compile-nativeheaderdir, compile-javah, compile-module-info">
<jar jarfile="${lib.dir}/wolfcrypt-jni.jar">
<manifest>
<attribute name="Implementation-Title"
Expand All @@ -174,12 +203,13 @@
</manifest>
<fileset dir="${build.dir}">
<include name="com/wolfssl/wolfcrypt/*.class"/>
<include name="module-info.class"/>
</fileset>
</jar>
</target>

<!-- create JAR with JNI and JCE classes, use this when wanting JCE -->
<target name="jar-jce" depends="compile-nativeheaderdir, compile-javah">
<target name="jar-jce" depends="compile-nativeheaderdir, compile-javah, compile-module-info">
<jar jarfile="${lib.dir}/wolfcrypt-jni.jar" basedir="${build.dir}">
<manifest>
<attribute name="Implementation-Title"
Expand Down
Loading
Loading