GeoLatte / geolatte-geom

A geometry model that conforms to the OGC Simple Features for SQL specification.
Other
132 stars 63 forks source link

Geolatte not registering Jackson Module with Spring Boot #161

Closed bigalnz closed 4 months ago

bigalnz commented 10 months ago

I know this may be read as being a Spring Boot issue, but I am not sure as I have read many documents on how to load a Jackson module into Spring Boot and none of them so far seem to work. All my error messages reference Geolatte in the stack trace, so I wonder if the issue lies there.

Here is my configuration: ENTITY

@Value
@Data
public class CheckDto implements Serializable {

    Long id;
    @JsonSerialize(using = GeometrySerializer.class)
    @JsonDeserialize(using = GeometryDeserializer.class)
    Point<G2D> location;
    // getters setters etc
}

Register the Bean for GeolatteGeomModule: Note this does get loaded as verified by breatpoints inside GeolatteGeomModule.

@Configuration
@EnableEntityViews("com.nz.kiwi.model")
@ComponentScan("com.nz.kiwi")
public class AppConfig {
@Bean
public ObjectMapper mapper() {
    CoordinateReferenceSystem<G2D> crs = WGS84;
    ObjectMapper gMapper = new ObjectMapper();
    gMapper.registerModule(new GeolatteGeomModule(crs));
    return gMapper;
}

Stacktrace:

Mon Sep 04 18:45:46 NZST 2023
There was an unexpected error (type=Internal Server Error, status=500).
No converter for [class com.nz.kiwi.view.HealthCheckDto] with preset Content-Type 'application/json'
org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.nz.kiwi.view.HealthCheckDto] with preset Content-Type 'application/json'
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:319)
    at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:245)
    at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:136)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:833)

Now if I change the entity to:

  @Value
  @Data
  public class CheckDto implements Serializable {

    Long id;
    //@JsonSerialize(using = GeometrySerializer.class)
    //@JsonDeserialize(using = GeometryDeserializer.class)
    Point<G2D> location;
    // getters setters etc
}

And the controller to load the GeolatteGeomModule manually I get:


    public ObjectMapper mapper() {
        CoordinateReferenceSystem<G2D> crs = WGS84;
        ObjectMapper gMapper = new ObjectMapper();
        gMapper.registerModule(new GeolatteGeomModule(crs));
        gMapper.registerModule(new JavaTimeModule());
        return gMapper;
    }

    @GetMapping(value = "/custom5/{id}", produces = "application/json")
    public ResponseEntity<?> BirdSummaryDTOCustom5(@PathVariable Long id) throws JsonProcessingException {
        HealthCheckDto hcDto = customBirdRepository.customQuery5(id);
        ObjectMapper objectMapper = mapper();
        String json = objectMapper.writeValueAsString(hcDto);
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .header("Custom-Header", "birdApp")
                .body(json);
    }
Which returns :
```
    {
        "id": 5,
        "catchDateTime": "2023-06-05 11:18",
        "releaseDateTime": "2023-06-05 11:45",
        "location": {
            "type": "Point",
            "crs": {
                "type": "name",
                "properties": {
                    "name": "EPSG:4326"
                }
            },
        "coordinates": [
            173.5,
            -35.5
        ]
    }
}

I have tried so many things - and followed so many SO answers, I just dont know where the issue lies.

In a working example with spring do I need:
@JsonSerialize(using = GeometrySerializer.class)
@JsonDeserialize(using = GeometryDeserializer.class)


Is my syntax for instantiating GeolatteGeomModule correct? Some of my error messages on stack trace complained about a missing Settings in constructor?
maesenka commented 10 months ago

You write "All my error messages reference Geolatte in the stack trace" but the stack trace you posted doesn't reference geolatte at all.

bigalnz commented 10 months ago

If I try this :

    @Bean
    Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        CoordinateReferenceSystem<G2D> crs = WGS84;
        return builder -> builder
                .deserializers(new GeometryDeserializer(WGS84, settings));
    }

Which is one way to load the GeolatteGeomModule the second param in GeometryDeserializer is a Settings but I dont seem to be able to import the settings class?

There is a settings class in the GeolatteGeomJson dependency - but I don't seem to be able to import it to provide the second param.

bigalnz commented 10 months ago

You write "All my error messages reference Geolatte in the stack trace" but the stack trace you posted doesn't reference geolatte at all.

Yes I accept that - but the last 2 days of trying to get this working, in all the different attempts - many of the messages have references GeoLatteGeomModule.

Once I get this going I would like to write a simple use case for Spring Boot for the docs.

bigalnz commented 10 months ago

Also articles like here : https://www.baeldung.com/spring-boot-customize-jackson-objectmapper

Reference injecting modules like this:

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
      .serializationInclusion(JsonInclude.Include.NON_NULL);
    return new MappingJackson2HttpMessageConverter(builder.build());
}

But in this context what would the corresponding value for LOCAL_DATETIME_SERIALIZER value be for GeolatteGeomModule?

bigalnz commented 10 months ago

Another message I was getting earlier:

UnsatisfiedDependencyException: Error creating bean with name 'org.geolatte.geom.json.GeometrySerializer': Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'org.geolatte.geom.json.Settings' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

How do I instantiate settings so I can pass it in as a constructor parameter?