Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-14109

Wrong detection of event type on ApplicationListener<> when using lambdas (ClassCast Exception)

    Details

    • Last commented by a User:
      true

      Description

      Hello there,

      i hope this is indeed a Spring Framework and not a Spring Boot bug. If not, please excuse and move it to the right place.

      In my application i have a WebSocketMessageBroker (enabled through @EnableWebSocketMessageBroker) as well as a HttpSessionEventPublisher (enabled via @Bean) and a ApplicationListener<HttpSessionCreatedEvent>.

      If i use an anonymous inner class for the ApplicationListener, everything works fine.

      If i use a lambda like so

      public ApplicationListener<HttpSessionCreatedEvent> httpSessionCreatedEventListener() {
        return (HttpSessionCreatedEvent event) -> {
          LoggerFactory.getLogger(ClasscastApplication.class).info("Session created...");
        };
      }
      

      the generic type of my listener is incorrectly identified and a BrokerAvailabilityEvent is passed to it leading to:

      Caused by: java.lang.ClassCastException: org.springframework.messaging.simp.broker.BrokerAvailabilityEvent cannot be cast to org.springframework.security.web.session.HttpSessionCreatedEvent
      	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:163) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:136) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:381) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.publishBrokerAvailableEvent(AbstractBrokerMessageHandler.java:262) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler.startInternal(SimpleBrokerMessageHandler.java:178) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.start(AbstractBrokerMessageHandler.java:164) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:173) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
      	... 14 common frames omitted
      

      I guess that might happen with other events, too, but i noticed it in context of messaging.

      I have attached a demo application that reproduces that bug.

        Issue Links

          Activity

          Hide
          juergen.hoeller Juergen Hoeller added a comment -

          This is unfortunately a known limitation with lambda-defined callbacks: We cannot reliably introspect generic type declarations there upfront. That said, we should at least make our event processing defensive enough: catching ClassCastException and simply skipping the listener if the event doesn't match... I'll try that right away.

          Show
          juergen.hoeller Juergen Hoeller added a comment - This is unfortunately a known limitation with lambda-defined callbacks: We cannot reliably introspect generic type declarations there upfront. That said, we should at least make our event processing defensive enough: catching ClassCastException and simply skipping the listener if the event doesn't match... I'll try that right away.
          Hide
          michael.simons Michael Simons added a comment -

          Hello Juergen,
          thanks for looking into it so quickly.

          Sorry, didn't find the other ticket.

          If you skip the listener, a big warning is need, i think.
          Or: Throw a custom exception with the reason and explanation.

          Just for your interest: We've been using TypeTools which are linked in the SO question in SPR-12525 for detecting generic types of Lambdas successfully.

          Thanks again,
          Michael.

          Show
          michael.simons Michael Simons added a comment - Hello Juergen, thanks for looking into it so quickly. Sorry, didn't find the other ticket. If you skip the listener, a big warning is need, i think. Or: Throw a custom exception with the reason and explanation. Just for your interest: We've been using TypeTools which are linked in the SO question in SPR-12525 for detecting generic types of Lambdas successfully. Thanks again, Michael.
          Hide
          juergen.hoeller Juergen Hoeller added a comment -

          Skipping the listener in case of a ClassCastException should actually be reasonably safe: It just means that we were trying to invoke it with a non-matching event type which, if known upfront, would have to led to the listener getting skipped in any case...

          Show
          juergen.hoeller Juergen Hoeller added a comment - Skipping the listener in case of a ClassCastException should actually be reasonably safe: It just means that we were trying to invoke it with a non-matching event type which, if known upfront, would have to led to the listener getting skipped in any case...
          Hide
          michael.simons Michael Simons added a comment -

          Thanks for clarifying, now i understand, i thought you meant skipping that listener from being registered at all.

          Show
          michael.simons Michael Simons added a comment - Thanks for clarifying, now i understand, i thought you meant skipping that listener from being registered at all.
          Hide
          hp9000 Hans-Peter Werner added a comment -

          Hello Juergen,

          I try to catch and log exceptions at the site where an event is fired. When a listener is throwing a ClassCastException, this happens without being noticed (provided the log-level is not debug) because of that exception being catched within the SimpleApplicationEventMulticaster. I don't know a perfect solution for that problem, but I would rather have too much exceptions than too few

          Cheers
          hp

          Show
          hp9000 Hans-Peter Werner added a comment - Hello Juergen, I try to catch and log exceptions at the site where an event is fired. When a listener is throwing a ClassCastException, this happens without being noticed (provided the log-level is not debug) because of that exception being catched within the SimpleApplicationEventMulticaster. I don't know a perfect solution for that problem, but I would rather have too much exceptions than too few Cheers hp
          Hide
          juergen.hoeller Juergen Hoeller added a comment -

          As of 4.3.4 / 4.2.9, we're now just swallowing a ClassCastException if it affects the passed-in event class directly (as in the case of a lambda-defined listener for a specific event type).

          Show
          juergen.hoeller Juergen Hoeller added a comment - As of 4.3.4 / 4.2.9, we're now just swallowing a ClassCastException if it affects the passed-in event class directly (as in the case of a lambda-defined listener for a specific event type).
          Hide
          michael.simons Michael Simons added a comment -

          Thanks Jürgen for the update. Good to know.

          Show
          michael.simons Michael Simons added a comment - Thanks Jürgen for the update. Good to know.

            People

            • Assignee:
              juergen.hoeller Juergen Hoeller
              Reporter:
              michael.simons Michael Simons
              Last updater:
              Juergen Hoeller
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                47 weeks, 2 days ago