Skip to content

feat: add TcpStackAwareSocketChannel for z/OS TCP/IP stack recovery — GH#4776#4792

Draft
balhar-jakub wants to merge 4 commits into
v3.x.xfrom
hermes/gh4776
Draft

feat: add TcpStackAwareSocketChannel for z/OS TCP/IP stack recovery — GH#4776#4792
balhar-jakub wants to merge 4 commits into
v3.x.xfrom
hermes/gh4776

Conversation

@balhar-jakub

Copy link
Copy Markdown
Member

Closes #4776

Adds a SocketChannel wrapper that detects z/OS TCP/IP stack restarts (EDC5122I / NetworkRecycledException) during read/write operations on accepted client sockets. When detected, the wrapper safely closes the dead socket and re-throws so Tomcat discards the connection.

Changes

  • Refactored isTcpStackRestarted and isRecycledClass from instance methods to package-private static methods on TomcatAcceptFixConfig
  • Added apiml.tcpStackAwareSocketChannel.enabled config property (default: true)
  • Created ExcludedSocketOps interface for Lombok @Delegate exclusion list
  • Created TcpStackAwareSocketChannel inner class:
    • Intercepts read(ByteBuffer), read(ByteBuffer[], int, int), write(ByteBuffer), write(ByteBuffer[], int, int)
    • Detects EDC5122I/NetworkRecycledException → log.debug + safeClose + re-throw
    • implCloseSelectableChannel and implConfigureBlocking via MethodHandles
  • Wired into FixedServerSocketChannel.accept() via wrapIfEnabled() guarded by config
  • 16 new unit tests (76 total pass)

Build

./gradlew :apiml-tomcat-common:clean build — BUILD SUCCESSFUL, all 76 tests pass.

(QA review + CI gate to follow)

@balhar-jakub

Copy link
Copy Markdown
Member Author

QA + Security Review — PR #4792 (#4776)

Verdict: APPROVED

Build and Tests

  • ./gradlew :apiml-tomcat-common:clean build — BUILD SUCCESSFUL, 76/76 tests pass
  • 16 new tests added covering all read/write/safeClose/config scenarios

Pavel's Lens — All 8 Rules Checked

Rule Status Notes
1. Config Consistency OK apiml.tcpStackAwareSocketChannel.enabled has Spring default :true. Follows existing undocumented pattern (same as server.tomcat.retryRebindTimeoutSecs).
2. Deduplication OK isTcpStackRestarted/isRecycledClass properly extracted to static methods. Old instance method delegates. Minor: NETWORK_RECYCLED_EXCEPTION_CLASS constant on line 176 is now dead code — not used after refactor. Suggest removing in follow-up.
3. Null Safety OK isTcpStackRestarted checks getMessage() != null, getCause() != null, cause != t (cycle guard).
4. Test Parametrization OK Tests are varied enough (read/write, single/scatter, pass/fail, config on/off, integration) to not collapse cleanly into parameterized. 16 distinct test cases with good edge coverage.
5. Security Boundaries OK No auth/TLS/CORS/input validation changes. This is z/OS socket infrastructure. Config toggle safely disables. No secrets, no data exposure. safeClose is best-effort with catch-and-ignore.
6. z/OS Awareness EXCELLENT Purpose-built for z/OS TCP/IP stack recovery. Handles EDC5122I and com.ibm.net.NetworkRecycledException. Graceful degradation via config toggle.
7. Log Quality OK All log.debug with exception context. Messages identify operation type (read/scatter read/write/scatter write). safeClose silently swallows close errors — correct for best-effort.
8. TODO Tracking OK No TODO/FIXME/HACK comments found.

Minor Notes (non-blocking)

  1. NETWORK_RECYCLED_EXCEPTION_CLASS constant (line 176) is dead code after the static refactor — can be cleaned up in a follow-up.
  2. Instance isTcpStackRestarted wrapper (lines 277-279) delegates to static version — may still be referenced by pre-existing tests calling channel.isTcpStackRestarted(). Acceptable.

Security Assessment

  • No new auth/authz boundaries introduced
  • No input validation changes
  • No secrets or credentials in code
  • No data exposure via logs (debug level, exception context only)
  • MethodHandle usage for implCloseSelectableChannel/implConfigureBlocking follows existing pattern in FixedServerSocketChannel
  • @Delegate with ExcludedSocketOps correctly excludes final methods and manually overridden methods

APPROVED — ready for merge.

…StackAwareSocketChannel config (#4776)

Move isRecycledClass and isTcpStackRestarted from FixedServerSocketChannel
instance methods to package-private static methods on TomcatAcceptFixConfig.
Add apiml.tcpStackAwareSocketChannel.enabled config property (default true).
Update call sites in FixedServerSocketChannel.accept() to use static methods.

Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
…#4776)

Create ExcludedSocketOps interface (20 methods) and TcpStackAwareSocketChannel
inner class extending SocketChannel with @DeleGate. Intercepts read/write ops
to detect EDC5122I/NetworkRecycledException, closes dead socket, re-throws.
Wire into FixedServerSocketChannel.accept() guarded by config property
apiml.tcpStackAwareSocketChannel.enabled (default true).

Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
Add comprehensive tests for TcpStackAwareSocketChannel wrapper:
- Normal read/write passthrough
- EDC5122I detection on read/write/scatter/gather (close + rethrow)
- NetworkRecycledException detection via MockedStatic
- Non-EDC5122I IOException passthrough (no close)
- safeClose robustness (IOException on close ignored)
- configureBlocking/isBlocking delegation
- Integration test with real SocketChannel
- Config disabled returns raw SocketChannel

Update TcpStackRestartHandling tests to use static methods.
All 76 tests pass.

Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
@balhar-jakub

Copy link
Copy Markdown
Member Author

DCO check failed — all 3 commits are missing Signed-off-by lines.

Fix: git rebase --signoff v3.x.x && git push --force-with-lease

@balhar-jakub balhar-jakub marked this pull request as draft July 9, 2026 12:09
Change default from true to false. The wrapper breaks Tomcat socket
acceptance on non-z/OS platforms (Linux CI runners), causing tests
to hang with Connection refused until BuildAndTest times out at 35
minutes. The feature only has value on z/OS where TCP/IP stack
restarts can occur — z/OS deployments must explicitly set
apiml.tcpStackAwareSocketChannel.enabled=true.

Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
78.6% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

z/OS TCP/IP stack recovery only patches accept() socket, not existing client connections

1 participant