diff --git a/src/main/java/org/springframework/data/mapping/callback/DefaultEntityCallbacks.java b/src/main/java/org/springframework/data/mapping/callback/DefaultEntityCallbacks.java index 5a4ed8cb99..143377663a 100644 --- a/src/main/java/org/springframework/data/mapping/callback/DefaultEntityCallbacks.java +++ b/src/main/java/org/springframework/data/mapping/callback/DefaultEntityCallbacks.java @@ -117,7 +117,7 @@ public T invokeCallback(EntityCallback callback, T entity, throw new IllegalArgumentException( String.format("Callback invocation on %s returned null value for %s", callback.getClass(), entity)); - } catch (ClassCastException ex) { + } catch (IllegalArgumentException | ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || EntityCallbackInvoker.matchesClassCastMessage(msg, entity.getClass())) { diff --git a/src/main/java/org/springframework/data/mapping/callback/EntityCallbackInvoker.java b/src/main/java/org/springframework/data/mapping/callback/EntityCallbackInvoker.java index 358eb9c629..03ad874df9 100644 --- a/src/main/java/org/springframework/data/mapping/callback/EntityCallbackInvoker.java +++ b/src/main/java/org/springframework/data/mapping/callback/EntityCallbackInvoker.java @@ -35,21 +35,26 @@ interface EntityCallbackInvoker { Object invokeCallback(EntityCallback callback, T entity, BiFunction, T, Object> callbackInvokerFunction); - static boolean matchesClassCastMessage(String classCastMessage, Class eventClass) { + static boolean matchesClassCastMessage(String exceptionMessage, Class eventClass) { // On Java 8, the message starts with the class name: "java.lang.String cannot be cast..." - if (classCastMessage.startsWith(eventClass.getName())) { + if (exceptionMessage.startsWith(eventClass.getName())) { return true; } // On Java 11, the message starts with "class ..." a.k.a. Class.toString() - if (classCastMessage.startsWith(eventClass.toString())) { + if (exceptionMessage.startsWith(eventClass.toString())) { return true; } // On Java 9, the message used to contain the module name: "java.base/java.lang.String cannot be cast..." - int moduleSeparatorIndex = classCastMessage.indexOf('/'); - if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) { + int moduleSeparatorIndex = exceptionMessage.indexOf('/'); + if (moduleSeparatorIndex != -1 && exceptionMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) { + return true; + } + + // On Java 18, the message is "IllegalArgumentException: argument type mismatch" + if (exceptionMessage.equals("argument type mismatch")) { return true; } diff --git a/src/test/java/org/springframework/data/mapping/callback/DefaultEntityCallbacksUnitTests.java b/src/test/java/org/springframework/data/mapping/callback/DefaultEntityCallbacksUnitTests.java index d3c91d97c4..90734ec8e5 100644 --- a/src/test/java/org/springframework/data/mapping/callback/DefaultEntityCallbacksUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/callback/DefaultEntityCallbacksUnitTests.java @@ -28,6 +28,7 @@ import org.springframework.core.Ordered; import org.springframework.data.mapping.Person; import org.springframework.data.mapping.PersonDocument; +import org.springframework.data.mapping.PersonNoId; import org.springframework.data.mapping.callback.CapturingEntityCallback.FirstCallback; import org.springframework.data.mapping.callback.CapturingEntityCallback.SecondCallback; import org.springframework.data.mapping.callback.CapturingEntityCallback.ThirdCallback; @@ -95,9 +96,8 @@ void invokeInvalidEvent() { DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(); callbacks.addEntityCallback(new InvalidEntityCallback() {}); - assertThatIllegalStateException() - .isThrownBy(() -> callbacks.callback(InvalidEntityCallback.class, new PersonDocument(null, "Walter", null), - "agr0", Float.POSITIVE_INFINITY)); + assertThatIllegalStateException().isThrownBy(() -> callbacks.callback(InvalidEntityCallback.class, + new PersonDocument(null, "Walter", null), "agr0", Float.POSITIVE_INFINITY)); } @Test // DATACMNS-1467 @@ -126,8 +126,7 @@ void errorsOnNullEntity() { DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(); callbacks.addEntityCallback(new CapturingEntityCallback()); - assertThatIllegalArgumentException() - .isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, null)); + assertThatIllegalArgumentException().isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, null)); } @Test // DATACMNS-1467 @@ -144,18 +143,31 @@ void errorsOnNullValueReturnedByCallbackEntity() { PersonDocument initial = new PersonDocument(null, "Walter", null); - assertThatIllegalArgumentException() - .isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, initial)); + assertThatIllegalArgumentException().isThrownBy(() -> callbacks.callback(CapturingEntityCallback.class, initial)); assertThat(first.capturedValue()).isSameAs(initial); assertThat(second.capturedValue()).isNotNull().isNotSameAs(initial); assertThat(third.capturedValues()).isEmpty(); } + @Test // GH-2583 + void skipsInvocationUsingJava18ReflectiveTypeRejection() { + + DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(); + callbacks.addEntityCallback(new Java18ClassCastStyle()); + + Person person = new PersonNoId(42, "Walter", "White"); + + Person afterCallback = callbacks.callback(BeforeConvertCallback.class, person); + + assertThat(afterCallback).isSameAs(person); + } + @Test // DATACMNS-1467 void detectsMultipleCallbacksWithinOneClass() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MultipleCallbacksInOneClassConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + MultipleCallbacksInOneClassConfig.class); DefaultEntityCallbacks callbacks = new DefaultEntityCallbacks(ctx); @@ -288,4 +300,13 @@ public Person onBeforeSave(Person object) { } } + static class Java18ClassCastStyle implements BeforeConvertCallback { + + @Override + public Person onBeforeConvert(Person object) { + throw new IllegalArgumentException("argument type mismatch"); + } + + } + }