Affects: 5.3.2
I am trying to programatically register ApplicationListener
via lambda and ApplicationListener.forPayload
like this:
class EventBusListenersRegistration(
private val applicationContext: ConfigurableApplicationContext,
private val eventListenersProvider: ObjectProvider<EventListener<TestEvent>>
): ApplicationRunner {
override fun run(args: ApplicationArguments?) {
eventListenersProvider.forEach{ eventListener ->
applicationContext.addApplicationListener(ApplicationListener.forPayload<TestEvent> { eventListener.on(it) }) }
}
}
and I am getting ClassCastException since this does not match. ReadinessState is payload of AvailabilityChangeEvent.
I guess there is something wrong how payload is handled
Comment From: bukajsytlos
I "hacked" it like this
private boolean matchesClassCastMessage(String classCastMessage, ApplicationEvent event) {
Class<?> eventClass = event.getClass();
if (event instanceof PayloadApplicationEvent) {
eventClass = ((PayloadApplicationEvent) event).getPayload().getClass();
}
// On Java 8, the message starts with the class name: "java.lang.String cannot be cast..."
if (classCastMessage.startsWith(eventClass.getName())) {
return true;
}
...
Is this correct solution?
Comment From: snicoll
@bukajsytlos sorry but I don't really understand what's happening here. Can you please move that code in text in a small project (for instance created from start.spring.io) and share it here? (something that we could run ourselves and that would reproduce the exception. Thank you.
Comment From: bukajsytlos
yes sorry, just run test and you get
java.lang.ClassCastException: class org.springframework.boot.availability.ReadinessState cannot be cast to class com.example.springframework.issue26349.TestEvent (org.springframework.boot.availability.ReadinessState and com.example.springframework.issue26349.TestEvent are in unnamed module of loader 'app')
at org.springframework.context.ApplicationListener.lambda$forPayload$0(ApplicationListener.java:59) ~[spring-context-5.3.2.jar:5.3.2]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.3.2.jar:5.3.2]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.3.2.jar:5.3.2]
Comment From: bukajsytlos
Looking at past issues, this is the domain of @jhoeller
Comment From: snicoll
Thanks @bukajsytlos there's no need to ping individual team members.
Comment From: snicoll
Thank you for sharing a sample.
ApplicationListener.<TestEvent>forPayload(
is a classic case of type erasure. While you can "force" the type so that the consumer matches your expectation, this is going to register an ApplicationListener<Object>
down the line. This listener is therefore listening to everything since its payload type is not constrained, hence the exception you're experiencing.
I've pinged Juergen earlier this afternoon to understand why this method has been introduced in 5.3 and how we could make this more apparent to users.