Skip to content

Conversation

@NeatGuyCoding
Copy link
Collaborator

@NeatGuyCoding NeatGuyCoding commented Dec 16, 2025

Description

Fixed an issue where SocketIOServer directly imported IoUring, Epoll, and KQueue classes, which would cause NoClassDefFoundError when these native transport dependencies are not present in the classpath. The fix uses reflection to dynamically check class availability and gracefully falls back to NIO transport when these classes are unavailable.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Test improvements
  • Build/tooling changes

Related Issue

Closes #(issue number)

Changes Made

  • Removed direct imports of IoUring, Epoll, KQueue and their related classes
  • Added reflection helper methods to dynamically check class availability and obtain class instances
  • Updated startAsync() and initGroups() methods to use reflection instead of direct class references
  • Fixed incorrect usage of IoUringIoHandler in default case, changed to use NioIoHandler

Testing

  • All existing tests pass
  • New tests added for new functionality
  • Tests pass locally with mvn test
  • Integration tests pass (if applicable)

Checklist

  • Code follows project coding standards
  • Self-review completed
  • Code is commented where necessary
  • Documentation updated (if needed)
  • Commit messages follow conventional format
  • No merge conflicts
  • All CI checks pass

Additional Notes

This fix ensures that SocketIOServer can start normally and automatically fall back to NIO transport even when netty-transport-native-epoll, netty-transport-native-kqueue, or netty-transport-native-io_uring dependencies are missing from the classpath. The code has been verified to compile successfully.

Summary by CodeRabbit

  • Improvements
    • Enhanced automatic transport backend detection and selection for improved server startup reliability.
    • Added graceful fallback mechanisms when preferred network transports are unavailable, ensuring consistent server initialization across different environments.

✏️ Tip: You can customize this high-level summary in your review settings.

…ansport classes in SocketIOServer

Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Copilot AI review requested due to automatic review settings December 16, 2025 01:44
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

This PR refactors Netty transport backend detection in SocketIOServer to use runtime reflection-based feature detection instead of direct availability checks. It introduces helper methods for dynamic class resolution and factory acquisition, with graceful NIO fallback when optional backends (IO_URING, EPOLL, KQUEUE) are unavailable.

Changes

Cohort / File(s) Summary
Runtime transport backend detection refactoring
netty-socketio-core/src/main/java/com/socketio4j/socketio/SocketIOServer.java
Replaces direct availability checks with reflection-based runtime detection via new helper methods (isIoUringAvailable, isEpollAvailable, isKQueueAvailable). Adds dynamic resolution of ServerSocketChannel classes and IoHandler factories via reflection. Reworks AUTO transport logic to detect and select first available backend with NIO fallback. Introduces safe class/method lookups and guards around reflective invocations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Reflection patterns: Multiple new helper methods using reflection for class/method lookups require verification for correctness and safety
  • Fallback logic: Ensure NIO fallback is correctly triggered when backends are unavailable
  • Transport selection flow: Review AUTO transport detection logic to confirm proper ordering and selection of available backends
  • Error handling: Validate that missing optional backends don't cause startup failures

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 Reflection dances in the runtime light,
Testing backends with careful sight,
URING, EPOLL, KQUEUE take their chance,
But NIO's always there—the fallback dance!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title is partially related to the changeset—it mentions reflection for native transport but is truncated and vague, not fully capturing the main change of fixing NoClassDefFoundError. Expand the truncated title to be complete and specific: 'Use reflection to avoid NoClassDefFoundError for optional native transport backends' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is mostly complete, following the template with clear problem statement, type of change, and changes made; however, CI checks status is not confirmed.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-refactor-channel-class-detection-1216

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

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 fixes a NoClassDefFoundError that occurs when native transport dependencies (Epoll, KQueue, IoUring) are missing from the classpath by replacing direct imports with reflection-based class loading and availability checks.

Key Changes:

  • Replaced direct class imports with reflection-based helper methods to check class availability and instantiate transport classes dynamically
  • Fixed incorrect default handler from IoUringIoHandler to NioIoHandler in the initGroups() method
  • Added graceful fallback to NIO transport when native transports are unavailable

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
netty-socketio-core/src/main/java/com/socketio4j/socketio/SocketIOServer.java (1)

