spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.95k stars 40.65k forks source link

NoSuchMethodException on org.apache.activemq.ActiveMQConnectionFactory.<init> when using spring-boot-starter-activemq in a native image #41212

Closed miller79 closed 3 months ago

miller79 commented 3 months ago

On my journey to understand GraalVM a bit more, I decided to clean up my spring-boot-benchmark project to show updated startup times with various Spring Boot dependencies. Why attempting to get the "all" version working with native, I was constantly getting NoSuchMethodError on the ActiveMQConnectionFactoryFactory. My understanding is that all Spring Starters should work out the box with Spring Native but I may have misread this.

Looking deeper into this, it appears the problem is the ActiveMQConnectionFactoryConfiguration class uses @ConditionalOnProperty and .enable properties which according to the documentation is not supported. With that knowledge, I was able to get past this error by creating the ActiveMQConnectionFactory bean directly (shown here). If you want to see the issue yourself, just remove the bean and you should get the same results. This project is just using spring-boot:build-image with the native profile to build.

I'm not sure if this is an issue per say or not but I feel that the auto configuration for ActiveMQ shouldn't break Native builds especially in a no configuration case but wanted to get your thoughts and if there is opportunity to making an alternate version that would work out the box.

wilkinsona commented 3 months ago

My understanding is that all Spring Starters should work out the box with Spring Native but I may have misread this.

Can you please point us to the documentation that gave you this impression? It's not the case as we offer starters for some projects that cannot be made to work with GraalVM. The Known Limitations section of the reference documentation and the wiki page to which it links should reflect the currently known state of things.

uses @ConditionalOnProperty and .enable properties which according to the documentation is not supported

The properties are supported, but when they change the application's beans, they must be set at build time.

I was able to get past this error by creating the ActiveMQConnectionFactory bean directly

The problem here is that the ActiveMQConnectionFactory instance is being created through reflection. You could also work around the problem by providing a runtime hint that allows ActiveMQConnectionFactory to be instantiated using reflection.

Looking at the code, we could either add those hints in Spring Boot (as it's our code that's using reflection), or I think we could remove the use of reflection entirely. Whether or not that's enough to get ActiveMQ working in a native image remains to be seen as there may be other uses of reflection in ActiveMQ itself where some reachability metadata would be required.

miller79 commented 3 months ago

Can you please point us to the documentation that gave you this impression? It's not the case as we offer starters for some projects that cannot be made to work with GraalVM. The Known Limitations section of the reference documentation and the wiki page to which it links should reflect the currently known state of things.

I may have confused documentation with what someone may have stated in a Youtube video (its all blending together from my learning time hehe). But thank you for the links to the limitations. That could have answered some questions I had a while ago a bit faster.

The properties are supported, but when they change the application's beans, they must be set at build time.

Ahh I misread the doc. That makes much more sense. Thanks for the clarity.

The problem here is that the ActiveMQConnectionFactory instance is being created through reflection. You could also work around the problem by providing a runtime hint that allows ActiveMQConnectionFactory to be instantiated using reflection.

I've been trying to understand how the runtime hints work but don't have a full understanding yet. I used the tracing agent to generate the json files when running the applicationand placed them in the META-INF/native-image folder under src/main/resources but that didn't resolve the error. It did show the class with the method as well which I found a bit odd for it not to work. Do you have some examples on how the runtime hints would apply or is that something that would have to go on the auto configuration code to be applied effectively? I would definitely prefer to use the properties if possible vs creating the factory directly.

in ActiveMQ itself where some reachability metadata would be required.

There is already metadata added for the ActiveMQ client it seems here. Again I'm still a bit new to it all but I'm not sure if the version has to be an exact match or just above the version available as the current client is 6.1.2.

miller79 commented 3 months ago

Also just to add some clarity, I wanted to state that the purpose of this issue is mainly to see if the auto config for ActiveMQ could be corrected to help developers using Native to work easier. As you have stated:

Looking at the code, we could either add those hints in Spring Boot (as it's our code that's using reflection), or I think we could remove the use of reflection entirely.

The original autoconfig I believe was probably made this way because the ActiveMQ factory requires different classes if it uses pools or not. Without duplicating all of the logic for each class, this was a clever way to reuse the code. The new tech is challenging those decisions now but I don't feel its anything that can't be resolved with the new understanding while keeping the expected outcome.

wilkinsona commented 3 months ago

With a change in Boot so that ActiveMQConnectionFactory is no longer created using reflection, a problem with the reachability metadata is revealed:

Caused by: java.io.IOException: Could not find factory class for resource: META-INF/services/org/apache/activemq/transport/tcp
    at org.apache.activemq.util.FactoryFinder$StandaloneObjectFactory.loadProperties(FactoryFinder.java:104)
    at org.apache.activemq.util.FactoryFinder$StandaloneObjectFactory.create(FactoryFinder.java:61)
    at org.apache.activemq.util.FactoryFinder.newInstance(FactoryFinder.java:154)
    at org.apache.activemq.transport.TransportFactory.findTransportFactory(TransportFactory.java:182)
    ... 39 more

While reachability metadata is provided for activemq-client, there is none for activemq-client-jakarta so the META-INF/services/org/apache/activemq/transport/tcp resource is not available in the native image. This resource refers to org.apache.activemq.transport.tcp.TcpTransportFactory which will require some further reachability metadata.

wilkinsona commented 3 months ago

I've pushed a fix for the Boot side of things. Please open a reachability metadata issue or PR for the activemq-client-jakarta piece.

miller79 commented 3 months ago

Wow you fixed that really quickly! Thanks! I have created an issue.