I have a mysterious java.lang.NoSuchMethodError exception after upgrading form spring boot 2.1.4 to spring boot 2.3.0. From my java knowledge this error is even not allowed to occur.
2020-05-30 15:08:53.690 INFO 41096 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-05-30 15:08:53.756 ERROR 41096 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
de.mvbonline.commons.amqp.util.ListenerUtil.createListenerContainer(ListenerUtil.java:21)
The following method did not exist:
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.setMessageListener(Ljava/lang/Object;)V
The method's class, org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer, is available from the following locations:
jar:file:/C:/projects/maven-repository/org/springframework/amqp/spring-rabbit/2.2.7.RELEASE/spring-rabbit-2.2.7.RELEASE.jar!/org/springframework/amqp/rabbit/listener/SimpleMessageListenerContainer.class
It was loaded from the following location:
file:/C:/projects/maven-repository/org/springframework/amqp/spring-rabbit/2.2.7.RELEASE/spring-rabbit-2.2.7.RELEASE.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
The lines form above
2020-05-30 15:08:52.849 INFO 41096 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'taskExecutor'
2020-05-30 15:08:53.534 WARN 41096 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataUpdateListenerContainer' defined in class path resource [de/mvbonline/pubx/restapi/config/RabbitDataUpdateConsumerConfiguration$DataUpdateRabbitConsumerConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.amqp.rabbit.listener.MessageListenerContainer]: Factory method 'dataUpdateListenerContainer' threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.setMessageListener(Ljava/lang/Object;)V
2020-05-30 15:08:53.538 INFO 41096 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'taskExecutor'
The class ListenerUtil
package de.mvbonline.commons.amqp.util;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
public class ListenerUtil {
private ListenerUtil() {
// No instanciation
}
public static MessageListenerContainer createListenerContainer(final ConnectionFactory connectionFactory, final MessageListener messageListener,
final String queueName, final int queueConsumers, final int queuePrefetch, final boolean queueListenersEnabled) {
if (!queueListenersEnabled || (queueConsumers <= 0)) {
return null;
}
final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setMessageListener(messageListener);
container.setQueueNames(queueName);
container.setConcurrentConsumers(queueConsumers);
container.setPrefetchCount(queuePrefetch);
return container;
}
}
I have recognized that it is searching for setMessageListener(java.lang.Object) while the method should be setMessageListener(org.springframework.amqp.core.MessageListener) but why is there something searched - we are not using any spring magic at this line of code.
Comment From: bmaehr
Sorry for bothering you. The Problem was a change of the method signature of setMessageListener between spring-rabbit 2.2.7 and spring-rabbit 1.4.0
Comment From: anandbikas
@bmaehr How did you solve it?
Comment From: bmaehr
@anandbikas You shouldnt have a problem if you support only one of the versions.
We have both - thats why I use this code:
try {
container.setMessageListener(messageListener);
}
catch (final NoSuchMethodError e) {
// Compatibility with new spring-rabbit versions
try {
final Method m = AbstractMessageListenerContainer.class.getDeclaredMethod("setMessageListener", MessageListener.class);
m.invoke(container, messageListener);
}
catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e2) {
// Restore previous exception
throw e;
}
}
Comment From: anandbikas
@bmaehr Earlier, I had got it working with xml based container configuration.
Recently I learned that one of the dependent jar was compiled with an older version of AMQP 1.7.11.RELEASE whereas the main application was using 2.3.5.
I compiled the dependent jar with upgraded AMQP and then it started working. This looks to me like some compile time binding.
Comment From: bmaehr
No, the signature has changed (the type of the parameter). Both signatures accept a parameter of the type MessageListener. but the old one did accept more. It was really tricky to find.