377-411: Apply the same warning message clarification as in startAsync.

Similar to the IO_URING/EPOLL/KQUEUE cases in startAsync, these blocks have duplicate warning messages with identical text. Apply the same differentiation suggested in the earlier comment for lines 246-256.

🧹 Nitpick comments (2)
netty-socketio-core/src/main/java/com/socketio4j/socketio/SocketIOServer.java (2)

74-108: Consider caching the Class<?> to avoid redundant lookups.

Each availability check loads the same class twice - once in isClassAvailable and again in Class.forName. While this works correctly, you could refactor to load the class once and reuse it.

Example refactor for isIoUringAvailable():

 private static boolean isIoUringAvailable() {
-    if (!isClassAvailable("io.netty.channel.uring.IoUring")) {
-        return false;
-    }
     try {
         Class<?> clazz = Class.forName("io.netty.channel.uring.IoUring");
         return (Boolean) clazz.getMethod("isAvailable").invoke(null);
     } catch (Exception e) {
         return false;
     }
 }

Apply the same pattern to isEpollAvailable() and isKQueueAvailable().


282-309: Consider removing intermediate "NIO selected" logs in AUTO cascade.

Lines 288, 296, and 304 log "AUTO selected NIO transport" even when the check cascade continues to the next transport. This could confuse log analysis. Consider logging only the final selection or using a different message for intermediate failures.

Example approach - only log the final decision:

+                String selectedTransport = "NIO";
                 if (isIoUringAvailable()) {
                     Class<? extends ServerChannel> clazz = getIoUringServerSocketChannelClass();
                     if (clazz != null) {
                         channelClass = clazz;
-                        log.info("AUTO selected IO_URING transport");
+                        selectedTransport = "IO_URING";
                     } else {
-                        log.info("AUTO selected NIO transport");
+                        // continue cascade
                     }
                 } else if (isEpollAvailable()) {
                     Class<? extends ServerChannel> clazz = getEpollServerSocketChannelClass();
                     if (clazz != null) {
                         channelClass = clazz;
-                        log.info("AUTO selected EPOLL transport");
+                        selectedTransport = "EPOLL";
                     } else {
-                        log.info("AUTO selected NIO transport");
+                        // continue cascade
                     }
                 } else if (isKQueueAvailable()) {
                     Class<? extends ServerChannel> clazz = getKQueueServerSocketChannelClass();
                     if (clazz != null) {
                         channelClass = clazz;
-                        log.info("AUTO selected KQUEUE transport");
+                        selectedTransport = "KQUEUE";
                     } else {
-                        log.info("AUTO selected NIO transport");
+                        // already NIO
                     }
-                } else {
-                    log.info("AUTO selected NIO transport");
                 }
+                log.info("AUTO selected {} transport", selectedTransport);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a005618 and 803907d.

