fix: keep JS parameters serializable via custom serialization - #25275
fix: keep JS parameters serializable via custom serialization#25275Artur- wants to merge 1 commit into
Conversation
JsFunction and JavaScriptInvocation both declare Serializable but hold their values in a List<Object>, so nothing stopped a value that Java serialization cannot write from ending up in the HTTP session. Neither is reliably transient: addJsInitializer retains a JsFunction for the lifetime of the registration so the initializer can be re-run on every re-attach, and an invocation waits in the session when its target element is not yet attached or is invisible. A bad value surfaced as a NotSerializableException during session passivation. Two unrelated kinds of serialization are involved. Jackson encodes the values for the browser and works for any bean; Java serialization writes them to the session and works only for Serializable. Requiring Serializable would have narrowed what executeJs accepts, undoing the bean support added in #22378 and rejecting JsonNode-typed variables, since JsonNode does not itself declare Serializable. Instead both types now write a serialization proxy that converts each value through JacksonCodec.serializableParameter: values that are already Serializable pass through untouched, and anything else is stored as the JSON it encodes to. encodeWithoutTypeInfo passes a JsonNode through unchanged, so the client receives an identical message. Only non-serializable values are converted, which is what keeps Element and ReturnChannelRegistration encoding deferred to the moment the UIDL message is built — an element attached only after the session is restored must still encode as a DOM reference rather than the null it would have produced earlier. Restoring an invocation skips the constructor's dry-run encode, since deserialization can reach it before the state nodes behind element parameters are fully restored.
|
|
The approach here seems broken since the decision whether to use Jackson or not is only used on the top level. It will still use Jackson for nested Element references (e.g. if you pass a My gut feeling is that we might not need this functionality at all since the application is typically in control of the actual types that are used and a serializable application won't use leaf types that are not serializable. There might be be some cases of add-ons using simple records in their JSON format but that's no different that add-ons that might use simple records for storing "tuples" in instance fields. So I'd suggest that we instead just document that it's recommended that parameters are |



JsFunction and JavaScriptInvocation both declare Serializable but hold their values in a List, so nothing stopped a value that Java serialization cannot write from ending up in the HTTP session. Neither is reliably transient: addJsInitializer retains a JsFunction for the lifetime of the registration so the initializer can be re-run on every re-attach, and an invocation waits in the session when its target element is not yet attached or is invisible. A bad value surfaced as a NotSerializableException during session passivation.
Two unrelated kinds of serialization are involved. Jackson encodes the values for the browser and works for any bean; Java serialization writes them to the session and works only for Serializable. Requiring Serializable would have narrowed what executeJs accepts, undoing the bean support added in #22378 and rejecting JsonNode-typed variables, since JsonNode does not itself declare Serializable.
Instead both types now write a serialization proxy that converts each value through JacksonCodec.serializableParameter: values that are already Serializable pass through untouched, and anything else is stored as the JSON it encodes to. encodeWithoutTypeInfo passes a JsonNode through unchanged, so the client receives an identical message.
Only non-serializable values are converted, which is what keeps Element and ReturnChannelRegistration encoding deferred to the moment the UIDL message is built — an element attached only after the session is restored must still encode as a DOM reference rather than the null it would have produced earlier. Restoring an invocation skips the constructor's dry-run encode, since deserialization can reach it before the state nodes behind element parameters are fully restored.