Section 3.5. Servlet Responses


3.5. Servlet Responses

In order to do anything useful, a servlet must send a response to each request that is made to it. In the case of an HTTP servlet, the response can include three components: a status code, any number of HTTP headers, and a response body.

The ServletResponse and HttpServletResponse interfaces include all the methods needed to create and manipulate a servlet's output. We've already seen that you specify the MIME type for the data returned by a servlet using the setContentType( ) method of the response object passed into the servlet. With an HTTP servlet, the MIME type is generally text/html, although some servlets return binary data: a servlet that loads a GIF file from a database and sends it to the web browser should set a content type of image/gif, while a servlet that returns an Adobe Acrobat file should set it to application/pdf.

ServletResponse and HttpServletResponse each define two methods for producing output streams, getOutputStream( ) and getWriter( ). The former returns a ServletOutputStream, which can be used for textual or binary data. The latter returns a java.io.PrintWriter object, which is used only for textual output. The getWriter( ) method examines the content type to determine which charset to use, so setContentType( ) should be called before getWriter( ).

HttpServletResponse also includes a number of methods for handling HTTP responses . Most of these allow you to manipulate the HTTP header fields. For example, setHeader( ), setIntHeader( ), and setDateHeader( ) allow you to set the value of a specified HTTP header, while containsHeader( ) indicates whether a certain header has already been set. You can use either the setStatus( ) or sendError( ) method to specify the status code sent back to the client. HttpServletResponse provides a long list of integer constants that represent specific HTTP status codes (we'll see some of these shortly). You typically don't need to worry about setting a status code, as the default code is 200 ("OK"), meaning that the servlet sent a normal response. However, a servlet that is part of a complex application structure (such as serving XML content to an AJAX-based dynamic web interface) may need to use a variety of status codes. Finally, the sendRedirect( ) method allows you to issue a page redirect. Calling this method sets the Location header to the specified location and uses the appropriate status code for a redirect.

3.5.1. Request Dispatching

Request dispatching allows a servlet to delegate request handling to other components on the server. A servlet can either forward an entire request to another servlet or include bits of content from other components in its own output. In either case, this is done with a RequestDispatcher object that is obtained from the ServletContext via the getrequestDispatcher( ) method (also available via the HttpServletRequest object). When you call this method, you specify the path to the servlet to which you are dispatching the request. The path should be relative to the servlet context. If you want to dispatch a request to /servlet/TargetServlet within the /app context (which is accessed from a user's browser by /app/servlet/TargetServlet), request a dispatcher for /servlet/TargetServlet.

When you dispatch a request, you can set request attributes using the setAttribute( ) method of ServletRequest and read them using the getAttribute( ) method. A list of available attribute names is returned by getAttributeNames( ). Rather than taking only String objects (like parameters), an attribute may be any valid Java object.

RequestDispatcher provides two methods for dispatching requests: forward( ) and include( ). To forward an entire request to another servlet, use the forward( ) method. When using forward( ), the ServletRequest object is updated to include the new target URL. If a ServletOutputStream or PrintWriter has already been retrieved from the ServletResponse object, the forward( ) method throws an IllegalStateException.

The include( ) method of RequestDispatcher causes the content of the dispatchee to be included in the output of the main servlet, just like a server-side include. To see how this works, let's look at part of a servlet that does a keep-alive check on several different servers. The ServerMonitorServlet referenced in this example relies on the serverurl attribute to determine which server to display monitoring information for:

 out.println("Uptime for our servers"); // Get a RequestDispatcher to the ServerMonitorServlet RequestDispatcher d = getServletContext(  ).      getRequestDispatcher("/servlet/ServerMonitorServlet"); req.setAttribute("serverurl", new URL("http://www1.company.com")); d.include(req, res); req.setAttribute("serverurl", new URL("http://www2.company.com")); d.include(req, res);

Request dispatching is obviously different from issuing an HTTP redirect (via the sendRedirect( ) method of HttpResponse) since everything takes place within a single server request. Using a forward rather than a redirect gives a better user experience since content is displayed in the browser without two round-trips to the server. However, when you forward a request, the apparent URL (from the browser's perspective) remains the original request URL. This can break relative links and sometimes leads to confusing caching behavior on the browser. It is a good practice to avoid relative links wherever possibleencode the full server-side path, starting with the context name, instead.

3.5.2. Error Handling

Sometimes things just go wrong. When that happens, it's nice to have a clean way out. The Servlet API gives you two ways of to deal with errors: you can manually send an error message back to the client or you can throw a ServletException. The easiest way to handle an error is simply to write an error message to the servlet's output stream. This is the appropriate technique to use when the error is part of a servlet's normal operation, such as when a user forgets to fill in a required form field.

3.5.2.1. Status codes

When an error is a standard HTTP error, you should use the sendError( ) method of HttpServletResponse to tell the server to send a standard error status code. HttpServletResponse defines integer constants for all the major HTTP status codes . Table 3-2 lists the most common status codes. For example, if a servlet can't find a file the user has requested, it can send a 404 ("File Not Found") error and let the browser display it in its usual manner. In this case, we can replace the typical setContentType( ) and getWriter( ) calls with something like this:

 response.sendError(HttpServletResponse.SC_NOT_FOUND);

If you want to specify your own error message (in addition to the web server's default message for a particular error code), you can call sendError( ) with an extra String parameter:

 response.sendError(HttpServletResponse.SC_NOT_FOUND,                    "It's dark. I couldn't find anything.");

Table 3-2. Some common HTTP status codes

Constant

Code

Default message

Meaning

SC_OK

200

OK

The client's request succeeded, and the server's response contains the requested data. This is the default status code.

SC_NO_CONTENT

204

No Content

The request succeeded, but there is no new response body to return. A servlet may find this code useful when it accepts data from a form, but wants the browser view to stay at the form. It avoids the "Document contains no data" error message.

SC_MOVED_PERMANENTLY

301

Moved Permanently

The requested resource has permanently moved to a new location. Any future reference should use the new location given by the Location header. Most browsers automatically access the new location.

SC_MOVED_TEMPORARILY

302

Moved Temporarily

The requested resource has temporarily moved to another location, but future references should still use the original URL to access the resource. The temporary new location is given by the Location header. Most browsers automatically access the new location.

SC_UNAUTHORIZED

401

Unauthorized

The request lacked proper authorization. Used in conjunction with the WWW-Authenticate and Authorization headers.

SC_NOT_FOUND

404

Not Found

The requested resource is not available.

SC_INTERNAL_SERVER_ERROR

500

Internal Server Error

An error occurred inside the server that prevented it from fulfilling the request.

SC_NOT_IMPLEMENTED

501

Not Implemented

The server doesn't support the functionality needed to fulfill the request.

SC_SERVICE_UNAVAILABLE

503

Service Unavailable

The server is temporarily unavailable, but service should be restored in the future. If the server knows when it will be available again, a Retry-After header may also be supplied.


3.5.2.2. Servlet exceptions

The Servlet API includes two Exception subclasses, ServletException and its derivative, UnavailableException. A servlet throws a ServletException to indicate a general servlet problem. When a server catches this exception, it can handle the exception however it sees fit.

UnavailableException is a bit more useful, however. When a servlet throws this exception, it is notifying the server that it is unavailable to service requests. You can throw an UnavailableException when some factor beyond your servlet's control prevents it from dealing with requests. To throw an exception that indicates permanent unavailability, use something like this:

 throw new UnavailableException(this,             "This is why you can't use the servlet.");

UnavailableException has a second constructor to use if the servlet is going to be temporarily unavailable. With this constructor, you specify how many seconds the servlet is going to be unavailable, as follows:

 throw new UnavailableException(120, this, "Try back in two minutes");

One caveat: the servlet specification does not mandate that servers actually try again after the specified interval. If you choose to rely on this capability, you should test it first with the container you plan to deploy on.

3.5.2.3. A file-serving servlet

Example 3-2 demonstrates both of these error-handling techniques, along with another method for reading data from the server. FileServlet reads a pathname from a form parameter and returns the associated file. Note that this servlet is designed to return only HTML files. If the file can't be found, the servlet sends the browser a 404 error. If the servlet lacks sufficient access privileges to load the file, it sends an UnavailableException instead. Keep in mind that this servlet is a teaching exercise: you should not deploy it on your web server. (For one thing, any security exception renders the servlet permanently unavailable, and for another, it can serve files from the root of your hard drive.)

Example 3-2. Serving files
 import javax.servlet.*; import javax.servlet.http.*; import java.io.*;   public class FileServlet extends HttpServlet {     public void doGet(HttpServletRequest req, HttpServletResponse resp)     throws ServletException, IOException {       File r;     FileReader fr;     BufferedReader br;     try {       r = new File(req.getParameter("filename"));       fr = new FileReader(r);       br = new BufferedReader(fr);       if(!r.isFile(  )) {  // Must be a directory or something else         resp.sendError(resp.SC_NOT_FOUND);         return;       }     }     catch (FileNotFoundException e) {       resp.sendError(resp.SC_NOT_FOUND);       return;     }     catch (SecurityException se) { // Be unavailable permanently       throw(new UnavailableException(         "Servlet lacks appropriate privileges."));     }       resp.setContentType("text/html");     PrintWriter out = resp.getWriter(  );     String text;     while( (text = br.readLine(  )) != null)       out.println(text);       br.close(  );   } }



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net