tags:

views:

43

answers:

5

I have a simple (Servlet, JSP, and JSTL) web app whose main functionality is displaying images retrieved from a back-end server. The controller servlet forwards the user to a JSP that in turn uses another Servlet to display the resulting image on that same JSP. Further down, the JSP has a line similar to:

<a href="<c:out value='${imageURL}'/>"><img src="<c:out value='${imageURL}'/>" alt="image view" border="1"></a>

which invokes a GET request on the image-generating servlet causing it to generate the image.

My question is: how do I handle Exceptions thrown by this image-generating servlet?

I already have an error page defined (in web.xml) to handle ServletException within my web app but this doesn't work for this image-generating Servlet, and results in the following errors showing up in my Tomcat server logs:

SEVERE: Exception Processing ErrorPage[exceptionType=javax.servlet.ServletException, location=/WEB-INF/ExceptionPage.jsp]
java.lang.IllegalStateException: Cannot reset buffer after response has been committed

What's my recourse in this situation?

I'd like to be able to handle Exceptions thrown from this image-generating Servlet, and display some error on the main UI or forward the user to another error page.

A: 

First, identify why the Illegal State exception is being thrown. Rather than dealing with a thrown exception, you probably just want to fix your code so that it goes away.

Zak
+1  A: 

According to the servlet API no servlet should call the getWriter() and getOutputStream() on the same response object as it causes the IllegalStateException. Usually this is the source of this exception. If you're outputing binary data like and image file you should use getOutputStream().

Andrius
Calling both `getWriter()` and `getOutputStream()` indeed also produces an `IllegalStateException`, but with an entirely different message like `getWriter has already been called for this response`. This is thus not the problem here.
BalusC
My image generating Servlet is only calling getOutputStream(). So this is not why I am getting the IllegalStateException.
ssahmed555
+2  A: 

You can't change the response to redirect to an error page while sending the response. It's already too late to change the entire response then. You can't ask those already sent bytes back from the client side. That's what the IllegalStateException stands for here. It's a point of no return.

Best what you can do is to just log the exception, or to rewrite the code so that it doesn't write any bit to the response (also not setting the response headers) while the business logic hasn't finished its task yet. Once you've determined that the business logic didn't throw any exception, then start writing (and thus indirectly also committing) the response. If the business logic has thrown an exception while the response isn't touched yet, then you can just safely throw it through so that it ends up in an error page. Although in case of an image servlet, you may also want to stream some standard 404.gif to the response instead. This because you can't display another HTML (error) page in an <img> element and you also can't change the URL of the parent JSP/HTML page as well since that concerns a different request.

BalusC
Thanks for the explanation - this makes sense now! I'd love to be able to stream a canned 404.gif image; problem is that my image Servlet can return either a GIF, PNG, TIFF, or PDF - so that would quite a few canned images for just one type of exception - the business logic can thrown any one of three exceptions when generating an image.I like your idea of not writing anything to the response until I am 100% sure that the business logic didn't throw an exception.
ssahmed555
Just set the `Content-Type` header accordingly based on the image extension. You can either fix it for the `404.gif` or just use `getServletContext().getMimeType(filename)` for this. You can get some ideas out of [this article](http://balusc.blogspot.com/2007/04/imageservlet.html).
BalusC
A: 

Looks like the problem you have is within your ExceptionPage.jsp, not your servlet code.

And this

java.lang.IllegalStateException: Cannot reset buffer after response has been committed

Means that you've already have tried to send a response. Probably you've opened an output stream directly and wrote some data to it. Once you've done it you cannot try to set headers and such on a response ( they are already on their way to the client ).

You need to do a better state management. Best way to do this is to separate request preprocessing from the response generation. Once you are writing response, you can only do or die. For this, check that you are not catching IOExceptions from the response output, wrapping them to ServletException and redirect them to your error page. You really cannot handle them in the context of the current request.

Alexander Pogrebnyak
Right you are - I've already set the content type and opened up the OutputStream by the time the exception happens.You're right: I need to rework the image-generating servlet.
ssahmed555
A: 

You should catch the exception and forward the request using RequestDispatcher to the required page:

  public void doGet(HttpServletRequest request, 
                HttpServletResponse response) 
throws ServletException, IOException  {

// The following piece of code results in NumberFormatException which will
// be detected by the container. The RequestDispatcher object will forward
// the same request to the other resource, here the file: forwardedJSP.jsp 
try  {
  int test = Integer.parseInt("abc");
} catch (NumberFormatException nfe) {
    RequestDispatcher rd = request.getRequestDispatcher("/forwardedJSP.jsp"); 
    rd.forward(request, response); 
}}
Snehal
He already has an `error-page` definied in `web.xml` which basically does the same as this but is a better practice. The whole problem is only that he was unable to display this error page.
BalusC
Thanks for clarifying, your answer makes much sense now.
Snehal