Browse Source

Properly analyze Java 9 class cast messages for lambda event listeners

Issue: SPR-16435
pull/1656/head
Juergen Hoeller 7 years ago
parent
commit
89d2bd954a
  1. 19
      spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
  2. 26
      spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java

19
spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -173,8 +173,9 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM @@ -173,8 +173,9 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || msg.startsWith(event.getClass().getName())) {
if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
@ -186,4 +187,18 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM @@ -186,4 +187,18 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
}
}
private boolean matchesClassCastMessage(String classCastMessage, String eventClassName) {
// On Java 8, the message simply starts with the class name: "java.lang.String cannot be cast..."
if (classCastMessage.startsWith(eventClassName)) {
return true;
}
// On Java 9, the message contains the module name: "java.base/java.lang.String cannot be cast..."
int moduleSeparatorIndex = classCastMessage.indexOf('/');
if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClassName, moduleSeparatorIndex + 1)) {
return true;
}
// Assuming an unrelated class cast failure...
return false;
}
}

26
spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -472,6 +472,30 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen @@ -472,6 +472,30 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
context.close();
}
@Test
public void lambdaAsListenerWithJava8StyleClassCastMessage() {
StaticApplicationContext context = new StaticApplicationContext();
ApplicationListener<ApplicationEvent> listener =
event -> { throw new ClassCastException(event.getClass().getName()); };
context.addApplicationListener(listener);
context.refresh();
context.publishEvent(new MyEvent(context));
context.close();
}
@Test
public void lambdaAsListenerWithJava9StyleClassCastMessage() {
StaticApplicationContext context = new StaticApplicationContext();
ApplicationListener<ApplicationEvent> listener =
event -> { throw new ClassCastException("spring.context/" + event.getClass().getName()); };
context.addApplicationListener(listener);
context.refresh();
context.publishEvent(new MyEvent(context));
context.close();
}
@Test
public void beanPostProcessorPublishesEvents() {
GenericApplicationContext context = new GenericApplicationContext();

Loading…
Cancel
Save