HotswapProjects / HotswapAgent

Java unlimited redefinition of classes at runtime.
GNU General Public License v2.0
2.36k stars 493 forks source link

1.4.2-SNAPSHOT (2024-03-16) release: org.hotswap.agent.javassist.NotFoundException: invokeCustomInitMethod(..) is not found in org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory #546

Closed jorgheymans closed 7 months ago

jorgheymans commented 7 months ago

Hi,

I just updated to the latest snapshot release (https://github.com/HotswapProjects/HotswapAgent/releases/download/1.4.2-SNAPSHOT/hotswap-agent-1.4.2-SNAPSHOT.jar) , and starting my app i get this:

HOTSWAP AGENT: 09:04:23.071 ERROR (org.hotswap.agent.annotation.handler.PluginClassFileTransformer) - InvocationTargetException in transform method on plugin 'class org.hotswap.agent.plugin.spring.SpringPlugin' class 'org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory' of classLoader 'jdk.internal.loader.ClassLoaders$AppClassLoader'
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.hotswap.agent.annotation.handler.PluginClassFileTransformer.transform(PluginClassFileTransformer.java:220)
    at org.hotswap.agent.annotation.handler.PluginClassFileTransformer.transform(PluginClassFileTransformer.java:112)
    at org.hotswap.agent.util.HotswapTransformer.transform(HotswapTransformer.java:264)
    at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:244)
    at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
    at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:541)
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
    at org.springframework.context.support.GenericApplicationContext.<init>(GenericApplicationContext.java:123)
    at org.springframework.web.context.support.GenericWebApplicationContext.<init>(GenericWebApplicationContext.java:99)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.<init>(ServletWebServerApplicationContext.java:120)
    at org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.<init>(AnnotationConfigServletWebServerApplicationContext.java:72)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory.createContext(ServletWebServerApplicationContextFactory.java:52)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory.create(ServletWebServerApplicationContextFactory.java:47)
    at org.springframework.boot.DefaultApplicationContextFactory.getFromSpringFactories(DefaultApplicationContextFactory.java:70)
    at org.springframework.boot.DefaultApplicationContextFactory.create(DefaultApplicationContextFactory.java:50)
    at org.springframework.boot.SpringApplication.createApplicationContext(SpringApplication.java:589)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:331)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
    at eu.olaf.afis.ims.Application.main(Application.java:15)
Caused by: org.hotswap.agent.javassist.NotFoundException: invokeCustomInitMethod(..) is not found in org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
    at org.hotswap.agent.javassist.CtClassType.getDeclaredMethod(CtClassType.java:1395)
    at org.hotswap.agent.plugin.spring.transformers.BeanFactoryTransformer.registerAbstractAutowireCapableBeanFactory(BeanFactoryTransformer.java:77)
    ... 41 more

HOTSWAP AGENT: 09:04:23.239 INFO (org.hotswap.agent.config.PluginRegistry) - Plugin 'org.hotswap.agent.plugin.spring.SpringPlugin' initialized in ClassLoader 'jdk.internal.loader.ClassLoaders$AppClassLoader@14899482'.
HOTSWAP AGENT: 09:04:23.240 INFO (org.hotswap.agent.plugin.spring.SpringPlugin) - Spring plugin initialized - Spring core version '6.1.4'

Using jdk 17.10 from jetbrains, spring boot 3.2.3.

Checking AbstractAutowireCapableBeanFactory, the method is there with this signature:

protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd, String initMethodName)

jorgheymans commented 7 months ago

Doing some digging. Spring changed the signature of org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeCustomInitMethod a while ago: https://github.com/spring-projects/spring-framework/commit/41ee23345d72623d18accb9484ce5119403c39d5

So here https://github.com/HotswapProjects/HotswapAgent/blob/93ca7bb48ba9793cd542d29e156e11087e384f02/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/BeanFactoryTransformer.java#L77 you would need to add support for the new method signature (an extra string parameter was added):

diff --git a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/BeanFactoryTransformer.java b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/BeanFactoryTransformer.java
index 2341fdec..d2922c0b 100644
--- a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/BeanFactoryTransformer.java
+++ b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/BeanFactoryTransformer.java
@@ -76,7 +76,8 @@ public class BeanFactoryTransformer {
         // try catch for custom init method
         CtMethod invokeCustomInitMethod = clazz.getDeclaredMethod("invokeCustomInitMethod",
             new CtClass[] {classPool.get(String.class.getName()), classPool.get("java.lang.Object"),
-                classPool.get("org.springframework.beans.factory.support.RootBeanDefinition")});
+                classPool.get("org.springframework.beans.factory.support.RootBeanDefinition"), 
+                classPool.get(String.class.getName())});
         invokeCustomInitMethod.addCatch(
             InitMethodEnhance.catchException("$2", "$$ha$LOGGER", "$e", "invokeCustomInitMethod", false),
             classPool.get("java.lang.Throwable"));

Unsure how this needs to be done if you want to support both versions of that class.

skybber commented 7 months ago

I see that there is only a single method invokeCustomInitMethod in AbstractAutowireCapableBeanFactory, so the clazz.getDeclaredMethod(methodName) method of CtClass could be used, so the transformation would not depend on the method parameters.

skybber commented 7 months ago

SNAPSHOT release is also fixed. Thanks for report!

jorgheymans commented 7 months ago

Woah thank you for the quick turn-around on this !