Skip to content

Lazy-initialize SerialPort in transport classes#145

Merged
kevinherron merged 8 commits into
masterfrom
lazy-serial-port
Mar 14, 2026
Merged

Lazy-initialize SerialPort in transport classes#145
kevinherron merged 8 commits into
masterfrom
lazy-serial-port

Conversation

@kevinherron
Copy link
Copy Markdown
Contributor

@kevinherron kevinherron commented Mar 14, 2026

Summary

  • Defer SerialPort.getCommPort() from constructor to connect/bind time using double-checked locking. Previously, the SerialPort was eagerly created in the constructor, which on Linux/macOS throws immediately for non-existent port descriptors (since jSerialComm validates the filesystem path). This made it impossible to catch and handle the error as a ModbusConnectException. On Windows, getCommPort() never validates — failure is deferred to openPort() — so the eager call silently succeeded but the behavior was inconsistent across platforms.
  • Wrap errors in proper exception typesgetCommPort failures are now caught and surfaced as ModbusException, and connect/bind failures (both from getCommPort and openPort) are wrapped as ModbusConnectException. Previously these were generic Exception instances.
  • Add comprehensive cross-platform tests documenting the platform-specific getCommPort behavior and verifying that both client and server transports handle bogus port names correctly on all OSes.
  • Expand CI matrix to build and test on Ubuntu, macOS, and Windows, since the new OS-specific tests exercise platform-dependent behavior.

Motivation

SerialPort.getCommPort(String) behaves differently by platform:

  • Linux/macOS: Validates that the port descriptor exists on the filesystem. Throws SerialPortInvalidPortException immediately if the file does not exist.
  • Windows: Rewrites the descriptor to \\.\COMx format without validation. The SerialPort object is created successfully; failure is deferred to openPort(), which returns false.

By deferring getCommPort to connect/bind time, the transport classes can catch the exception on Linux/macOS and wrap it as a ModbusConnectException, giving callers a consistent error-handling experience across all platforms.

Test plan

  • SerialPortGetCommPortBehaviorTest — OS-gated tests document raw getCommPort behavior on each platform
  • ClientTransport / ServerTransport nested tests verify constructor never calls getCommPort, and that connect/bind with bogus ports fails with ModbusConnectException
  • CI runs on ubuntu-latest, macos-latest, and windows-latest

Add tests asserting that Windows-style descriptors (COM999) also fail
on Linux/Mac, and that Unix-style descriptors (/dev/...) succeed at
getCommPort on Windows but fail at openPort. Fix test expectations
based on CI results: Windows getCommPort does not throw for bogus COM
ports, deferring failure to openPort.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR defers creation of jSerialComm SerialPort instances until connect/bind time to normalize cross-platform behavior (Linux/macOS throwing on invalid descriptors vs Windows deferring failure to openPort()), and adds tests + CI coverage to lock that behavior in.

Changes:

  • Lazily initialize SerialPort in client/server transports (double-checked locking) and wrap failures in ModbusException / ModbusConnectException.
  • Add OS-gated tests documenting SerialPort.getCommPort() behavior and ensuring bogus ports fail consistently via ModbusConnectException.
  • Expand GitHub Actions CI to run Maven build/tests on Ubuntu, macOS, and Windows.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
modbus-serial/src/main/java/com/digitalpetri/modbus/serial/client/SerialPortClientTransport.java Lazy SerialPort creation + improved exception wrapping and connection checks.
modbus-serial/src/main/java/com/digitalpetri/modbus/serial/server/SerialPortServerTransport.java Lazy SerialPort creation + improved exception wrapping during bind/unbind.
modbus-serial/src/test/java/com/digitalpetri/modbus/serial/SerialPortGetCommPortBehaviorTest.java New cross-platform behavior tests for getCommPort() and transport failure semantics.
.github/workflows/maven.yml CI now runs the Maven build on Linux/macOS/Windows to exercise platform-specific tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

kevinherron and others added 4 commits March 14, 2026 13:35
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@kevinherron kevinherron merged commit fc1e090 into master Mar 14, 2026
3 checks passed
@kevinherron kevinherron added this to the 2.1.5 milestone Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants