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

JacksonJsonProvider has @Produces(MediaType.WILDCARD) and yet hasMatchingMediaType(MediaType.WILDCARD) return false #193

Closed
sdyura opened this issue Aug 21, 2024 · 1 comment
Labels

Comments

@sdyura
Copy link
Contributor

sdyura commented Aug 21, 2024

the class JacksonJaxbJsonProvider has @Produces(MediaType.WILDCARD)

but then because JacksonJaxbJsonProvider extends JacksonJsonProvider (that ALSO has @Produces(MediaType.WILDCARD))

and JacksonJsonProvider implements the method:

    @Override
    protected boolean hasMatchingMediaType(MediaType mediaType)
    {
        /* As suggested by Stephen D, there are 2 ways to check: either
         * being as inclusive as possible (if subtype is "json"), or
         * exclusive (major type "application", minor type "json").
         * Let's start with inclusive one, hard to know which major
         * types we should cover aside from "application".
         */
        if (mediaType != null) {
            // Ok: there are also "xxx+json" subtypes, which count as well
            String subtype = mediaType.getSubtype();

            // [Issue#14]: also allow 'application/javascript'
            return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json")
                   || "javascript".equals(subtype)
                   // apparently Microsoft once again has interesting alternative types?
                   || "x-javascript".equals(subtype)
                   || "x-json".equals(subtype) // [Issue#40]
                   ;
        }
        /* Not sure if this can happen; but it seems reasonable
         * that we can at least produce JSON without media type?
         */
        return true;
    }

and so when MediaType.WILDCARD, that literally comes from the annotation (picked up here) of the very same class gets passed back into its hasMatchingMediaType, it returns false, basically saying it cant be used to produce the very thing it says it can handle producing.

so it never gets picked up in situations when it SHOULD be getting picked up.

example situation is if we want to return a random class that have nothing setup for content-type, this can happen when we are using a ErrorHandler from a lib, and the lib does not know if the service will want json or xml, so it cant specify the content-type, but it should get encoded by the default the service has setup, and this case json. but instead of trying to encode this object in json, something JacksonJaxbJsonProvider can do, it falls through to the next encoder, text and that fails.

if we change@Produces(MediaType.WILDCARD) to @Produces(MediaType.APPLICATION_JSON) on JacksonJaxbJsonProvider then it works correctly. here is a PR demonstrating the fix: #194 (also FasterXML/jackson-jakarta-rs-providers#31)

to demonstrate the issue i have made 2 exactly the same apps, one uses jackson, the other one uses jsonb
the jsonb one works, the jackson one fails

jackson_vs_jsonb.zip
to run, open both projects in intellij and then run all the tests, in the jackson one there will be one test failure
this is because the jsonb one correctly has @Produces({"application/json", "text/json", "*/*"}) on its JsonBindingProvider

here is the stack trace of it calling hasMatchingMediaType with the MediaType specified in @Produces(MediaType.WILDCARD)

