diff --git a/kubernetes-kit-starter/src/main/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/Job.java b/kubernetes-kit-starter/src/main/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/Job.java index 4d0d90d..aa74207 100644 --- a/kubernetes-kit-starter/src/main/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/Job.java +++ b/kubernetes-kit-starter/src/main/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/Job.java @@ -9,8 +9,10 @@ */ package com.vaadin.kubernetes.starter.sessiontracker.serialization.debug; +import java.io.InvalidObjectException; import java.io.ObjectStreamClass; import java.lang.invoke.SerializedLambda; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; @@ -194,10 +196,11 @@ void deserializationFailed(Exception ex) { outcome.add(Outcome.DESERIALIZATION_FAILED); log(CATEGORY_ERRORS, Outcome.DESERIALIZATION_FAILED.name() + ": " + ex.getMessage()); - if (ex instanceof ClassCastException - && ex.getMessage().contains(SerializedLambda.class.getName()) + Throwable cause = tryUnwrapLambdaDeserializationCause(ex); + if (cause instanceof ClassCastException + && cause.getMessage().contains(SerializedLambda.class.getName()) && !serializedLambdaMap.isEmpty()) { - String targetType = tryDetectClassCastTarget(ex.getMessage()); + String targetType = tryDetectClassCastTarget(cause.getMessage()); if (targetType != null) { String bestCandidates = serializedLambdaMap.values().stream() .filter(serializedLambda -> serializedLambda @@ -223,6 +226,19 @@ void deserializationFailed(Exception ex) { } } + // Base on the JDK version, SerializedLambda.readResolve may differently + // wrap the cause of error + private Throwable tryUnwrapLambdaDeserializationCause(Throwable exception) { + Throwable cause = exception; + if (cause instanceof InvalidObjectException) { + cause = cause.getCause(); + } + if (cause instanceof InvocationTargetException) { + cause = cause.getCause(); + } + return cause; + } + private Logger getLogger() { return LoggerFactory.getLogger(DebugMode.class); } diff --git a/kubernetes-kit-starter/src/test/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/SerializationDebugRequestHandlerTest.java b/kubernetes-kit-starter/src/test/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/SerializationDebugRequestHandlerTest.java index d7eaaa3..9cdb1ed 100644 --- a/kubernetes-kit-starter/src/test/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/SerializationDebugRequestHandlerTest.java +++ b/kubernetes-kit-starter/src/test/java/com/vaadin/kubernetes/starter/sessiontracker/serialization/debug/SerializationDebugRequestHandlerTest.java @@ -1,21 +1,23 @@ package com.vaadin.kubernetes.starter.sessiontracker.serialization.debug; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.lang.invoke.SerializedLambda; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; import org.assertj.core.api.SoftAssertions; import org.assertj.core.data.Index; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.springframework.mock.web.MockHttpSession; @@ -314,8 +316,40 @@ void handleRequest_serializationTimeout_timeoutReported() { } @Test - @Disabled("Find a way to simulate SerializedLambda ClassCastException") void handleRequest_lambdaSelfReferenceClassCast_errorCaught() { + + LambdaDeserializationClassCast holder = new LambdaDeserializationClassCast(); + httpSession.setAttribute("OBJ1", holder.selfReferenceLambda); + + runDebugTool(); + Result result = resultHolder.get(); + assertThat(result.getOutcomes()) + .containsExactly(Outcome.DESERIALIZATION_FAILED); + String unserializableInfo = String.join(System.lineSeparator(), + result.getErrors()); + assertThat(unserializableInfo) + .contains("cannot assign instance of " + + SerializedLambda.class.getName()) + .contains("field " + + LambdaDeserializationClassCast.class.getName() + + ".selfReferenceLambda") + .contains("in instance of " + + LambdaDeserializationClassCast.class.getName()) + .contains( + "SERIALIZED LAMBDA CLASS CAST EXCEPTION BEST CANDIDATES") + .contains("SerializedLambda[capturingClass=class " + + LambdaDeserializationClassCast.class.getName()); + } + + static class LambdaDeserializationClassCast implements Serializable { + + private final Serializable capture = new Serializable() { + }; + + private final SerializableRunnable selfReferenceLambda = () -> { + Objects.requireNonNull(capture); + }; + } private void assertDebugToolNotExecuted() {