📒 Files selected for processing (1)
  • netty-socketio-core/src/main/java/com/socketio4j/socketio/SocketIOServer.java (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build (21) / build
  • GitHub Check: build (25) / build
  • GitHub Check: build (17) / build
  • GitHub Check: qodana
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (5)
netty-socketio-core/src/main/java/com/socketio4j/socketio/SocketIOServer.java (5)

65-72: LGTM - Helper method works for availability checks.

The approach is appropriate for detecting optional dependencies. Note that other class-loading errors (e.g., LinkageError, NoClassDefFoundError) will propagate, but in this context that's acceptable since those indicate more serious configuration issues.


110-135: LGTM - Dynamic channel class resolution is correct.

The reflection-based approach with null fallback aligns with the PR's goal of avoiding hard dependencies on native transports.


137-162: LGTM - IoHandler factory resolution handles missing dependencies gracefully.

The methods correctly return null when classes are unavailable, allowing callers to fall back to NIO.


413-437: LGTM - AUTO cascade is cleaner than in startAsync.

This implementation avoids the intermediate logging issue present in startAsync's AUTO case by logging only once at the end. The approach is correct and maintainable.


439-442: LGTM - Default case correctly uses NioIoHandler.

This change addresses the issue mentioned in the PR objectives where IoUringIoHandler was incorrectly used in the default case.

@github-actions
Copy link

Qodana for JVM

61 new problems were found

Inspection name Severity Problems
Vulnerable declared dependency 🔶 Warning 8
Pointless arithmetic expression 🔶 Warning 7
Comparison of 'short' and 'char' values 🔶 Warning 2
Result of method call ignored 🔶 Warning 2
Busy wait 🔶 Warning 1
Injection point with ambiguous dependencies 🔶 Warning 1
Constant values 🔶 Warning 1
Number of placeholders does not match number of arguments in logging call 🔶 Warning 1
Unnecessary 'null' check before method call 🔶 Warning 1
Wrapper type may be primitive 🔶 Warning 1
Non-distinguishable logging calls ◽️ Notice 27
Vulnerable declared dependency ◽️ Notice 9

☁️ View the detailed Qodana report

Detected 123 dependencies

Third-party software list

This page lists the third-party software dependencies used in project

Dependency Version Licenses
aesh 2.8.2 Apache-2.0
annotations 26.0.2-1 Apache-2.0
arc-processor 3.30.1 Apache-2.0
arc 3.30.1 Apache-2.0
asm-analysis 9.9 BSD-3-Clause
asm-commons 9.9 BSD-3-Clause
asm-tree 9.9 BSD-3-Clause
asm-util 9.9 BSD-3-Clause
asm 9.9 BSD-3-Clause
byte-buddy 1.17.7 Apache-2.0
cache-api 1.1.1 Apache-2.0
commons-codec 1.20.0 Apache-2.0
commons-compress 1.28.0 Apache-2.0
commons-io 2.21.0 Apache-2.0
commons-logging-jboss-logging 1.0.0.final Apache-2.0
commons-logging 1.3.5 Apache-2.0
crac 1.5.0 BSD-2-Clause
gizmo 1.9.0 Apache-2.0
gizmo2 2.0.0.beta10 Apache-2.0
hazelcast 5.2.5 MIT
jackson-annotations 2.20 Apache-2.0
jackson-core 2.20.1 Apache-2.0
jackson-databind 2.20.1 Apache-2.0
jackson-dataformat-yaml 2.20.0 AML
jackson-datatype-jsr310 2.20.0 Apache-2.0
jakarta.annotation-api 2.1.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.annotation-api 3.0.0 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.el-api 6.0.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.enterprise.cdi-api 4.1.0 Apache-2.0
jakarta.enterprise.lang-model 4.1.0 Apache-2.0
jakarta.inject-api 2.0.1 Apache-2.0
jakarta.interceptor-api 2.2.0 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.json-api 2.1.3 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jakarta.transaction-api 2.0.1 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
jandex-gizmo2 3.5.2 Apache-2.0
jandex 3.5.2 Apache-2.0
jansi 2.4.0 Apache-2.0
jboss-logging 3.6.1.final Apache-2.0
jboss-logmanager 3.1.2.final Apache-2.0
jboss-threads 3.9.1 Apache-2.0
jctools-core 4.0.5 Apache-2.0
jdk-classfile-backport 25.1 GPL-2.0-only
jodd-util 6.3.0 BSD-2-Clause
jspecify 1.0.0 Apache-2.0
jul-to-slf4j 2.0.17 MIT
kryo 5.6.2 BSD-3-Clause
log4j-api 2.25.2 Apache-2.0
log4j-to-slf4j 2.25.2 Apache-2.0
micrometer-commons 1.16.0 Apache-2.0
micrometer-observation 1.16.0 Apache-2.0
micronaut-aop 4.10.2 Apache-2.0
micronaut-context-propagation 4.10.2 Apache-2.0
micronaut-context 4.10.2 Apache-2.0
micronaut-core-reactive 4.10.2 Apache-2.0
micronaut-core 4.10.2 Apache-2.0
micronaut-discovery-core 4.10.2 Apache-2.0
micronaut-http-server 4.10.2 Apache-2.0
micronaut-http 4.10.2 Apache-2.0
micronaut-inject 4.10.2 Apache-2.0
micronaut-retry 4.10.2 Apache-2.0
micronaut-router 4.10.2 Apache-2.0
micronaut-runtime 4.10.2 Apache-2.0
microprofile-config-api 3.1 Apache-2.0
microprofile-context-propagation-api 1.3 Apache-2.0
minlog 1.3.1 BSD-3-Clause
mutiny 3.0.1 Apache-2.0
nativeimage 23.1.2 UPL-1.0
netty-common 4.2.7.final Apache-2.0
parsson 1.1.7 Classpath-exception-2.0
EPL-2.0
GPL-2.0-only
quarkus-arc-deployment 3.30.1 Apache-2.0
quarkus-arc-dev 3.30.1 Apache-2.0
quarkus-arc 3.30.1 Apache-2.0
quarkus-bootstrap-app-model 3.30.1 Apache-2.0
quarkus-bootstrap-core 3.30.1 Apache-2.0
quarkus-bootstrap-runner 3.30.1 Apache-2.0
quarkus-builder 3.30.1 Apache-2.0
quarkus-class-change-agent 3.30.1 Apache-2.0
quarkus-classloader-commons 3.30.1 Apache-2.0
quarkus-core-deployment 3.30.1 Apache-2.0
quarkus-core 3.30.1 Apache-2.0
quarkus-development-mode-spi 3.30.1 Apache-2.0
quarkus-devui-deployment-spi 3.30.1 Apache-2.0
quarkus-fs-util 1.2.0 Apache-2.0
quarkus-hibernate-validator-spi 3.30.1 Apache-2.0
quarkus-ide-launcher 3.30.1 Apache-2.0
quarkus-smallrye-context-propagation-spi 3.30.1 Apache-2.0
reactive-streams 1.0.4 MIT-0
reactor-core 3.6.2 Apache-2.0
reactor-core 3.7.9 Apache-2.0
readline 2.6 Apache-2.0
redisson 3.52.0 Apache-2.0
reflectasm 1.11.9 BSD-3-Clause
rxjava 3.1.8 Apache-2.0
slf4j-api 2.0.17 MIT
slf4j-jboss-logmanager 2.0.2.final Apache-2.0
smallrye-common-annotation 2.14.0 Apache-2.0
smallrye-common-classloader 2.14.0 Apache-2.0
smallrye-common-constraint 2.14.0 Apache-2.0
smallrye-common-cpu 2.14.0 Apache-2.0
smallrye-common-expression 2.14.0 Apache-2.0
smallrye-common-function 2.14.0 Apache-2.0
smallrye-common-io 2.14.0 Apache-2.0
smallrye-common-net 2.14.0 Apache-2.0
smallrye-common-os 2.14.0 Apache-2.0
smallrye-common-process 2.14.0 Apache-2.0
smallrye-common-ref 2.14.0 Apache-2.0
smallrye-common-resource 2.14.0 Apache-2.0
smallrye-config-common 3.14.1 Apache-2.0
smallrye-config-core 3.14.1 Apache-2.0
smallrye-config 3.14.1 Apache-2.0
snakeyaml 2.4 Apache-2.0
snakeyaml 2.5 Apache-2.0
spring-aop 7.0.1 Apache-2.0
spring-beans 7.0.1 Apache-2.0
spring-boot-autoconfigure 4.0.0 Apache-2.0
spring-boot-starter-logging 4.0.0 Apache-2.0
spring-boot-starter 4.0.0 Apache-2.0
spring-boot 4.0.0 Apache-2.0
spring-context 7.0.1 Apache-2.0
spring-core 7.0.1 Apache-2.0
spring-expression 7.0.1 Apache-2.0
wildfly-common 2.0.1 Apache-2.0
word 23.1.2 UPL-1.0
Contact Qodana team

Contact us at qodana-support@jetbrains.com

@sanjomo sanjomo merged commit 36a0768 into main Dec 16, 2025
10 checks passed
@sanjomo sanjomo deleted the feature-refactor-channel-class-detection-1216 branch December 16, 2025 07:02
@sanjomo sanjomo mentioned this pull request Dec 18, 2025
18 tasks
@coderabbitai coderabbitai bot mentioned this pull request Dec 21, 2025
18 tasks
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.

3 participants