Error with Jackson data-binding while playing around with full stack signals

Hey everyone :wave:,
I’m trying the new full stack signals with Vaadin 24.6.0.alpha1, but unfortunately I have some problems with LocalDateTime and Jackson. I want to implement a little chat app using the new ListSignal. This is my ChatService:

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;
    }
}

This is the corresponding ChatView in the frontend:

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

Every time I submit a message, I see the following error:

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

Although I think that it should not be necessary, I tried to follow the suggestion in the stacktrace and added the following dependency to the pom.xml of my project:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

In addition, I added an ObjectMapper, that registers Jackson’s JavaTimeModule:

package com.example.application;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

@Configuration
public class AppConfig {
    
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
            .registerModule(new JavaTimeModule());
    }
}

But unfortunately the error remains. It seems that the ObjectMapper is not invoked by the SignalsHandler or any of the subsequent methods.

Any ideas or help would be great. Thanks in advance!

Hilla is normally using his own ObjectMapper (hilla/packages/java/endpoint/src/main/java/com/vaadin/hilla/EndpointController.java at e51a04b5c8c8abba18e84e5f8433daf0ace96866 · vaadin/hilla · GitHub) but I’m not sure if the same one is also used for the signals or if another one is used

1 Like

Thank you for this hint @knoobie. I had a look at Type Conversion | Reference | Hilla | Vaadin Docs and according to the docs a string like 2019-01-01T12:34:56 should be converted to java.time.LocalDateTime. In my example, the string for the field timestamp has a value like 2024-11-03T13:33:44.272Z. Although there is a tiny difference in the format, I suppose it should work. It seems as the signals may not use Hilla’s default ObjectMapper?

IIRC state event class (don’t remember the exact name) internally instantiets a new object mapper.

Might deserve a GH ticket

1 Like

Thanks for your answer @marcoc_753. I’m not sure, if you are sure about the GitHub ticket ^^ Any other thoughts about this? Maybe @Leif or @Soroosh_Taefi? I had a look at GitHub - taefi/full-stack-signals-demo and couldn’t find any specific configuration regarding ObjectMapper, although the example in the repo does not contain a Instant, LocalDate or LocalDateTime field.

Thanks Marco! That was really the class hilla/packages/java/endpoint/src/main/java/com/vaadin/hilla/signals/core/event/StateEvent.java at e51a04b5c8c8abba18e84e5f8433daf0ace96866 · vaadin/hilla · GitHub

I believe this is bug and deserves a ticket. Generally speaking, it should use the default ObjectMapper.

1 Like
1 Like