Skip to content

fix: call gomtm bridge methods with correct casing - #4

Merged
codeh007 merged 1 commit into
mainfrom
fix/swarm-runtime-methods
Mar 28, 2026
Merged

codeh007 merged 1 commit into
mainfrom
fix/swarm-runtime-methods

Conversation

@codeh007

@codeh007 codeh007 commented Mar 28, 2026 •

Copy link
Copy Markdown
Owner

Why\nSwarmRuntime currently invokes bridge methods using lower-case names (startNode/getState/etc). The gomobile AAR exports StartNode/GetState/etc, so Start/Stop/Probe never execute.\n\n## What\n- Add method-name fallbacks for Start/Stop and probe getters (GetState/GetPeerID/GetBootstrapAddr/GetLastError/GetDiscoveredPeers/DrainLogs).\n- Add setter fallbacks for Config (SetBootstrapAddr/SetNodeName/SetAutoReconnect).\n- Keep probe tolerant if methods are missing; Start/Stop now raise clear error if no method found.\n\n## Result\nStart node now correctly calls into the gomtm AAR on Android, enabling swarm join and peer discovery.\n

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced Swarm runtime compatibility and stability by supporting multiple method name variants for core operations including configuration, node lifecycle management, peer discovery, and system logging.
    • Improved robustness and reduced errors when connecting to different network bridge implementations with varying API naming standards and versions.

@coderabbitai

coderabbitai Bot commented Mar 28, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

SwarmRuntime was updated to support multiple method name variants for reflective invocations and property setters. The changes introduce candidate-name fallback mechanisms throughout the class, enabling compatibility with different naming conventions (camelCase, PascalCase) for bridge and config methods.

Changes

Cohort / File(s) Summary
SwarmRuntime Reflection Refactoring
app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt
Refactored reflection helpers to support multiple method name candidates. invokeString() now delegates to invokeStringByNames(). Added invokeStringByNames() with fallback iteration through candidate names. Added invokeStaticByNames() for static method calls with candidate name attempts. Updated start() config property setters to use multi-name variants. Updated probe() to use invokeStringByNames() for peerId, bootstrapAddress, lastError, getState, and getDiscoveredPeers with capitalized and lowercase fallbacks. Updated drainLogs() to try DrainLogs then drainLogs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop hop, what a delight!
Method names now flexible and right,
With fallbacks so clever, in snake or in camel,
The bridge finds its way through the reflection's new channel, 🎪
Variant names? We've got 'em all—no hassle!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: call gomtm bridge methods with correct casing' directly describes the main change: updating method invocations to use correct capitalization (capitalized names like StartNode/GetState instead of lowercase variants) for gomtm bridge compatibility.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/swarm-runtime-methods

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt (1)

17-23: Add coverage for the PascalCase bridge shape.

These fallback lists are the core fix, but the current fakes in app/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.kt:53-57 and app/src/test/java/com/gomtm/android/swarm/SwarmRuntimeTest.kt:59-88 still only expose lowercase members. A second fake bridge/config with StartNode/StopNode/GetState/DrainLogs and Set* setters would keep the exact AAR casing this PR targets from regressing silently.

Also applies to: 29-31, 37-37, 46-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt` around lines 17 -
23, Tests currently only create fakes exposing lowercase bridge/config members,
so add a second fake in SwarmRuntimeTest that exposes the PascalCase AAR casing
(StartNode, StopNode, GetState, DrainLogs and
SetBootstrapAddr/SetNodeName/SetAutoReconnect setters) and use it in the
existing test cases (the blocks around the current fakes) to assert that
invokeStaticByNames, setStringProperty and setBooleanProperty correctly handle
PascalCase names; ensure the fake exposes both method and property names
referenced in SwarmRuntime (StartNode/StopNode/GetState/DrainLogs and Set*
setters) so the PR’s casing fallback lists are covered and won’t regress
silently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt`:
- Around line 110-136: The setters setStringProperty and setBooleanProperty
currently only catch NoSuchMethodException, so any exception thrown by the
invoked setter is left wrapped in InvocationTargetException; update both
functions to catch InvocationTargetException around
targetClass.getMethod(...).invoke(...) and unwrap its cause (as
invokeStaticByNames does) so the original exception is surfaced—rethrow the
underlying cause (or wrap it in an IllegalStateException while preserving it as
the cause) to maintain consistent error reporting with invokeStaticByNames and
reference these methods by name when making the change.

---