java.lang.Exception: Stack trace
	at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
	at org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider.hasMatchingMediaType(JacksonJsonProvider.java:189)
	at org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase.isWriteable(ProviderBase.java:551)
	at org.glassfish.jersey.message.internal.MessageBodyFactory.isWriteable(MessageBodyFactory.java:1146)
	at org.glassfish.jersey.message.WriterModel.isWriteable(WriterModel.java:63)
	at org.glassfish.jersey.internal.routing.ContentTypeDeterminer.determineResponseMediaType(ContentTypeDeterminer.java:94)
	at org.glassfish.jersey.server.internal.routing.AbstractMethodSelectingRouter.determineResponseMediaType(AbstractMethodSelectingRouter.java:495)
	at org.glassfish.jersey.server.internal.routing.AbstractMethodSelectingRouter$3.apply(AbstractMethodSelectingRouter.java:437)
	at org.glassfish.jersey.server.internal.routing.AbstractMethodSelectingRouter$3.apply(AbstractMethodSelectingRouter.java:428)
	at org.glassfish.jersey.process.internal.Stages$LinkedStage.apply(Stages.java:284)
	at org.glassfish.jersey.process.internal.Stages.process(Stages.java:147)
	at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:396)
	at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:450)
	at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:282)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:266)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:253)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:696)
	at io.helidon.microprofile.server.JaxRsService.doHandle(JaxRsService.java:228)
	at io.helidon.microprofile.server.JaxRsService.lambda$handle$2(JaxRsService.java:185)
	at io.helidon.common.context.Contexts.runInContext(Contexts.java:117)
	at io.helidon.microprofile.server.JaxRsService.handle(JaxRsService.java:185)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.doRoute(HttpRoutingImpl.java:165)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:124)
	at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:102)
	at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:76)
	at io.helidon.webserver.http.Filters$FilterChainImpl.proceed(Filters.java:121)
	at io.helidon.webserver.observe.metrics.MetricsFeature.lambda$configureVendorMetrics$2(MetricsFeature.java:90)
	at io.helidon.webserver.http.Filters$FilterChainImpl.proceed(Filters.java:119)
	at io.helidon.common.context.Contexts.runInContext(Contexts.java:117)
	at io.helidon.webserver.context.ContextRoutingFeature.filter(ContextRoutingFeature.java:50)
	at io.helidon.webserver.http.Filters$FilterChainImpl.proceed(Filters.java:119)
	at io.helidon.webserver.http.Filters.executeFilters(Filters.java:87)
	at io.helidon.webserver.http.Filters.lambda$filter$0(Filters.java:83)
	at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:76)
	at io.helidon.webserver.http.Filters.filter(Filters.java:83)
	at io.helidon.webserver.http.HttpRoutingImpl.route(HttpRoutingImpl.java:73)
	at io.helidon.webserver.http1.Http1Connection.route(Http1Connection.java:356)
	at io.helidon.webserver.http1.Http1Connection.handle(Http1Connection.java:194)
	at io.helidon.webserver.ConnectionHandler.run(ConnectionHandler.java:165)
	at io.helidon.common.task.InterruptableTask.call(InterruptableTask.java:47)
	at io.helidon.webserver.ThreadPerTaskExecutor$ThreadBoundFuture.run(ThreadPerTaskExecutor.java:239)
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:309)

resulting error in app:

Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=text/plain, type=class my.package.errors.ErrorResponse, genericType=class my.package.errors.ErrorResponse.
   at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:224) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:139) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:85) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:139) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:61) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:139) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1116) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:691) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:398) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:450) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:282) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.internal.Errors.process(Errors.java:292) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.internal.Errors.process(Errors.java:274) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.internal.Errors.process(Errors.java:244) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:266) ~[jersey-common-3.1.7.jar:?]
   at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:253) ~[jersey-server-3.1.7.jar:?]
   at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:696) ~[jersey-server-3.1.7.jar:?]
   at io.helidon.microprofile.server.JaxRsService.doHandle(JaxRsService.java:228) ~[helidon-microprofile-server-4.0.11.jar:4.0.11]
   at io.helidon.microprofile.server.JaxRsService.lambda$handle$2(JaxRsService.java:185) ~[helidon-microprofile-server-4.0.11.jar:4.0.11]
   at io.helidon.common.context.Contexts.runInContext(Contexts.java:117) ~[helidon-common-context-4.0.11.jar:4.0.11]
   at io.helidon.microprofile.server.JaxRsService.handle(JaxRsService.java:185) ~[helidon-microprofile-server-4.0.11.jar:4.0.11]
   at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.doRoute(HttpRoutingImpl.java:165) ~[helidon-webserver-4.0.11.jar:4.0.11]
   at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:124) ~[helidon-webserver-4.0.11.jar:4.0.11]
   at io.helidon.webserver.http.HttpRoutingImpl$RoutingExecutor.call(HttpRoutingImpl.java:102) ~[helidon-webserver-4.0.11.jar:4.0.11]
   at io.helidon.webserver.http.ErrorHandlers.runWithErrorHandling(ErrorHandlers.java:76) ~[helidon-webserver-4.0.11.jar:4.0.11]
   ... 17 more
@cowtowncoder cowtowncoder changed the title JacksonJaxbJsonProvider has @Produces(MediaType.WILDCARD) and yet hasMatchingMediaType(MediaType.WILDCARD) return false JacksonJsonProvider has @Produces(MediaType.WILDCARD) and yet hasMatchingMediaType(MediaType.WILDCARD) return false Aug 23, 2024
@cowtowncoder
Copy link
Member

Merged in 2.18 for 2.18.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants