High performance data exchange via USB for Java, using a thread and the usb4java library.
This is the SensorsINI continuation of Luca Longinotti / iniVation
USBTransferThread
(BSD-2-Clause). Last upstream tag: 0.9.8 (December 2020). SensorsINI 0.9.9
catches submit failures in run() so they are not uncaught.
jAER uses a frozen jar from this project (jars/USBTransferThread-*.jar). Keep the
li.longi.USBTransferThread package name so existing AEReaders keep compiling.
iniVation stopped at 0.9.8. jAER could not patch the jar, so AEReaders
wrapped start/stop around it. The main reason for this GitHub continuation is
those uncaught IllegalStateExceptions from failed LibUsb.submitTransfer
(missing endpoint after hotplug, gone device, stall, I/O). They killed
USBTransferThread without reaching the shutdown callback, so the viewer could
show LIVE with a dead reader. 0.9.9 catches that in run(), records
getStartFailure(), and exits so join() returns.
Requires JDK 25 (same as jAER javac target 25) and Maven 3.9+.
mvn -DskipTests package
The jar is target/USBTransferThread-0.9.9.jar. Drop that file into jAER jars/
and point NetBeans file.reference.USBTransferThread-*.jar at it. mvn deploy
is not required for jAER.
This library does not replace usb4java. usb4java already does device open, control transfers, and both sync and async I/O. The aim here is high-throughput streaming on bulk and interrupt endpoints: keep several transfers in flight (overlapped / pipelined async I/O) and run libusb’s event loop on a dedicated max-priority thread, so processing and the rest of the JVM do not stall the USB pipe.
usb4java still owns open/claim. USBTransferThread owns the streaming loop:
allocate a pool of Transfers, submit them, handleEvents, resubmit after
processTransfer, set the IN buffer limit to the bytes received, and cancel/free
on interrupt(). A blocking LibUsb.bulkTransfer on the viewer thread cannot do
that.
Open the device with usb4java (LibUsb.init, open handle,
claim the interface). Implement RestrictedTransferCallback:
prepareTransfer— fill the buffer for OUT transfers (no-op for IN).processTransfer— readstatus(),actualLength(), andbuffer()after each completion. Successful IN transfers have the buffer limit set to the bytes received.
Construct USBTransferThread with the DeviceHandle, endpoint address, transfer
type (LibUsb.TRANSFER_TYPE_BULK or TRANSFER_TYPE_INTERRUPT), and the callback.
Optional arguments: buffer count and size (defaults 16 × 8192 bytes; pass 0
for a default), then optional setup / shutdown / exceptional-shutdown Runnables
and a libusb Context. Call start(). If submit fails (LIBUSB_ERROR_NOT_FOUND
and similar), the thread exits after deallocateTransfers; join() returns and
getStartFailure() holds the IllegalStateException. Change pool size later with
setBufferNumber / setBufferSize. Stop with interrupt() then join().
See src/test/java/li/longi/USBTransferThread/USBTransferThreadTest.java for a
full IN bulk example. jAER AEReaders follow the same pattern.