Nitpick comments:
In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt`:
- Around line 17-23: Tests currently only create fakes exposing lowercase
bridge/config members, so add a second fake in SwarmRuntimeTest that exposes the
PascalCase AAR casing (StartNode, StopNode, GetState, DrainLogs and
SetBootstrapAddr/SetNodeName/SetAutoReconnect setters) and use it in the
existing test cases (the blocks around the current fakes) to assert that
invokeStaticByNames, setStringProperty and setBooleanProperty correctly handle
PascalCase names; ensure the fake exposes both method and property names
referenced in SwarmRuntime (StartNode/StopNode/GetState/DrainLogs and Set*
setters) so the PR’s casing fallback lists are covered and won’t regress
silently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b5f5102-3a2b-4acf-aea7-abf45f8ebab4

📥 Commits

Reviewing files that changed from the base of the PR and between 6481107 and 0d885e5.

📒 Files selected for processing (1)
  • app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt

Comment on lines +110 to +136
private fun setStringProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: String) {
var lastMissing: NoSuchMethodException? = null
for (name in methodNames) {
try {
targetClass.getMethod(name, String::class.java).invoke(target, value)
return
} catch (error: NoSuchMethodException) {
lastMissing = error
continue
}
}
throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
}

private fun setBooleanProperty(targetClass: Class<*>, target: Any, methodName: String, value: Boolean) {
private fun setBooleanProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: Boolean) {
val booleanType = Boolean::class.javaPrimitiveType ?: Boolean::class.javaObjectType
targetClass.getMethod(methodName, booleanType).invoke(target, value)
var lastMissing: NoSuchMethodException? = null
for (name in methodNames) {
try {
targetClass.getMethod(name, booleanType).invoke(target, value)
return
} catch (error: NoSuchMethodException) {
lastMissing = error
continue
}
}
throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "SwarmRuntime.kt" | head -5

Repository: codeh007/gomtm-android

Length of output: 125


🏁 Script executed:

cat -n app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt | head -150

Repository: codeh007/gomtm-android

Length of output: 7100


🏁 Script executed:

rg "invokeStaticByNames" -A 20 app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt

Repository: codeh007/gomtm-android

Length of output: 2104


Unwrap setter failures the same way as bridge call failures.

Lines 114 and 129 call Method.invoke(), which wraps exceptions thrown by the invoked method in InvocationTargetException. The setStringProperty and setBooleanProperty helpers only catch NoSuchMethodException, leaving any failure from the actual setter invocation (e.g., SetBootstrapAddr, SetNodeName, SetAutoReconnect) wrapped in an opaque reflection exception. However, invokeStaticByNames (lines 102–105) already unwraps these failures, creating an inconsistency in error reporting.

Patch
 private fun setStringProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: String) {
     var lastMissing: NoSuchMethodException? = null
     for (name in methodNames) {
         try {
             targetClass.getMethod(name, String::class.java).invoke(target, value)
             return
         } catch (error: NoSuchMethodException) {
             lastMissing = error
             continue
+        } catch (error: InvocationTargetException) {
+            val cause = error.targetException ?: error.cause ?: error
+            throw IllegalStateException(cause.message ?: cause.javaClass.simpleName, cause)
         }
     }
     throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
 }
 
 private fun setBooleanProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: Boolean) {
     val booleanType = Boolean::class.javaPrimitiveType ?: Boolean::class.javaObjectType
     var lastMissing: NoSuchMethodException? = null
     for (name in methodNames) {
         try {
             targetClass.getMethod(name, booleanType).invoke(target, value)
             return
         } catch (error: NoSuchMethodException) {
             lastMissing = error
             continue
+        } catch (error: InvocationTargetException) {
+            val cause = error.targetException ?: error.cause ?: error
+            throw IllegalStateException(cause.message ?: cause.javaClass.simpleName, cause)
         }
     }
     throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private fun setStringProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: String) {
var lastMissing: NoSuchMethodException? = null
for (name in methodNames) {
try {
targetClass.getMethod(name, String::class.java).invoke(target, value)
return
} catch (error: NoSuchMethodException) {
lastMissing = error
continue
}
}
throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
}
private fun setBooleanProperty(targetClass: Class<*>, target: Any, methodName: String, value: Boolean) {
private fun setBooleanProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: Boolean) {
val booleanType = Boolean::class.javaPrimitiveType ?: Boolean::class.javaObjectType
targetClass.getMethod(methodName, booleanType).invoke(target, value)
var lastMissing: NoSuchMethodException? = null
for (name in methodNames) {
try {
targetClass.getMethod(name, booleanType).invoke(target, value)
return
} catch (error: NoSuchMethodException) {
lastMissing = error
continue
}
}
throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
private fun setStringProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: String) {
var lastMissing: NoSuchMethodException? = null
for (name in methodNames) {
try {
targetClass.getMethod(name, String::class.java).invoke(target, value)
return
} catch (error: NoSuchMethodException) {
lastMissing = error
continue
} catch (error: InvocationTargetException) {
val cause = error.targetException ?: error.cause ?: error
throw IllegalStateException(cause.message ?: cause.javaClass.simpleName, cause)
}
}
throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
}
private fun setBooleanProperty(targetClass: Class<*>, target: Any, methodNames: List<String>, value: Boolean) {
val booleanType = Boolean::class.javaPrimitiveType ?: Boolean::class.javaObjectType
var lastMissing: NoSuchMethodException? = null
for (name in methodNames) {
try {
targetClass.getMethod(name, booleanType).invoke(target, value)
return
} catch (error: NoSuchMethodException) {
lastMissing = error
continue
} catch (error: InvocationTargetException) {
val cause = error.targetException ?: error.cause ?: error
throw IllegalStateException(cause.message ?: cause.javaClass.simpleName, cause)
}
}
throw IllegalStateException("config setter not found: ${methodNames.joinToString()}", lastMissing)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/gomtm/android/swarm/SwarmRuntime.kt` around lines 110 -
136, The setters setStringProperty and setBooleanProperty currently only catch
NoSuchMethodException, so any exception thrown by the invoked setter is left
wrapped in InvocationTargetException; update both functions to catch
InvocationTargetException around targetClass.getMethod(...).invoke(...) and
unwrap its cause (as invokeStaticByNames does) so the original exception is
surfaced—rethrow the underlying cause (or wrap it in an IllegalStateException
while preserving it as the cause) to maintain consistent error reporting with
invokeStaticByNames and reference these methods by name when making the change.

@codeh007
codeh007 merged commit 2bb105c into main Mar 28, 2026
2 checks passed
@codeh007
codeh007 deleted the fix/swarm-runtime-methods branch March 28, 2026 08:14
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