This is a Java library and API for invoking J2735 (2024) encode/decode functions on C code generated by asn1c using the Foreign Function and Memory API in Java 25, and a Java wrapper generated by the Jextract tool.
It includes the same complete implementation of J2735 (2024) as the USDOT asn1_codec C++ project, now easily accessible from Java on both Linux and Windows.
It enables converting between these ASN.1 encodings:
- XER - XML Encoding Rules
- UPER - Unaligned Packed Encoding Rules
The library is available in Maven Central. The repository includes both the Java JAR and native libraries for Linux and Windows. Consuming the native libraries from Maven Central requires some extra configuration for clients. To use the library from a Gradle project, add a configurations section to build.gradle:
configurations {
nativeLibraryArtifact_windows_x86_64
nativeLibraryArtifact_linux_x86_64
}Add the Java dependency to the dependencies section in the normal way, and also include the native library dependencies as shown:
dependencies {
...
implementation 'com.neaeraconsulting:j2735-2024-ffm-lib:3.0.0'
nativeLibraryArtifact_windows_x86_64 (
group: 'com.neaeraconsulting',
name: 'j2735-2024-ffm-lib',
version: '3.0.0-beta',
classifier: 'windows-x86_64',
ext: 'dll',
transitive: false
)
nativeLibraryArtifact_linux_x86_64 (
group: 'com.neaeraconsulting',
name: 'j2735-2024-ffm-lib',
version: '3.0.0-beta',
classifier: 'linux-x86_64',
ext: 'so',
transitive: false
)
}Note the "classifier", "ext", and "transitive: false" tags.
Register tasks to copy the native libraries to the build/libs directory of the client:
def buildDir = project.layout.buildDirectory.get()
// Copy the native library to the build directory - Windows
tasks.register('copyNativeLibrary_windows_x86_64', Copy) {
from configurations.nativeLibraryArtifact_windows_x86_64
into "$buildDir/libs"
rename { libFileName -> 'asnapplication.dll' }
}
// Copy the native library to the build directory - Linux
tasks.register('copyNativeLibrary_linux_x86_64', Copy) {
from configurations.nativeLibraryArtifact_linux_x86_64
into "$buildDir/libs"
rename { libFileName -> 'libasnapplication.so' }
}
tasks.register('copyDependencies', Copy) {
from configurations.runtimeClasspath
into "$buildDir/libs"
}Add the copy tasks to the build task:
build {
dependsOn copyNativeLibrary_linux_x86_64
dependsOn copyNativeLibrary_windows_x86_64
finalizedBy copyDependencies
}Finally, add the native access flag to the client JAR's MANIFEST:
jar {
manifest {
attributes (
'Main-Class': 'org.example.Main',
'Class-Path': configurations.runtimeClasspath.collect { it.getName() }.join(' '),
'Enable-Native-Access': 'ALL-UNNAMED'
)
}
}to prevent native access warnings when running the client.
Instructions for consuming from a Maven POM are similar, writeup TBD.
The j2735-2024-ffm-lib project is a Java library that exposes the MessageFrameCodec class which has two methods:
Converts an XER encoded MessageFrame to UPER
- Parameter xer The XER encoded MessageFrame
- returns Byte array with the UPER encoding
Convert an UPER encoded MessageFrame to XER
- Parameter uper - The UPER encoded MessageFrame
- Returns the XER encoded result
// Initialize the library
MessageFrameCodec codec;
String libResource = System.getProperty("os.name").toLowerCase().contains("win")
? "C:/Users/spongebob/app/asnapplication.dll" // Use windows library
: "/home/app/libasnapplication.so"; // Use Linux library
Path libPath = Paths.get(libResource);
codec = new MessageFrameCodec(262144L, 8192L, 512L, libPath);
// Convert XER to UPER
byte[] uper = codec.xerToUper("<MessageFrame><messageId>19</messageId><value><SPAT><intersections><IntersectionState><id><id>12111</id></id><revision>0</revision><status>0000000000000000</status><timeStamp>35176</timeStamp><states><MovementState><signalGroup>2</signalGroup><state-time-speed><MovementEvent><eventState><protected-Movement-Allowed/></eventState><timing><minEndTime>22120</minEndTime><maxEndTime>22121</maxEndTime></timing></MovementEvent></state-time-speed></MovementState><MovementState><signalGroup>4</signalGroup><state-time-speed><MovementEvent><eventState><stop-And-Remain/></eventState><timing><minEndTime>22181</minEndTime><maxEndTime>22181</maxEndTime></timing></MovementEvent></state-time-speed></MovementState><MovementState><signalGroup>6</signalGroup><state-time-speed><MovementEvent><eventState><protected-Movement-Allowed/></eventState><timing><minEndTime>22120</minEndTime><maxEndTime>22121</maxEndTime></timing></MovementEvent></state-time-speed></MovementState><MovementState><signalGroup>8</signalGroup><state-time-speed><MovementEvent><eventState><stop-And-Remain/></eventState><timing><minEndTime>21852</minEndTime><maxEndTime>21852</maxEndTime></timing></MovementEvent></state-time-speed></MovementState><MovementState><signalGroup>1</signalGroup><state-time-speed><MovementEvent><eventState><stop-And-Remain/></eventState><timing><minEndTime>21852</minEndTime><maxEndTime>21852</maxEndTime></timing></MovementEvent></state-time-speed></MovementState><MovementState><signalGroup>5</signalGroup><state-time-speed><MovementEvent><eventState><stop-And-Remain/></eventState><timing><minEndTime>21852</minEndTime><maxEndTime>21852</maxEndTime></timing></MovementEvent></state-time-speed></MovementState></states></IntersectionState></intersections></SPAT></value></MessageFrame>");
// Convert UPER to XER
String xer = codec.uperToXer(HexFormat.of().parseHex("001338000817a780000089680500204642b342b34802021a15a955a940181190acd0acd20100868555c555c00104342aae2aae002821a155715570"));A native, dynamic library, libasnapplication.so, is generated from asn1c generated C code. The library is installed on the same image as the Java API.
The Java library, j2735-2024-ffm-lib, includes code generated by the Jextract tool.
Native method calls and native data structures are handled inside a Java thread-scoped "Arena".
Arenas are FFM's way of limiting the scope and lifetime of memory used by native methods, to prevent memory leaks and security issues.
See: docs.oracle.com: Memory Segments and Arenas
Java FFM bindings are generated by running the Jextract tool on the src/convert.h header file.
Prerequisites:
- Java 25 is required to build and use version 3.x of the library.
- The build scripts require Docker.
- Visual Studio 2022 is required to rebuild for Windows.
To get started check out the repository with submodules
git clone --recurse-submodules https://github.com/neaeraconsulting/j2735-ffm-java.gitThe native library for Linux and the jextract bindings can be regenerated by running:
docker compose -f docker-compose-build.yml up --build -dwhich pulls in the generated C files from asn1_codec, compiles them with the C API in the src folder.
The unzipped C files are copied to the generated-files folder. They are not persisted to the repo since they are identical to the files from asn1_codec, but it can be useful to have the unzipped files for debugging the src/convert.h API in an IDE.
The compiled shared library for Linux is copied to the lib folder.
The Java source code from jextract is copied to the generated-jextract folder.
The Windows library doesn't have an automated build process. It can be recreated using Visual Studio 2022 (not VSCode) with the Clang compiler for Windows. Some edits to the generated C files are required to build for Windows. Follow the instructions here: C codec edits for Windows. And then build via CMake in Visual Studio. The CMakeSettings.json file contains the Visual Studio configuration to use CMake with the clang compiler.
After regenerating the native libraries to the lib folder, also be sure to copy them to the j2735-2024-ffm-lib/src/test/resources/j2735ffm folder since they are required for the unit tests in that Java project via:
cd lib
cp libasnapplication.so ../j2735-2024-ffm-lib/src/test/resources/j2735ffm/
cp asnapplication.dll ../j2735-2024-ffm-lib/src/test/resources/j2735ffm/The unit tests can be run in either Linux or Windows.
To test the library:
cd j2735-2024-ffm-lib
./gradlew clean build testRequires:
- Docker
Run docker-compose from the root directory:
docker compose -f docker-compose-api.yml up --build -dUse the *.http scripts in the /j2735-2024-api/http-tests directory to test the API. These scripts work with
IntelliJ IDE, or in VSCode with the REST Client extension.
The following translation methods are available at base URL https://localhost:4000 All methods are POSTs.
| Method | Description |
|---|---|
| /uper/bin/xer | UPER binary to XER |
| /uper/hex/xer | UPER hex string to XER |
| /xer/uper/bin | XER to UPER binary |
| /xer/uper/hex | XER to UPER hex string |
Content types are:
| Encoding | Content Type |
|---|---|
| UPER binary | application/octet-stream |
| UPER hex | text/plain |
| XER | application/xml |
Swagger UI documentation for the Demo REST API is available at:
http://localhost:4000/swagger-ui.html
The OpenAPI specification is available in JSON format at:
http://localhost:4000/api-docs
or in YAML format at: