Kubernetes Kit Session Replication Debug Tool
The Kubernetes Kit session replication debugging tool is composed of three components working together: HTTP Session Replication Tester; Vaadin RequestHandler
; and HTTP Filter.
The tester tries to serialize and deserialize the HTTP session. The RequestHandler
evaluates if the HTTP request produces UI changes and prepares the tester execution and the result notification on the UI notification, if server push is enabled. The HTTP filter runs the tester after the HTTP request has been processed.
Enabling Debug Tool
The debug tool won’t work in production builds. During development, it works only if a couple of conditions are met.
First, vaadin.devmode.sessionSerialization.enabled
application configuration property has to be set to true
. You may set it either in a properties file or in a YAML file like so:
vaadin.devmode.sessionSerialization.enabled=true
Second, the sun.io.serialization.extendedDebugInfo
system property has to be set to true
and the JVM has to have been started with the --add-opens java.base/java.io=ALL-UNNAMED
flag. How you provide these factors depends on whether you run your application with the Spring Boot Maven Plugin or by directly executing the application JAR.
You can define the configuration in the pom.xml
file, or define it as environment variables when you run the application from the command-line.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jvmArguments>
--add-opens java.base/java.io=ALL-UNNAMED
-Dsun.io.serialization.extendedDebugInfo=true
</jvmArguments>
</configuration>
</plugin>
Caution
|
Potential side-effects from serialization when using extended debug info.
The sun.io.serialization.extendedDebugInfo system property can be useful in making exception messages more verbose during serialization. The toString() method is used to represent serialized objects, and in rare cases this can cause issues which are not related to serialization. For example, with Hibernate, PersistentList.toString() forces initialization of the lazy loaded collection. If this happens without an active Hibernate session, an exception is thrown.
|
The debug tool is installed automatically on Spring Boot projects. Otherwise, you can install it, manually. Configure the provided Service Init Listener, either by adding an entry in META-INF/services/VaadinServiceInitListener
or by defining a bean if using the Vaadin Spring add-on.
Additionally, the HTTP filter must be registered either as a Spring bean, or with a ServletContextListener
.
@Configuration
class AppConfig {
@Bean
VaadinServiceInitListener serializationDebugInitListener() {
return new SerializationDebugRequestHandler.InitListener();
}
@Bean
@Order(Integer.MIN_VALUE)
FilterRegistrationBean<SerializationDebugRequestHandler.Filter> sessionSerializationDebugToolFilter() {
return new FilterRegistrationBean<>(
new SerializationDebugRequestHandler.Filter());
}
}
Session Serialization Timeout
When the HTTP Session Replication Tester tries to serialize and deserialize an HTTP session, the process may take longer for larger sessions. The Debug Tool uses a timeout to wait for this process to finish. However, if the serialization process takes longer than the timeout period, it aborts the waiting and continues with the next steps. Serialization timeout is logged, and also appears in the results to notify the user. To allow more time, configure the serialization timeout
property, which is set in milliseconds.
In SpringBoot applications, it’s available in the SerializationProperties
configuration properties. The timeout can be set through application properties like so:
vaadin.serialization.timeout=60000
In non SpringBoot applications, a system property is available to set timeout:
vaadin.serialization.timeout=60000
If not configured, timeout defaults to 30 seconds.
Serialization Test Results
The results from the test are saved in the server logs. They include test process outcomes (e.g., SERIALIZATION_FAILED
, DESERIALIZATION_FAILED
, SUCCESS
, etc.). They also include a list of unserializable classes, object class graph in case of deserialization errors, and potential causes of SerializedLambda
ClassCastException
.
As an example, suppose you have an application that isn’t coded from the beginning to support session replication. With the Kubernetes Kit session replication debug tool in action, issues like the following can be spotted during development mode by checking server logs.
For views directly or indirectly referencing unserializable objects, the classes causing issues are reported. Double check those classes and make them Serializable
. Then navigate to the view again to see if the issues are solved or if other problems have arisen.
Session serialization attempt completed in 382 ms with outcomes: [SERIALIZATION_FAILED, NOT_SERIALIZABLE_CLASSES]
NOT SERIALIZABLE CLASSES FOUND:
===============================
com.vaadin.starter.bakery.ui.views.storefront.OrderPresenter
Start Track ID: 266, Stack depth: 33. Reference stack;
- element of array (index: 0)
- array (class "[Ljava.lang.Object;", size: 1)
- field (class "java.lang.invoke.SerializedLambda", name: "capturedArgs", type: "class [Ljava.lang.Object;")
- object (class "java.lang.invoke.SerializedLambda", SerializedLambda[capturingClass=class com.vaadin.starter.bakery.ui.views.storefront.StorefrontView, functionalInterfaceMethod=com/vaadin/flow/function/ValueProvider.apply:(Ljava/lang/Object;)Ljava/lang/Object;, implementation=invokeStatic com/vaadin/starter/bakery/ui/views/storefront/StorefrontView.lambda$new$497a7bcf$1:(Lcom/vaadin/starter/bakery/ui/views/storefront/OrderPresenter;Lcom/vaadin/starter/bakery/backend/data/entity/Order;)Ljava/lang/Object;, instantiatedMethodType=(Lcom/vaadin/starter/bakery/backend/data/entity/Order;)Ljava/lang/Object;, numCaptured=1])
[.... omitted .... ]
End Track ID: 216
com.vaadin.starter.bakery.ui.utils.converters.LocalTimeConverter
Start Track ID: 4383, Stack depth: 70. Reference stack:
- element of array (index: 0)
- array (class "[Ljava.lang.Object;", size: 1)
- field (class "java.lang.invoke.SerializedLambda", name: "capturedArgs", type: "class [Ljava.lang.Object;")
- object (class "java.lang.invoke.SerializedLambda", SerializedLambda[capturingClass=class com.vaadin.starter.bakery.ui.views.orderedit.OrderEditor, functionalInterfaceMethod=com/vaadin/flow/component/ItemLabelGenerator.apply:(Ljava/lang/Object;)Ljava/lang/String;, implementation=invokeVirtual com/vaadin/starter/bakery/ui/utils/converters/LocalTimeConverter.encode:(Ljava/time/LocalTime;)Ljava/lang/String;, instantiatedMethodType=(Ljava/time/LocalTime;)Ljava/lang/String;, numCaptured=1])
- field (class "com.vaadin.flow.component.combobox.ComboBoxBase", name: "itemLabelGenerator", type: "interface com.vaadin.flow.component.ItemLabelGenerator")
- object (class "com.vaadin.flow.component.combobox.ComboBox", com.vaadin.flow.component.combobox.ComboBox@6a256d7f)
[.... omitted .... ]
End Track ID: 3268
com.vaadin.starter.bakery.backend.service.PickupLocationService
Start Track ID: 3348, Stack depth: 78. Reference stack:
- field (class "com.vaadin.starter.bakery.ui.crud.CrudEntityDataProvider", name: "crudService", type: "interface com.vaadin.starter.bakery.backend.service.FilterableCrudService")
- object (class "com.vaadin.starter.bakery.ui.crud.CrudEntityDataProvider", com.vaadin.starter.bakery.ui.crud.CrudEntityDataProvider@42830bd9)
- field (class "com.vaadin.flow.data.provider.DataCommunicator", name: "dataProvider", type: "interface com.vaadin.flow.data.provider.DataProvider")
- object (class "com.vaadin.flow.component.combobox.ComboBoxDataCommunicator", com.vaadin.flow.component.combobox.ComboBoxDataCommunicator@1ae5194d)
- field (class "com.vaadin.flow.component.combobox.ComboBoxDataController", name: "dataCommunicator", type: "class com.vaadin.flow.component.combobox.ComboBoxDataCommunicator")
- object (class "com.vaadin.flow.component.combobox.ComboBoxDataController", com.vaadin.flow.component.combobox.ComboBoxDataController@351fed32)
[.... omitted .... ]
End Track ID: 3348
com.vaadin.starter.bakery.ui.views.orderedit.OrderEditor$$Lambda$2071/0x0000000800c7a840
[ SAM interface: java.util.function.Function.compose(java.util.function.Function) ]
Start Track ID: 3525, Stack depth: 69. Reference stack:
- element of array (index: 0)
- array (class "[Ljava.lang.Object;", size: 1)
- field (class "java.lang.invoke.SerializedLambda", name: "capturedArgs", type: "class [Ljava.lang.Object;")
- object (class "java.lang.invoke.SerializedLambda", SerializedLambda[capturingClass=class com.vaadin.starter.bakery.ui.dataproviders.DataProviderUtil, functionalInterfaceMethod=com/vaadin/flow/component/ItemLabelGenerator.apply:(Ljava/lang/Object;)Ljava/lang/String;, implementation=invokeStatic com/vaadin/starter/bakery/ui/dataproviders/DataProviderUtil.lambda$createItemLabelGenerator$bbbabe6b$1:(Ljava/util/function/Function;Ljava/lang/Object;)Ljava/lang/String;, instantiatedMethodType=(Ljava/lang/Object;)Ljava/lang/String;, numCaptured=1])
[.... omitted .... ]
End Track ID: 3525
com.vaadin.starter.bakery.backend.service.ProductService
Start Track ID: 3947, Stack depth: 68. Reference stack:
- field (class "com.vaadin.starter.bakery.ui.crud.CrudEntityDataProvider", name: "crudService", type: "interface com.vaadin.starter.bakery.backend.service.FilterableCrudService")
- object (class "com.vaadin.starter.bakery.ui.crud.CrudEntityDataProvider", com.vaadin.starter.bakery.ui.crud.CrudEntityDataProvider@7392f310)
- field (class "com.vaadin.starter.bakery.ui.views.orderedit.OrderItemsEditor", name: "productDataProvider", type: "interface com.vaadin.flow.data.provider.DataProvider")
- object (class "com.vaadin.starter.bakery.ui.views.orderedit.OrderItemsEditor", com.vaadin.starter.bakery.ui.views.orderedit.OrderItemsEditor@2a64d7d9)
[.... omitted .... ]
End Track ID: 3947
com.vaadin.starter.bakery.ui.views.orderedit.OrderEditor$$Lambda$2032/0x0000000800c74c40
[ SAM interface: java.util.function.Function.compose(java.util.function.Function) ]
Start Track ID: 4182, Stack depth: 49. Reference stack:
- element of array (index: 0)
- array (class "[Ljava.lang.Object;", size: 1)
- field (class "java.lang.invoke.SerializedLambda", name: "capturedArgs", type: "class [Ljava.lang.Object;")
- object (class "java.lang.invoke.SerializedLambda", SerializedLambda[capturingClass=class com.vaadin.starter.bakery.ui.dataproviders.DataProviderUtil, functionalInterfaceMethod=com/vaadin/flow/component/ItemLabelGenerator.apply:(Ljava/lang/Object;)Ljava/lang/String;, implementation=invokeStatic com/vaadin/starter/bakery/ui/dataproviders/DataProviderUtil.lambda$createItemLabelGenerator$bbbabe6b$1:(Ljava/util/function/Function;Ljava/lang/Object;)Ljava/lang/String;, instantiatedMethodType=(Ljava/lang/Object;)Ljava/lang/String;, numCaptured=1])
- field (class "com.vaadin.flow.component.combobox.ComboBoxBase", name: "itemLabelGenerator", type: "interface com.vaadin.flow.component.ItemLabelGenerator")
- object (class "com.vaadin.flow.component.combobox.ComboBox", com.vaadin.flow.component.combobox.ComboBox@7a86589e)
[.... omitted .... ]
End Track ID: 4182
com.vaadin.starter.bakery.backend.service.OrderService$$EnhancerBySpringCGLIB$$212d0636
Start Track ID: 4757, Stack depth: 10. Reference stack:
- field (class "com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider", name: "orderService", type: "class com.vaadin.starter.bakery.backend.service.OrderService")
- object (class "com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider", com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider@39940b23)
[.... omitted .... ]
End Track ID: 4757
com.vaadin.starter.bakery.ui.views.storefront.OrderPresenter$$Lambda$1943/0x0000000800c4d040
[ SAM interface: java.util.function.Consumer.andThen(java.util.function.Consumer) ]
Start Track ID: 4758, Stack depth: 10. Reference stack:
- field (class "com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider", name: "pageObserver", type: "interface java.util.function.Consumer")
- object (class "com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider", com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider@39940b23)
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {ordersGridDataProvider=com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider@39940b23})
- field (class "com.vaadin.flow.spring.scopes.BeanStore", name: "objects", type: "interface java.util.Map")
- object (class "com.vaadin.flow.spring.scopes.BeanStore", com.vaadin.flow.spring.scopes.BeanStore@55b88ad)
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {1=com.vaadin.flow.spring.scopes.BeanStore@55b88ad})
- field (class "com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper", name: "uiStores", type: "interface java.util.Map")
- object (class "com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper", com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper@53b93285)
[.... omitted .... ]
End Track ID: 4758
To solve SerializedLambda
class cast exceptions during deserialization, analyze the class graph from the bottom to the top and search for known classes. Check the BEST CANDIDATES
sections to identify the failing lambda expression.
Session serialization attempt completed in 1375 ms with outcomes: [SERIALIZATION_FAILED, NOT_SERIALIZABLE_CLASSES, DESERIALIZATION_FAILED]
ERRORS DURING SERIALIZATION/DESERIALIZATION PROCESS:
====================================================
DESERIALIZATION_FAILED: cannot assign instance of java.lang.invoke.SerializedLambda to field com.vaadin.flow.component.ComponentEventBus$ListenerWrapper.listener of type com.vaadin.flow.component.ComponentEventListener in instance of com.vaadin.flow.component.ComponentEventBus$ListenerWrapper
SERIALIZED LAMBDA CLASS CAST EXCEPTION BEST CANDIDATES:
=======================================================
SerializedLambda[capturingClass=class com.vaadin.starter.bakery.ui.views.dashboard.DashboardView, functionalInterfaceMethod=com/vaadin/flow/component/ComponentEventListener.onComponentEvent:(Lcom/vaadin/flow/component/ComponentEvent;)V, implementation=invokeSpecial com/vaadin/starter/bakery/ui/views/dashboard/DashboardView.lambda$measurePageLoadPerformance$387549c5$1:(Ljava/util/concurrent/atomic/AtomicInteger;Lcom/vaadin/flow/component/charts/events/ChartLoadEvent;)V, instantiatedMethodType=(Lcom/vaadin/flow/component/charts/events/ChartLoadEvent;)V, numCaptured=2]
- field (class "com.vaadin.flow.component.ComponentEventBus$ListenerWrapper", name: "listener", type: "interface com.vaadin.flow.component.ComponentEventListener")
- object (class "com.vaadin.flow.component.ComponentEventBus$ListenerWrapper", com.vaadin.flow.component.ComponentEventBus$ListenerWrapper@3cfd4b4e)
- custom writeObject data (class "java.util.ArrayList")
- object (class "java.util.ArrayList", [com.vaadin.flow.component.ComponentEventBus$ListenerWrapper@3cfd4b4e])
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {class com.vaadin.flow.component.charts.events.ChartLoadEvent=[com.vaadin.flow.component.ComponentEventBus$ListenerWrapper@3cfd4b4e]})
- field (class "com.vaadin.flow.component.ComponentEventBus", name: "componentEventData", type: "class java.util.HashMap")
- object (class "com.vaadin.flow.component.ComponentEventBus", com.vaadin.flow.component.ComponentEventBus@1f42a3e)
- field (class "com.vaadin.flow.component.Component", name: "eventBus", type: "class com.vaadin.flow.component.ComponentEventBus")
- object (class "com.vaadin.flow.component.charts.Chart", com.vaadin.flow.component.charts.Chart@1d873f81)
- field (class "com.vaadin.flow.internal.nodefeature.ComponentMapping", name: "component", type: "class com.vaadin.flow.component.Component")
- object (class "com.vaadin.flow.internal.nodefeature.ComponentMapping", com.vaadin.flow.internal.nodefeature.ComponentMapping@103098d)
- element of array (index: 3)
- array (class "[Lcom.vaadin.flow.internal.nodefeature.NodeFeature;", size: 8)
- field (class "com.vaadin.flow.internal.StateNode", name: "features", type: "interface java.io.Serializable")
- object (class "com.vaadin.flow.internal.StateNode", com.vaadin.flow.internal.StateNode@5753faff)
- custom writeObject data (class "java.util.HashSet")
- object (class "java.util.LinkedHashSet", [com.vaadin.flow.internal.StateNode@5753faff, com.vaadin.flow.internal.StateNode@715baa6, com.vaadin.flow.internal.StateNode@2002fea9, com.vaadin.flow.internal.StateNode@52b50419, com.vaadin.flow.internal.StateNode@253f0346, com.vaadin.flow.internal.StateNode@32fba91, com.vaadin.flow.internal.StateNode@598ff72d, com.vaadin.flow.internal.StateNode@105e75d3, com.vaadin.flow.internal.StateNode@52acc625, com.vaadin.flow.internal.StateNode@25be0e23, com.vaadin.flow.internal.StateNode@102dfa9, com.vaadin.flow.internal.StateNode@18580649, com.vaadin.flow.internal.StateNode@1e1751e3, com.vaadin.flow.internal.StateNode@4fa4edf8, com.vaadin.flow.internal.StateNode@4d41533b, com.vaadin.flow.internal.StateNode@2e470fb9, com.vaadin.flow.internal.StateNode@26527a8d, com.vaadin.flow.internal.StateNode@13fd3186, com.vaadin.flow.internal.StateNode@acffdd6, com.vaadin.flow.internal.StateNode@3a5f4c8b, com.vaadin.flow.internal.StateNode@e1d32e9])
- field (class "com.vaadin.flow.internal.StateTree", name: "dirtyNodes", type: "interface java.util.Set")
- object (class "com.vaadin.flow.internal.StateTree", com.vaadin.flow.internal.StateTree@5199da78)
- field (class "com.vaadin.flow.internal.StateNode", name: "owner", type: "interface com.vaadin.flow.internal.NodeOwner")
- object (class "com.vaadin.flow.internal.StateNode", com.vaadin.flow.internal.StateNode@5a9ad499)
- custom writeObject data (class "java.util.ArrayList")
- object (class "java.util.ArrayList", [com.vaadin.flow.internal.StateNode@5a9ad499])
- field (class "com.vaadin.flow.internal.nodefeature.NodeList", name: "values", type: "interface java.util.List")
- object (class "com.vaadin.flow.internal.nodefeature.ElementChildrenList", com.vaadin.flow.internal.nodefeature.ElementChildrenList@1794b4cc)
- element of array (index: 1)
- array (class "[Lcom.vaadin.flow.internal.nodefeature.NodeFeature;", size: 13)
- field (class "com.vaadin.flow.internal.StateNode", name: "features", type: "interface java.io.Serializable")
- object (class "com.vaadin.flow.internal.StateNode", com.vaadin.flow.internal.StateNode@3989de96)
- field (class "com.vaadin.flow.dom.Node", name: "node", type: "class com.vaadin.flow.internal.StateNode")
- object (class "com.vaadin.flow.dom.Element", <vaadin-grid suppress-template-warning multi-sort-priority="prepend" theme="orders dashboard" id="ordersGrid">
<vaadin-grid-column suppress-template-warning></vaadin-grid-column>
</vaadin-grid>)
- field (class "com.vaadin.flow.component.grid.GridArrayUpdater$UpdateQueueData", name: "element", type: "class com.vaadin.flow.dom.Element")
- object (class "com.vaadin.flow.component.grid.GridArrayUpdater$UpdateQueueData", com.vaadin.flow.component.grid.GridArrayUpdater$UpdateQueueData@36801ef0)
- field (class "com.vaadin.flow.component.grid.Grid$GridArrayUpdaterImpl", name: "data", type: "class com.vaadin.flow.component.grid.GridArrayUpdater$UpdateQueueData")
- object (class "com.vaadin.flow.component.grid.Grid$GridArrayUpdaterImpl", com.vaadin.flow.component.grid.Grid$GridArrayUpdaterImpl@349c650f)
- field (class "com.vaadin.flow.data.provider.DataCommunicator", name: "arrayUpdater", type: "interface com.vaadin.flow.data.provider.ArrayUpdater")
- object (class "com.vaadin.flow.data.provider.DataCommunicator", com.vaadin.flow.data.provider.DataCommunicator@563c6dd6)
- element of array (index: 0)
- array (class "[Ljava.lang.Object;", size: 1)
- field (class "java.lang.invoke.SerializedLambda", name: "capturedArgs", type: "class [Ljava.lang.Object;")
- object (class "java.lang.invoke.SerializedLambda", SerializedLambda[capturingClass=class com.vaadin.flow.data.provider.DataCommunicator, functionalInterfaceMethod=com/vaadin/flow/data/provider/DataProviderListener.onDataChange:(Lcom/vaadin/flow/data/provider/DataChangeEvent;)V, implementation=invokeSpecial com/vaadin/flow/data/provider/DataCommunicator.lambda$handleAttach$425c8a01$1:(Lcom/vaadin/flow/data/provider/DataChangeEvent;)V, instantiatedMethodType=(Lcom/vaadin/flow/data/provider/DataChangeEvent;)V, numCaptured=1])
- field (class "com.vaadin.flow.data.provider.AbstractDataProvider$1", name: "val$listener", type: "interface com.vaadin.flow.data.provider.DataProviderListener")
- object (class "com.vaadin.flow.data.provider.AbstractDataProvider$1", com.vaadin.flow.data.provider.AbstractDataProvider$1@36139299)
- field (class "com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper", name: "listener", type: "interface com.vaadin.flow.function.SerializableConsumer")
- object (class "com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper", com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper@772e98a2)
- custom writeObject data (class "java.util.ArrayList")
- object (class "java.util.ArrayList", [com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper@772e98a2, com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper@7311029f])
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {class com.vaadin.flow.data.provider.DataChangeEvent=[com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper@772e98a2, com.vaadin.flow.data.provider.AbstractDataProvider$DataListenerWrapper@7311029f]})
- field (class "com.vaadin.flow.data.provider.AbstractDataProvider", name: "listeners", type: "class java.util.HashMap")
- object (class "com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider", com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider@39940b23)
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {ordersGridDataProvider=com.vaadin.starter.bakery.ui.dataproviders.OrdersGridDataProvider@39940b23})
- field (class "com.vaadin.flow.spring.scopes.BeanStore", name: "objects", type: "interface java.util.Map")
- object (class "com.vaadin.flow.spring.scopes.BeanStore", com.vaadin.flow.spring.scopes.BeanStore@55b88ad)
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {1=com.vaadin.flow.spring.scopes.BeanStore@55b88ad})
- field (class "com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper", name: "uiStores", type: "interface java.util.Map")
- object (class "com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper", com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper@53b93285)
- custom writeObject data (class "java.util.HashMap")
- object (class "java.util.HashMap", {com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper=com.vaadin.flow.spring.scopes.VaadinUIScope$UIStoreWrapper@53b93285, com.vaadin.flow.server.SessionRouteRegistry=com.vaadin.flow.server.SessionRouteRegistry@4f248e99, clientRoutingMode=false})
- field (class "com.vaadin.flow.server.Attributes", name: "attributes", type: "class java.util.HashMap")
- object (class "com.vaadin.flow.server.Attributes", com.vaadin.flow.server.Attributes@46ee4d44)
- field (class "com.vaadin.flow.server.VaadinSession", name: "attributes", type: "class com.vaadin.flow.server.Attributes")
- custom writeObject data (class "com.vaadin.flow.server.VaadinSession")
- object (class "com.vaadin.flow.spring.SpringVaadinSession", com.vaadin.flow.spring.SpringVaadinSession@5544baac)
- custom writeObject data (class "java.util.HashMap")
- root object (class "java.util.HashMap", {com.vaadin.flow.server.VaadinSession.springServlet=com.vaadin.flow.spring.SpringVaadinSession@5544baac, springServlet.lock=java.util.concurrent.locks.ReentrantLock@46e4aae6[Unlocked], org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@5786acad, clusterKey=ba60b62d-ae4c-46ee-9259-45b03ec9623d_SOURCE:DAD56D60A91A82D69729F102FED0D322, SPRING_SECURITY_CONTEXT=SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [[email protected], Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_admin]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=9EEA113887DE836010569B45CD9744B3], Granted Authorities=[ROLE_admin]]]})
For example, on the above snippet when navigating to the application DashboardView
, the process failed to deserialize a lambda expression into com.vaadin.flow.component.ComponentEventListener
.
DESERIALIZATION_FAILED:
cannot assign instance of java.lang.invoke.SerializedLambda
to field com.vaadin.flow.component.ComponentEventBus$ListenerWrapper.listener
of type com.vaadin.flow.component.ComponentEventListener
in instance of com.vaadin.flow.component.ComponentEventBus$ListenerWrapper
Checking the BEST CANDIDATES
section, it’s possible to see that there is an entry whose capturingClass
is DashboardView
. The lambda captures a ChartLoadEvent
. The method defining the lambda expression can be detected from the implementation
attribute, that is measurePageLoadPerformance
in DashboardView
.
SERIALIZED LAMBDA CLASS CAST EXCEPTION BEST CANDIDATES:
=======================================================
SerializedLambda[
capturingClass=class com.vaadin.starter.bakery.ui.views.dashboard.DashboardView,
functionalInterfaceMethod=com/vaadin/flow/component/ComponentEventListener.onComponentEvent:(Lcom/vaadin/flow/component/ComponentEvent;)V,
implementation=invokeSpecial com/vaadin/starter/bakery/ui/views/dashboard/DashboardView.lambda$measurePageLoadPerformance$387549c5$1:(Ljava/util/concurrent/atomic/AtomicInteger;Lcom/vaadin/flow/component/charts/events/ChartLoadEvent;)V,
instantiatedMethodType=(Lcom/vaadin/flow/component/charts/events/ChartLoadEvent;)V,
numCaptured=2]
The mentioned method presents the following code. It can be deduced that the problem is that the lambda expression is capturing this
and providing it to other components that probably store a reference to it.
private void measurePageLoadPerformance() {
final int nTotal = 5; // the total number of charts on the page
AtomicInteger nLoaded = new AtomicInteger();
ComponentEventListener<ChartLoadEvent> chartLoadListener = (event) -> {
nLoaded.addAndGet(1);
if (nLoaded.get() == nTotal) {
UI.getCurrent().getPage().executeJs("$0._chartsLoadedResolve()", this); 1
}
};
todayCountChart.addChartLoadListener(chartLoadListener); 2
deliveriesThisMonthChart.addChartLoadListener(chartLoadListener);
deliveriesThisYearChart.addChartLoadListener(chartLoadListener);
yearlySalesGraph.addChartLoadListener(chartLoadListener);
monthlyProductSplit.addChartLoadListener(chartLoadListener);
}
-
Lambda expression captures
this
instance. -
Lambda expression is used as
ChartLoadListener
.
To fix this issue, replace the lambda expression with an anonymous class:
ComponentEventListener<ChartLoadEvent> chartLoadListener = new ComponentEventListener<>() {
@Override
public void onComponentEvent(ChartLoadEvent event) {
nLoaded.addAndGet(1);
if (nLoaded.get() == nTotal) {
UI.getCurrent().getPage().executeJs("$0._chartsLoadedResolve()", DashboardView.this);
}
}
};