Java Bindings

The Java bindings are distributed as a JAR and target Java 8. Native libraries are embedded in the JAR libraries for Windows and Linux in the resources. The correct native library is automatically loaded during static initialization. These native libraries wrap the underlying C API with a thin layer of JNI.

Maven#

Release artifacts are published to Maven central. You can use them within your projects:

<dependency>
<groupId>io.stepfunc</groupId>
<artifactId>dnp3</artifactId>
<version>${dnp3.version}</version>
</dependency>

Dependencies#

The Java bindings depend on two additional open source projects in addition to the Rust dependencies:

  • joou-java-6 - Apache 2.0 - Java Object Oriented Unsigned (JOOU) integer classes
  • commons-lang3 - Apache 2.0 - Java utility classes

These libraries are not distributed by Step Function I/O directly and are only declared as a dependency for the package manager to retrieve.

Unsigned Integers#

Java does not support unsigned integers as part of the core language. To overcome this limitation, the Java code generator uses classes from the JOOU library in place of these missing primitives. This ensures that numeric types crossing the Java/JNI boundary are always pre-validated to be within the correct range. User code that creates unsigned integers will need to import symbols from the JOOU library.

It is particularly helpful to statically import the factory methods on Unsigned class:

import static org.joou.Unsigned.*;
import org.joou.UShort;

This allows the user to succinctly instantiate create instances of the unsigned classes:

UShort value = ushort(65535);

Mapping#

Java is an object-oriented language and has support for all of the abstract patterns modeled in the code generator. This section describes those mappings.

Errors#

C API errors are transformed into exceptions containing the error enum. The exception class inherits from RuntimeException.

warning

Uncaught exceptions thrown in callbacks will cause the program to terminate. User code should always wrap callback logic with try/catch syntax if there is a possibility the callback will throw.

Iterators#

Iterators are transformed into List<T> by the code generator. This means that the collections returned by callbacks may be used outside the callback, e.g. sent to another thread for processing.

Structs#

Native structs are mapped to Java classes. They have public member visibility and the constructor ensures that all values are initialized properly.

Classes#

Abstract classes are mapped to Java classes. They have a private pointer to the underlying native resource. There are two types of generated classes in Java: ones that that only have a finalize method and ones that also expose a public shutdown method that allows the user to proactively release native resources.

The types that provide a shutdown method represent long-lived resources like a Runtime, Master, or TCPServer. Under the hood, they map to an asynchronous Rust task executing on the Tokio runtime. The shutdown method allows the user to precisely control when the resource/task will stop.

Types that do not provide a shutdown method are automatically garbage collected, and native resources are deallocated in the class's finalize method. These types of classes are typically builder objects such as Commands, Request, and AddressFilter.

Asynchronous Methods#

Abstract asynchronous methods are transformed into methods that return CompletionStage<T>. Users may then chain then use the object in an asynchronous workflow or transform it into a CompletableFuture<T> and call get().