Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jersey throws NPE for each call after using ContainerRequestContext.abortWith()

I have a RESTful endpoint which I secured with a simple authorization check via a custom ContainerRequestFilter. The filter checks if all the information contained in the HTTP session is correct and if not it executes this:

requestContext.abortWith(Response.status(Response.Status.FORBIDDEN)
.entity("Forbidden").build());

This is all fine and dandy. The strange thing is that when I make the same GET request again the Jersey server reports an NPE and does not return anything.

The NPE stacktrace:

Jul 20, 2016 5:27:53 PM org.glassfish.jersey.server.ServerRuntime$Responder writeResponse
SEVERE: An I/O error has occurred while writing a response message entity to the container output stream.
java.lang.IllegalStateException: The output stream has already been closed.
    at org.glassfish.jersey.message.internal.CommittingOutputStream.setStreamProvider(CommittingOutputStream.java:147)
    at org.glassfish.jersey.message.internal.OutboundMessageContext.setStreamProvider(OutboundMessageContext.java:803)
    at org.glassfish.jersey.server.ContainerResponse.setStreamProvider(ContainerResponse.java:372)
    at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:694)
    at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:444)
    at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:434)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:329)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384)
    at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
    at java.lang.Thread.run(Thread.java:745)

What is happening? I dont want to close the output stream. I only need a method to return a http return code + message to the requester.

like image 709
Alex Avatar asked Jul 20 '16 15:07

Alex


3 Answers

In case anyone would ever face the same issue I had here's the answer: you cannot re-use Responses! The output stream writer's instance is bound to the Request and as soon as you use it in abortWith() the stream will be committed forever. As such it can no longer be used to send responses.

You need to initialize a new Request from within the filter() method.

in simple terms, do not use such constructs:

 private static final Response ACCESS_FORBIDDEN = Response.status(Response.Status.FORBIDDEN)
            .entity("Access blocked for all users !!").build();

btw, I've picken up this broken piece of code here: http://howtodoinjava.com/jersey/jersey-rest-security/ So be wary when implementing this RequestFilter.

like image 181
Alex Avatar answered Oct 23 '22 12:10

Alex


As mentioned, the problem is on the static ACCESS_DENIED Response that were already set.

private static final Response ACCESS_DENIED = Response.status(Response.Status.UNAUTHORIZED).entity("Accesso não Autorizado").build();

So replace:

requestContext.abortWith(ACCESS_DENIED)

by

requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Accesso não Autorizado").build());
like image 31
Ricardo Gellman Avatar answered Oct 23 '22 12:10

Ricardo Gellman


You need not use requestContext.abortWith() inside the filter method. Instead, do the following.

    String authCredentials = containerRequest.getHeaderString(HttpHeaders.AUTHORIZATION);

The header sent from your REST client [browser/postman/SoapUI/jersey client program] will be of the format "Basic encodedString". The encodedString can be easily decoded using java.util.Base64. After decoding it at server end, the string will be of format username:password. Use String.split(":");to separate user name and password. Proceed to validation. If validation turns false, throw WebApplicationException from the ContainerRequestFilter's filter method that you are overriding here.

like image 1
Kavitha Karunakaran Avatar answered Oct 23 '22 13:10

Kavitha Karunakaran