Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClassFileTransformer.transform is not called when class is redefined #222

Open
Mugenor opened this issue Mar 12, 2023 · 6 comments
Open

Comments

@Mugenor
Copy link

Mugenor commented Mar 12, 2023

Hi
I'm trying to implement a plugin for HotSwapAgent and noticed that the method annotated with @OnClassLoadEvent(classNameRegexp = ".*", events = {LoadEvent.REDEFINE}) is not called when I'm trying to hotswap any class.
After some debugging process I found out that HotswapTransformer.transform (subclass of ClassFileTransformer) is not called for the class I tried to hotswap.
That is why I think that the problem is in VM, but let me know if I'm wrong, please.

JVM I'm using:

openjdk 17.0.5 2022-10-18
OpenJDK Runtime Environment JBR-17.0.5+1-653.14-nomod (build 17.0.5+1-b653.14)
OpenJDK 64-Bit Server VM JBR-17.0.5+1-653.14-nomod (build 17.0.5+1-b653.14, mixed mode)

HotSwapAgent version: 1.4.2-SNAPSHOT
OS: MacOS

@skybber
Copy link
Collaborator

skybber commented Mar 12, 2023

I've tried a junit test from the owb-plugin and the method HotswapTransformer.transform is called correctly. Even I'm using HA+jbr17 on daily basis, so it must work. Is your plugin registered in HA-core correctly ?

@Mugenor
Copy link
Author

Mugenor commented Mar 12, 2023

Yes, I think it's registered correctly. It's initialised, listeners with LoadEvent.DEFINE event type are called correctly.

I'll provide some context:
I'm implementing a plugin for MyBatis to be able to refresh queries written in annotations.
I found out that method annotated with @OnClassLoadEvent(classNameRegexp = ".*", events = {LoadEvent.REDEFINE}) is actually called when I'm updating a query in annotation in my mapper, but I receive proxy class instead of interface itself (not sure if it's intended or not).
If I stop in debugger in the redefine listener method, I can see that ReloadJavaProxyCommand initiated redefinition of this proxy class via call of PluginManager.hotswap. And as far as I see PluginManager.hotswap is used in the junit tests.
So, probably that's the reason, why tests are working.
But if I try to redefine any other class the same listener is not called, but I expect it to be called.

I can try to create and share small project with my plugin if it helps.

P.S.: I'm not sure why ProxyPlugin is actually called since it's using pretty similar @OnClassLoadEvent annotation content

@skybber
Copy link
Collaborator

skybber commented Mar 13, 2023

Dcevm redefines all classes extended from redefined class, that's the reason why you see the proxy class. ProxyPlugin redefines (rebuilds) Proxy classes (java proxy, cglib) in second step - after base class is redefined and after some timeout.
May be you can create a small project and I'll look on it.

@Mugenor
Copy link
Author

Mugenor commented Mar 13, 2023

mybatis-plugin-reproduction.zip
I attached the zip file with a demo project.
When I'm trying to redefine org.example.MyBatisMapper I receive event about it in org.example.hotswapplugin.NewMyBatisPlugin#onMapperInterfaceReload, but here I receive not an Interface, but a proxy class.
And when I'm trying to redefine org.example.MyService class, I don't receive an event regarding it.
And JVM is started with those parameters:

<<JDK-PATH>>/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:51312,suspend=y,server=n -XX:-MaxFDLimit --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED -XX:+AllowEnhancedClassRedefinition -XX:HotswapAgent=external -javaagent:<<JDK-PATH>/lib/hotswap/hotswap-agent.jar=disablePlugin=JdkPlugin,disablePlugin=Logback,disablePlugin=WatchResources,disablePlugin=Idea,disablePlugin=Hibernate,disablePlugin=Hibernate3JPA,disablePlugin=Hibernate3,disablePlugin=Jersey1,disablePlugin=Jersey2,disablePlugin=Jetty,disablePlugin=Tomcat,disablePlugin=ZK,disablePlugin=MyFaces,disablePlugin=Mojarra,disablePlugin=Omnifaces,disablePlugin=ELResolver,disablePlugin=WildFlyELResolver,disablePlugin=OsgiEquinox,disablePlugin=Owb,disablePlugin=WebObjects,disablePlugin=Weld,disablePlugin=JBossModules,disablePlugin=ResteasyRegistry,disablePlugin=Deltaspike,disablePlugin=GlassFish,disablePlugin=Weblogic,disablePlugin=Vaadin,disablePlugin=Wicket,disablePlugin=CxfJAXRS,disablePlugin=FreeMarker,disablePlugin=Undertow,disablePlugin=MyBatis,disablePlugin=IBatis,disablePlugin=Spring

Thank you in advance!

@skybber
Copy link
Collaborator

skybber commented Mar 13, 2023

May be the problem is in debugger that does not stop in the method when redefinition is triggered from debugger itself.

@Mugenor
Copy link
Author

Mugenor commented Mar 13, 2023

I added log statement to NewMyBatisPlugin#onMapperInterfaceReload to test your theory and can confirm it.
Thank you for pointing to the right direction.

So, is it a bug or a feature of debugger? And is there a workaround for this?

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

No branches or pull requests

2 participants