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

Default Mixin added by Jackson2ObjectMapperBuilder are missing required runtime hints #31606

Closed
denysandriyanov opened this issue Nov 14, 2023 · 11 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Milestone

Comments

@denysandriyanov
Copy link

denysandriyanov commented Nov 14, 2023

Affects: 3.1.5


Hello.

I am using
id 'org.springframework.boot' version '3.1.5'
and
id 'org.graalvm.buildtools.native' version '0.9.27'

When making a rest response with ProblemDetail that contain some property it is expected that this property will be rendered as top level key-value pair in the output JSON, and it does work so in JVM mode.
While in native image it will always return properties like a "properties" sub map.

Please see example below:

  1. In JVM - displayed as expected:
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "User not found",
    "instance": "/oauth2/management/users/d44cdf72-8417-5963-85ae-f1936288ae82/identities",
    "properties": {
        "userId": "d44cdf72-8417-5963-85ae-f1936288ae82"
    },
    "traceId": "8e18eedd0d9092798fbf8d4cb4c9ca00"
}
  1. In Native image the same entity - displayed as sub-map:
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "User not found",
    "instance": "/oauth2/management/users/d44cdf72-8417-5963-85ae-f1936288ae82/identities",
    "properties": {
        "properties": {
            "userId": "d44cdf72-8417-5963-85ae-f1936288ae82"
        },
        "traceId": "fc9ced45f90fa96fa6a81148bf06e5cc"
    }
}
@denysandriyanov denysandriyanov changed the title In native image ProblemDetail propertties are NOT rendered as top level key-value pairs in the output JSON, but always like a sub-map In native image ProblemDetail properties are NOT rendered as top level key-value pairs in the output JSON, but always like a sub-map Nov 14, 2023
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Nov 14, 2023
@snicoll snicoll changed the title In native image ProblemDetail properties are NOT rendered as top level key-value pairs in the output JSON, but always like a sub-map In native image ProblemDetail properties are not rendered as top level key-value pairs, but always like a sub-map Nov 14, 2023
@snicoll
Copy link
Member

snicoll commented Nov 14, 2023

@denysandriyanov thanks for the report, but unfortunately I can't reproduce this with:

problemDetail.setProperty("userId", UUID.randomUUID().toString());
problemDetail.setProperty("traceId", UUID.randomUUID().toString());
http :8080/spring-projects/spring-nope
HTTP/1.1 404
Connection: keep-alive
Content-Type: application/problem+json
Date: Tue, 14 Nov 2023 19:03:57 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked

{
    "detail": "No project found with slug 'spring-nope'",
    "instance": "/spring-projects/spring-nope",
    "properties": {
        "traceId": "f881377f-1df8-49e7-aca9-e7be6006f59f",
        "userId": "d0d71e6a-fb4b-4a66-a04f-46182dff5666"
    },
    "status": 404,
    "title": "Unknown Project",
    "type": "https://example.org/problems/unknown-project"
}

Rather than the output, we'd need a small sample that we can run ourselves to understand how your use case differs. You can attach a zip to this issue or push the code to a separate GitHub repository.

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Nov 14, 2023
@denysandriyanov
Copy link
Author

denysandriyanov commented Nov 14, 2023

to give a bit more context.

I am building ProblemDetail like this

    public ProblemDetail build(int statusCode, String title, String message, Object properties) {
        ProblemDetail problemDetail = ProblemDetail.forStatus(statusCode);
        problemDetail.setTitle(title);
        problemDetail.setDetail(message);

        if (properties != null) {
            problemDetail.setProperty(PROPERTIES, properties);
        }

        Optional<String> currentTraceId = ofNullable(tracer.spanBuilder(AUTH_CONSENT_SPAN).startSpan())
                .map(Span::getSpanContext)
                .map(SpanContext::getTraceId);

        currentTraceId.ifPresent(traceId -> problemDetail.setProperty(TRACE_ID, traceId));

        return problemDetail;
    }

and then i return it in ExceptionHandler

    @ExceptionHandler(AuthIdentityException.class)
    ProblemDetail authIdentityExceptionHandler(AuthIdentityException e) {
        log.error(e.getMessage(), e);
        return problemDetailBuilder.build(e.getStatusCode(), e.getTitle(), e.getMessage(), e.getProperties());
    }

