Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ListSignal with Java 8 date/time types fail with Jackson ObjectMapper #2891

Closed
rbrki07 opened this issue Nov 4, 2024 · 2 comments · Fixed by #2899
Closed

ListSignal with Java 8 date/time types fail with Jackson ObjectMapper #2891

rbrki07 opened this issue Nov 4, 2024 · 2 comments · Fixed by #2899
Assignees
Labels
bug Something isn't working hilla Issues related to Hilla Impact: High Severity: Major

Comments

@rbrki07
Copy link
Contributor

rbrki07 commented Nov 4, 2024

Describe the bug

I want to use the new ListSignal with a Java Record that contains a field of type LocalDateTime. This is currently not possible, as a request from the frontend to the backend containing such a field cannot be deserialized due to a Jackson data binding error.

Expected-behavior

I'm able to use the new ListSignal with a Java Record that contains a field of type LocalDateTime, LocalDate or Instant.

Reproduction

  • Create a new Hilla project using a current pre-release.
  • Enable feature flag for full stack signals
  • Create a ChatService like this:
package com.example.application.services;

import java.time.LocalDateTime;

import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.hilla.BrowserCallable;
import com.vaadin.hilla.signals.ListSignal;

@BrowserCallable
@AnonymousAllowed
public class ChatService {

    public record ChatMessage(String author, String message, LocalDateTime timestamp) { }
    
    private ListSignal<ChatMessage> messages = new ListSignal<>(ChatMessage.class);

    public ListSignal<ChatMessage> messages() {
        return messages;
    }
}
  • Create a ChatView like this:
import { HorizontalLayout, MessageInput, MessageList, TextField } from "@vaadin/react-components"
import { ChatService } from "Frontend/generated/endpoints"
import { useSignal } from '@vaadin/hilla-react-signals';

const chatMessages = ChatService.messages()

const convertToMessageListItem = (author: string, message: string, timestamp: string) => ({
  text: message,
  time: timestamp,
  userName: author,
})

const ChatView = () => {
    const username = useSignal('')

    return (
        <>
            <MessageList items={chatMessages.value.map((m) => convertToMessageListItem(m.value.message, m.value.author, m.value.timestamp))} />
            <HorizontalLayout theme={'margin'}>
                <TextField placeholder={'Your name'} onValueChanged={(e) => username.value = e.detail.value} className={'items-center'} />
                <MessageInput onSubmit={(e) => chatMessages.insertLast({ author: username.value, message: e.detail.value, timestamp: new Date().toISOString() })} className={'flex-grow'} />
            </HorizontalLayout>
        </>
    )
}

export default ChatView

If you submit a new message to the backend, you will see the following error in the backend logs:

2024-11-02T17:40:22.957+01:00 ERROR 70019 --- [nio-8080-exec-2] com.vaadin.hilla.EndpointInvoker         : Endpoint 'SignalsHandler' method 'update' execution failure

java.lang.reflect.InvocationTargetException: null
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
        at com.vaadin.hilla.EndpointInvoker.invokeVaadinEndpointMethod(EndpointInvoker.java:454) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.EndpointInvoker.invoke(EndpointInvoker.java:203) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.EndpointController.doServeEndpoint(EndpointController.java:251) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.EndpointController.serveEndpoint(EndpointController.java:199) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) ~[spring-web-6.1.13.jar:6.1.13]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) ~[spring-web-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.30.jar:6.0]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.13.jar:6.1.13]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.30.jar:6.0]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.13.jar:6.1.13]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.13.jar:6.1.13]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.13.jar:6.1.13]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.13.jar:6.1.13]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.13.jar:6.1.13]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.13.jar:6.1.13]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:384) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.30.jar:10.1.30]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: java.lang.IllegalArgumentException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.example.application.services.ChatService$ChatMessage["timestamp"])
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4624) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:4555) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.vaadin.hilla.signals.core.event.StateEvent.convertValue(StateEvent.java:108) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.signals.core.event.ListStateEvent.<init>(ListStateEvent.java:108) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.signals.ListSignal.processEvent(ListSignal.java:130) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.signals.Signal.submit(Signal.java:100) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.signals.ListSignal.submit(ListSignal.java:113) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at com.vaadin.hilla.signals.handler.SignalsHandler.update(SignalsHandler.java:97) ~[hilla-endpoint-24.6.0.alpha2.jar:na]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
        ... 53 common frames omitted
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.example.application.services.ChatService$ChatMessage["timestamp"])
        at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1887) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer.deserialize(UnsupportedTypeDeserializer.java:48) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:545) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:576) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:446) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1493) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185) ~[jackson-databind-2.17.2.jar:2.17.2]
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4619) ~[jackson-databind-2.17.2.jar:2.17.2]
        ... 61 common frames omitted

System Info

24.6.0.alpha2

@rbrki07 rbrki07 added bug Something isn't working hilla Issues related to Hilla labels Nov 4, 2024
@platosha
Copy link
Contributor

platosha commented Nov 5, 2024

We need to make sure that the correct Jackson mapper is used. One issue is that it's difficult to share the mapper for the signals constructor. Related issue: #2786, one approach to fix there relies on JSON mapper.

@platosha
Copy link
Contributor

platosha commented Nov 7, 2024

Let's expose a static method in the signals library that enables setting the Jackson ObjectMapper. Before the call, the built-in mapper is null, so any interaction with it will throw.

Hilla automaticalliy initializes signals using the same mapper with regular endpoint requests.

@taefi taefi self-assigned this Nov 8, 2024
taefi added a commit that referenced this issue Nov 8, 2024
platosha added a commit that referenced this issue Nov 26, 2024
* fix: add support for java8 date time in signals

Fixes #2891

* remove unnecessary qualifier

* introduce API to set ObjectMapper in Signal library

* add/improve javadoc

---------

Co-authored-by: Anton Platonov <[email protected]>
Co-authored-by: Luciano Vernaschi <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working hilla Issues related to Hilla Impact: High Severity: Major
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants