NotAcceptableException (HTTP 406 Not Acceptable) but no apparent error from MessageBodyWriter

836 Views Asked by At

In a webapp implemented as a Jetty container, we have a custom javax.ws.rs.ext.MessageBodyWriter<T> annotated with

@Singleton
@Provider
@Produces( "application/rss+xml" )

We also have a resource which works just fine. The get() method is annotated with

@Produces( "application/vnd.api+json" )

Visiting that endpoint returns the expected json response.

Adding .rss to the endpoint causes a 406 response to be returned.

What could be the reason it is not finding the MessageBodyWriter for returning the RSS response?

The full stacktrace is:

javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable
    at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.getMethodRouter(MethodSelectingRouter.java:472)
    at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.access$000(MethodSelectingRouter.java:73)
    at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter$4.apply(MethodSelectingRouter.java:674)
    at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.apply(MethodSelectingRouter.java:305)
    at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:86)
    at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
    at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:89)
    at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:69)
    at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:38)
    at org.glassfish.jersey.process.internal.Stages.process(Stages.java:173)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:247)
    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:265)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)
    at <our package>.Jetty94HttpContainer.handle(Jetty94HttpContainer.java:167) // Jetty94HttpContainer extends AbstractHandler implements Container
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at com.codahale.metrics.jetty9.InstrumentedHandler.handle(InstrumentedHandler.java:284)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
    at org.eclipse.jetty.server.Server.handle(Server.java:516)
    at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
    at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
    at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
    at java.base/java.lang.Thread.run(Thread.java:829)
2

There are 2 best solutions below

0
Lety On BEST ANSWER

The resource methods annotated with a request method should declare the supported request media types equals to the entity providers that supply mapping services between representations and their associated Java types.

Adding:

@Produces( "application/vnd.api+json", "application/rss+xml" )

to the get() method should do the trick.

0
halfer On

(Posted an answer from the question author in order to move it to the answer space).

Thanks to Lety I was able to find the problem. It's one of those which are super-obvious once you realise it, but which no amount of staring at the code alone would reveal!

This answer also helped

If you want to switch a resource output between different output types depending on the Accept request header, the resource method needs @Produces to cover all the desired output types, as well as having a MessageBodyWriter configured for that output.

In other words, I changed the get() method of the resource to read ...

@Produces( "application/vnd.api+json", "application/rss+xml" )

... and this is what made it work.