The Exception is

public class AuthIdentityException extends RuntimeException {
    private final String title;
    private final int statusCode;
    private final transient Object properties;

    public AuthIdentityException(String title, int statusCode, String message, Object properties, Throwable cause) {
        super(message, cause);
        this.statusCode = statusCode;
        this.title = title;
        this.properties = properties;
    }
}

Where Object properties in my example is Map<String, Object> with key="userId", value="d44cdf72-8417-5963-85ae-f1936288ae82"

Hope that helps. Also, just make sure you use Native Image build

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 14, 2023
@snicoll
Copy link
Member

snicoll commented Nov 14, 2023

Hope that helps.

Not really. We'd need to move all that code in text into something that we can run and, as we do, we may miss a step that you're not showing. As I've asked before, can you please share a sample project we can run ourselves, not code snippet in text.

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Nov 14, 2023
@denysandriyanov

This comment was marked as resolved.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 14, 2023
@denysandriyanov

This comment was marked as resolved.

@denysandriyanov
Copy link
Author

denysandriyanov commented Nov 14, 2023

Please see attached reproducer.zip

As a side note, as a description for setProperty method in ProblemDetail there is

When Jackson JSON is present on the classpath, any properties set here are rendered as top level key-value pairs in the output JSON. Otherwise, they are rendered as a "properties" sub-map.

May be in native image it can not detect it?

@snicoll
Copy link
Member

snicoll commented Nov 15, 2023

@denysandriyanov unfortunately, the entire application is not what I have in mind. It doesn't even start for me:

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/snicoll/Downloads/reproducer/build.gradle' line: 31

* What went wrong:
A problem occurred evaluating root project 'auth-identity-service'.
> /Users/snicoll/Downloads/reproducer/VERSION (No such file or directory)

I am looking at the code and I don't even see what creates AuthIdentityException. Can you please simplify?

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Nov 15, 2023
@denysandriyanov
Copy link
Author

reproducer_v2.zip

Please see attached.

There is a DummyController.java class, when you call it it will throw

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 15, 2023
@snicoll
Copy link
Member

snicoll commented Nov 15, 2023

@denysandriyanov I am happy to try to help but please make sure this can be started? The application does not start (and doesn't require Java 20 either):

{"@timestamp":"2023-11-15T11:05:26.675Z","log.level":"ERROR","message":"\n\n***************************\nAPPLICATION FAILED TO START\n***************************\n\nDescription:\n\nFailed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.\n\nReason: Failed to determine a suitable driver class\n\n\nAction:\n\nConsider the following:\n\tIf you want an embedded database (H2, HSQL or Derby), please put it on the classpath.\n\tIf you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).\n","ecs.version": "1.2.0","service.name":"auth-identity-service","event.dataset":"auth-identity-service","process.thread.name":"main","log.logger":"org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter"}

also make sure ./gradlew nativeCompile actually works.

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Nov 15, 2023
@snicoll
Copy link
Member

snicoll commented Nov 15, 2023

Nevermind. I ripped out 90% of the config and the reproducer didn't even enabled problem details. I've managed to reproduce it in demo project by adding a nested map that I overlooked.

Courtesy of @rstoyanchev, it looks like ProblemDetailJacksonMixin does not work in Native.

@snicoll snicoll added type: bug A general bug and removed status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on labels Nov 15, 2023
@snicoll snicoll added this to the 6.0.14 milestone Nov 15, 2023
@snicoll snicoll added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Nov 15, 2023
@snicoll
Copy link
Member

snicoll commented Nov 15, 2023

The framework registers custom Mixin but does not provide the necessary hints as Spring Boot does for scanned mixins. In the meantime, you can workaround the problem by adding the following your your custom hints:

new BindingReflectionHintsRegistrar().registerReflectionHints(hints.reflection(),
		ProblemDetailJacksonMixin.class);

@snicoll snicoll changed the title In native image ProblemDetail properties are not rendered as top level key-value pairs, but always like a sub-map Default Mixin added by Jackson2ObjectMapperBuilder are missing required runtime hints Nov 15, 2023
@snicoll snicoll self-assigned this Nov 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants