raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.29k stars 807 forks source link

Java 17 reflection changes #1682

Closed rupinder10 closed 1 month ago

rupinder10 commented 3 months ago

I have some code developed with Java 8 that uses reflection to call a private method in URLClassLoader. Unfortunately, this isnt possible in Java 17 so the application stops working in Java 17 because the

setAccessible(true) in java.lang.reflect.Method in no longer works. This requires an --add-opens command line parameter. But I cannot do this for a few different reasons.

I was thinking maybe I can add a new class using ByteBuddy into the java.base module and then let that handle this code. Is this possible ? If yes, how can I add a new class to an existing module ?

anders-steen-dige-madsen commented 2 months ago

Why can you not add an --add-opens command line parameter? If you need to ship this to a client that cannot provide them directly you need to bake it into the runtime

dogourd commented 2 months ago

Starting from Java 9, Instrumentation provides the redefineModule and isModifiableModule methods, which allow you to modify module visibility. In Byte Buddy, you might want to look into AgentBuilder.assureReadEdgeFromAndTo and ClassInjector.UsingInstrumentation.redefineModule, as these could be helpful.

anders-steen-dige-madsen commented 2 months ago

@rupinder10 I would strongly advise against going down this path, since reflecting on internal implementation details, even of otherwise public classes are not guaranteed to be forward-compatible with newer version of the JDK as they are released. Provide the functionality that you need yourself or continue down the path of future technical debt.

raphw commented 2 months ago

@rupinder10 I do not know your problem well enough to recommend anything, but if you just want to get this working, you could look into ByteBuddyAgent.install() and use redefineModule, all without code instrumentation. From Java 22, this stops working, too, and you will have to register an agent on the command line.

rupinder10 commented 2 months ago

Thanks for the responses everybody. @anders-steen-dige-madsen why I can't add --add-opens is because its an agent I am building for an application that I don't own. So I dont want to add an additional requirement on the product vendor to have to change their startup scripts. And even if they could, it requires them to restart the application. Currently my agent works by just dropping it into the product's namespace folder.

Looks like there is no elegant way to do it anyway as the solutions will only work temporarily. So -add-opens it will have to be with a JVM restart required.

raphw commented 2 months ago

As an agent, you configure add-opens via the Instrumentation API. It has redefefineModule for it.

rupinder10 commented 2 months ago

As an agent, you configure add-opens via the Instrumentation API. It has redefefineModule for it.

But as you mentioned that will stop working with Java 22. Or did I misunderstand that ?

raphw commented 2 months ago

No, only dynamic attach, not the module retransformation.

rupinder10 commented 2 months ago

I guess that could have some use. Although if I have to add an agent on the command line, I would have to shutdown the JVM anyway. which means I could also add the -add-opens at the same time. My original need was to avoid the restart of the JVM.

raphw commented 2 months ago

If you have an agent, you can add your own interface to communicate with the running JVM. You can open a socket, for example, or use a file for communication. Just make sure you validate such requests to avoid exposing the VM.

rupinder10 commented 2 months ago

I didn't get that. Can you provide specifics to what you mean ?

raphw commented 2 months ago

If you install an agent once on the command line, you can update your instrumentation dynamically thereafter using your own communication channel. As long as you attached an agent that is capable of that upon the first startup.

anders-steen-dige-madsen commented 2 months ago

Thanks for the responses everybody. @anders-steen-dige-madsen why I can't add --add-opens is because its an agent I am building for an application that I don't own. So I dont want to add an additional requirement on the product vendor to have to change their startup scripts. And even if they could, it requires them to restart the application. Currently my agent works by just dropping it into the product's namespace folder.

Looks like there is no elegant way to do it anyway as the solutions will only work temporarily. So -add-opens it will have to be with a JVM restart required.

Then you are putting the onus on your customer. Instead you should plan for Integrity by Default and make sure to notify them before hand so they can change it to only run on startup instead of dynamically since that will not be allowed by default going forward in Java 24+.

rupinder10 commented 2 months ago

All good arguments. My product is a troubleshooting product for a java server. Customers typically activate it when they think they have an issue. The instrumentation kicks in and starts collecting all the data. With dynamic instrumentation going away, the customer would have to shutdown the JVM, add the the command line and restart. But that resolves the issue. Hence the concern. Off course, the alternative would be to add the command line and then add some switches to activate and deactivate the instrumentation, as @raphw suggested