123123213weqw opened a new pull request, #10638:
URL: https://github.com/apache/rocketmq/pull/10638

   ## What
   
   Allows the controller path (both controller-in-namesrv and the standalone 
`mqcontroller`) to start on JDK 17 by adding `--add-opens 
java.base/java.lang=ALL-UNNAMED` to the JDK 9+ branch of `runserver.sh`.
   
   ## Why
   
   Closes #10612. Also closes #10170 (duplicate, same root cause).
   
   On JDK 9+, the controller fails at startup with 
`InaccessibleObjectException`:
   
   ```
   java.lang.reflect.InaccessibleObjectException: Unable to make field private
   java.lang.String java.lang.StackTraceElement.classLoaderName accessible:
   module java.base does not "opens java.lang" to unnamed module @...
       at 
java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
       ...
       at 
com.caucho.hessian.io.JavaDeserializer.getFieldMap(JavaDeserializer.java:329)
       at 
com.caucho.hessian.io.JavaDeserializer.<init>(JavaDeserializer.java:97)
       at 
com.caucho.hessian.io.StackTraceElementDeserializer.<init>(StackTraceElementDeserializer.java:57)
       at 
com.caucho.hessian.io.SerializerFactory.<clinit>(SerializerFactory.java:589)
       at 
org.apache.rocketmq.controller.impl.manager.ReplicasInfoManager.<clinit>(ReplicasInfoManager.java:79)
       at 
org.apache.rocketmq.controller.impl.DLedgerController.<init>(DLedgerController.java:132)
       at 
org.apache.rocketmq.controller.ControllerManager.initialize(ControllerManager.java:122)
   ```
   
   The Hessian `SerializerFactory` (pulled in transitively via DLedger, used by 
`ReplicasInfoManager` for `brokerReplicaInfo` serialization) reflectively 
accesses `java.lang.StackTraceElement.classLoaderName` during its static 
initializer. JDK 9+ strong encapsulation denies that access by default.
   
   The user-confirmed workaround in #10170 is precisely this JVM flag; this PR 
puts it in the right branch of the shipped script so users don't have to roll 
their own.
   
   ## How
   
   `runserver.sh` already detects `JAVA_MAJOR_VERSION` and branches at line 87. 
This PR adds the `--add-opens` to the `else` (JDK 9+) branch only, so JDK 8 — 
which doesn't recognize the option — is untouched.
   
   ```sh
       else
         # ... existing G1GC + Xlog options ...
         # JDK 9+ strong encapsulation blocks the reflective access that the
         # Hessian SerializerFactory (pulled in transitively via DLedger, used 
on
         # the controller path) performs against JDK core types ...
         JAVA_OPT="${JAVA_OPT} --add-opens java.base/java.lang=ALL-UNNAMED"
       fi
   ```
   
   Scope is deliberately narrow: only `java.base/java.lang`, because that's the 
only package hessian's `StackTraceElementDeserializer` reaches into. If hessian 
(or anything else) later trips on a different JDK core package, it's a one-line 
addition here.
   
   ## Verification
   
   Couldn't run the full controller on the test box (no maven), so I verified 
the mechanism directly with a minimal reproducer that performs the same 
reflective access:
   
   ```java
   import java.lang.reflect.Field;
   public class TestAddOpens {
       public static void main(String[] args) throws Exception {
           Field f = 
StackTraceElement.class.getDeclaredField("classLoaderName");
           f.setAccessible(true);
           System.out.println("OK: " + f.get(new 
StackTraceElement("a","b","c",1)));
       }
   }
   ```
   
   Results on the test box (Ubuntu 22.04, OpenJDK 17.0.19 + OpenJDK 1.8.0_492):
   
   | JDK | Command | Result |
   |-----|---------|--------|
   | 17 | `java TestAddOpens` | ❌ `InaccessibleObjectException` (matches the 
issue's stack trace) |
   | 17 | `java --add-opens java.base/java.lang=ALL-UNNAMED TestAddOpens` | ✅ 
`OK: null` |
   | 8  | `java --add-opens java.base/java.lang=ALL-UNNAMED TestAddOpens` | ❌ 
`Unrecognized option: --add-opens` (confirms the JDK 9+ branch guard is 
required) |
   
   `bash -n runserver.sh` clean.
   
   ## Notes for reviewers
   
   - I went with the script-level fix instead of a code-level fix (subclassing 
`SerializerFactory` and overriding `getClassDeserializer`) because:
     1. It matches the workaround the project already endorses (#10170 comment 
from @Kris20030907).
     2. It's a one-line, zero-risk change.
     3. The same `runserver.sh` is used by both startup paths, so a single 
change covers both.
   - If you'd prefer a code-level fix instead (or in addition), I'm happy to do 
that — the `SerializerFactory` could be wrapped to skip the 
`StackTraceElementDeserializer` registration, which would also let us drop the 
JVM flag eventually.
   - `.cmd` (`runserver.cmd`) doesn't have the equivalent fix — let me know if 
you want it patched in this PR or separately; I held off because I can't verify 
the Windows behavior here.
   
   ## How to test
   
   1. Build as usual, then on a JDK 17 box:
      ```
      sh bin/mqcontroller -c ...   # or bin/mqnamesrv with 
enableControllerInNamesrv=true
      ```
   2. Before this PR: `InaccessibleObjectException` from 
`SerializerFactory.<clinit>`.
   3. After this PR: starts normally.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to