Tuesday, March 29, 2011

Chapter 27: The sendError Method

In the chapter on introduction to Servlet Exception Handling, we have used the term, use the sendError method and in the previous chapter, we saw an example that actually used this method. This chapter is dedicated solely for the purpose of explaining the features of this method and how to use it effectively.

So, lets get started!!!

The sendError Method

The sendError method sends an error response to the client using the specified status (Remember the bunch of status codes we saw in one of the previous chapters?). Using this method clears the buffer. The server creates an HTML-formatted server error page. This page contains a default, or the message you provide, as an argument. It also sets the content type to “text/html”, even if you changed this, but leaves cookies and other headers unmodified.

Exam Trivia:
We cannot or rather should not send data to a client after the sendError() method is invoked. Once this method is invoked, all the buffered output will be discarded. If data has been written to the response buffer but not yet returned to the client (i.e., response not committed), the data is cleared and replaced with the data sent by the sendError() method. Data written to the response after the sendError() is called is ignored. However, if you write data to the response buffer and try to commit it after the sendError is invoked, an IllegalStateException will be thrown.
Be cautious and watch out for code that does exactly this in the exam question. It may look legit but it will throw an exception and you may overlook the exception choice in the answer.

The sendError method will set the appropriate headers and content body for an error message to return to the client. An optional String argument can be provided to the sendError method, which can be used in the content body of the error. Using this method will commit the response (if not already committed) and terminate it. The data stacked in the output stream to the client before calling sendError() method is ignored.

Internally, the servlet base classes prevent you from writing to the output stream after calling sendError(). In the write-to-stream methods there is a test for a previous error in the servlet that looks like this:

//suspended is a flag set once output is committed
if (suspended)//true if sendError has been called
throw new IOException
(sm.getString("responseBase.write.suspended"));

That is why you can't add to the outputstream after calling sendError().
The best way to understand the sendError() method is to look at it directly. Lets take a look at some sample code that will help us do just that.

Code: This is the actual code implementation of how Tomcat implements the sendError method.

/**
* Send an error response with the status and message.
*
* @param status HTTP status code to send
* @param message Corresponding message to send
*
* @exception IllegalStateException if this response has
* already been committed
* @exception IOException if an input/output error occurs
*/
public void sendError(int status, String message)
throws IOException
{
if (isCommitted())
throw new IllegalStateException
(sm.getString("httpResponseBase.sendError.ise"));

if (included)
return; //Ignore any call from an included servlet

setError();

// Record the status code and message.
this.status = status;
this.message = message;

// Clear any data content that has been buffered
resetBuffer();

// Cause the response to be finished
// (from the application perspective)
setSuspended(true);
}

If you observe the code above, the first thing it does is, throw an IllegalStateException if the response was already committed. Then it returns from the method if it is not invoked by the outermost servlet. The next thing it does is, call the setError method which updates an internal flag. Then it updates the status and message fields. These two fields are of most importance to us. Then it clears the buffer and finally suspends any further output stream access.

You can make better use of this method if you create a wrapper for it. You might want to do this if you care to send custom messages to the client rather than accept the default ones provided by the container.

Below is code for a sample wrapper that can be used for sending custom messages instead of using the default one.

Code:

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.PrintWriter;
import java.io.IOException;

public class ErrorHandlingServlet extends HttpServlet
{
//your own custom flag:
static final int SC_CUSTOM_ERROR_FIRST_NAME = 3229;

public void sendError(HttpServletResponse response,
int code)
throws ServletException, IOException
{
// Message sent by sendError().
String message = getErrorMessage(code);

if(message.equals("NONE"))
{
response.sendError(HttpServletResponse.SC_ FORBIDDEN);
} else
{
response.sendError(HttpServletResponse.SC_ FORBIDDEN,
message);
}

//update your own log
//logError(code, message);
}

public String getErrorMessage(int code)
{
String message = "NONE";
switch (code)
{
case HttpServletResponse.SC_OK:
return ("OK");
case HttpServletResponse.SC_ACCEPTED:
return ("Accepted");
case HttpServletResponse.SC_BAD_GATEWAY:
return ("Bad Gateway");
case HttpServletResponse.SC_BAD_REQUEST:
return ("Bad Request");
case HttpServletResponse.SC_CONFLICT:
return ("Conflict");

//There are a lot more codes.
//Just put a few for example

//first custom message; overrides the default message
case HttpServletResponse.SC_GONE:
return ("Sorry, this resource +
"is not available any more.");
case
HttpServletResponse.SC_HTTP_VERSION_NOT_ SUPPORTED:
return ("Hey! You are doing something Hinky" +
" and we do not support it.");
case HttpServletResponse.SC_INTERNAL_SERVER_ERROR:
return ("I Have no clue as to what happened, but it "
"was a bad server error");
case HttpServletResponse.SC_MOVED_PERMANENTLY:
return ("This Page has moved For good"
"permanently!");
// You can add a lot more custom error messages and provide
// handling for it.
default:
return ("NONE");
}
}
}


Previous Chapter: Chapter 26 - Returning an Error Code to the Client

Next Chapter: Chapter 27 - setStatus Method

No comments:

Post a Comment

© 2013 by www.inheritingjava.blogspot.com. All rights reserved. No part of this blog or its contents may be reproduced or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the Author.

ShareThis

Google+ Followers

Followers