View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package javax.servlet.http;
21  
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.io.OutputStreamWriter;
25  import java.io.UnsupportedEncodingException;
26  import java.lang.reflect.Method;
27  import java.text.MessageFormat;
28  import java.util.Enumeration;
29  import java.util.Locale;
30  import java.util.ResourceBundle;
31  
32  import javax.servlet.GenericServlet;
33  import javax.servlet.ServletException;
34  import javax.servlet.ServletOutputStream;
35  import javax.servlet.ServletRequest;
36  import javax.servlet.ServletResponse;
37  
38  
39  /**
40   *
41   * Provides an abstract class to be subclassed to create
42   * an HTTP servlet suitable for a Web site. A subclass of
43   * <code>HttpServlet</code> must override at least 
44   * one method, usually one of these:
45   *
46   * <ul>
47   * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
48   * <li> <code>doPost</code>, for HTTP POST requests
49   * <li> <code>doPut</code>, for HTTP PUT requests
50   * <li> <code>doDelete</code>, for HTTP DELETE requests
51   * <li> <code>init</code> and <code>destroy</code>, 
52   * to manage resources that are held for the life of the servlet
53   * <li> <code>getServletInfo</code>, which the servlet uses to
54   * provide information about itself 
55   * </ul>
56   *
57   * <p>There's almost no reason to override the <code>service</code>
58   * method. <code>service</code> handles standard HTTP
59   * requests by dispatching them to the handler methods
60   * for each HTTP request type (the <code>do</code><i>XXX</i>
61   * methods listed above).
62   *
63   * <p>Likewise, there's almost no reason to override the 
64   * <code>doOptions</code> and <code>doTrace</code> methods.
65   * 
66   * <p>Servlets typically run on multithreaded servers,
67   * so be aware that a servlet must handle concurrent
68   * requests and be careful to synchronize access to shared resources.
69   * Shared resources include in-memory data such as
70   * instance or class variables and external objects
71   * such as files, database connections, and network 
72   * connections.
73   * See the
74   * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
75   * Java Tutorial on Multithreaded Programming</a> for more
76   * information on handling multiple threads in a Java program.
77   *
78   * @author	Various
79   * @version	$Version$
80   *
81   */
82  
83  
84  
85  public abstract class HttpServlet extends GenericServlet
86      implements java.io.Serializable
87  {
88      private static final String METHOD_DELETE = "DELETE";
89      private static final String METHOD_HEAD = "HEAD";
90      private static final String METHOD_GET = "GET";
91      private static final String METHOD_OPTIONS = "OPTIONS";
92      private static final String METHOD_POST = "POST";
93      private static final String METHOD_PUT = "PUT";
94      private static final String METHOD_TRACE = "TRACE";
95  
96      private static final String HEADER_IFMODSINCE = "If-Modified-Since";
97      private static final String HEADER_LASTMOD = "Last-Modified";
98      
99      private static final String LSTRING_FILE =
100 	"javax.servlet.http.LocalStrings";
101     private static ResourceBundle lStrings =
102 	ResourceBundle.getBundle(LSTRING_FILE);
103    
104    
105    
106     
107     /**
108      * Does nothing, because this is an abstract class.
109      * 
110      */
111 
112     public HttpServlet() { }
113     
114     
115 
116     /**
117      *
118      * Called by the server (via the <code>service</code> method) to
119      * allow a servlet to handle a GET request. 
120      *
121      * <p>Overriding this method to support a GET request also
122      * automatically supports an HTTP HEAD request. A HEAD
123      * request is a GET request that returns no body in the
124      * response, only the request header fields.
125      *
126      * <p>When overriding this method, read the request data,
127      * write the response headers, get the response's writer or 
128      * output stream object, and finally, write the response data.
129      * It's best to include content type and encoding. When using
130      * a <code>PrintWriter</code> object to return the response,
131      * set the content type before accessing the
132      * <code>PrintWriter</code> object.
133      *
134      * <p>The servlet container must write the headers before
135      * committing the response, because in HTTP the headers must be sent
136      * before the response body.
137      *
138      * <p>Where possible, set the Content-Length header (with the
139      * {@link javax.servlet.ServletResponse#setContentLength} method),
140      * to allow the servlet container to use a persistent connection 
141      * to return its response to the client, improving performance.
142      * The content length is automatically set if the entire response fits
143      * inside the response buffer.
144      *
145      * <p>When using HTTP 1.1 chunked encoding (which means that the response
146      * has a Transfer-Encoding header), do not set the Content-Length header.
147      *
148      * <p>The GET method should be safe, that is, without
149      * any side effects for which users are held responsible.
150      * For example, most form queries have no side effects.
151      * If a client request is intended to change stored data,
152      * the request should use some other HTTP method.
153      *
154      * <p>The GET method should also be idempotent, meaning
155      * that it can be safely repeated. Sometimes making a
156      * method safe also makes it idempotent. For example, 
157      * repeating queries is both safe and idempotent, but
158      * buying a product online or modifying data is neither
159      * safe nor idempotent. 
160      *
161      * <p>If the request is incorrectly formatted, <code>doGet</code>
162      * returns an HTTP "Bad Request" message.
163      * 
164      *
165      * @param req	an {@link HttpServletRequest} object that
166      *			contains the request the client has made
167      *			of the servlet
168      *
169      * @param resp	an {@link HttpServletResponse} object that
170      *			contains the response the servlet sends
171      *			to the client
172      * 
173      * @exception IOException	if an input or output error is 
174      *				detected when the servlet handles
175      *				the GET request
176      *
177      * @exception ServletException	if the request for the GET
178      *					could not be handled
179      *
180      * 
181      * @see javax.servlet.ServletResponse#setContentType
182      *
183      */
184 
185     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
186 	throws ServletException, IOException
187     {
188 	String protocol = req.getProtocol();
189 	String msg = lStrings.getString("http.method_get_not_supported");
190 	if (protocol.endsWith("1.1")) {
191 	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
192 	} else {
193 	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
194 	}
195     }
196 
197 
198 
199 
200 
201     /**
202      *
203      * Returns the time the <code>HttpServletRequest</code>
204      * object was last modified,
205      * in milliseconds since midnight January 1, 1970 GMT.
206      * If the time is unknown, this method returns a negative
207      * number (the default).
208      *
209      * <p>Servlets that support HTTP GET requests and can quickly determine
210      * their last modification time should override this method.
211      * This makes browser and proxy caches work more effectively,
212      * reducing the load on server and network resources.
213      *
214      *
215      * @param req	the <code>HttpServletRequest</code> 
216      *			object that is sent to the servlet
217      *
218      * @return		a <code>long</code> integer specifying
219      *			the time the <code>HttpServletRequest</code>
220      *			object was last modified, in milliseconds
221      *			since midnight, January 1, 1970 GMT, or
222      *			-1 if the time is not known
223      *
224      */
225 
226     protected long getLastModified(HttpServletRequest req) {
227 	return -1;
228     }
229 
230 
231 
232 
233     /**
234      * 
235      *
236      * <p>Receives an HTTP HEAD request from the protected
237      * <code>service</code> method and handles the
238      * request.
239      * The client sends a HEAD request when it wants
240      * to see only the headers of a response, such as
241      * Content-Type or Content-Length. The HTTP HEAD
242      * method counts the output bytes in the response
243      * to set the Content-Length header accurately.
244      *
245      * <p>If you override this method, you can avoid computing
246      * the response body and just set the response headers
247      * directly to improve performance. Make sure that the
248      * <code>doHead</code> method you write is both safe
249      * and idempotent (that is, protects itself from being
250      * called multiple times for one HTTP HEAD request).
251      *
252      * <p>If the HTTP HEAD request is incorrectly formatted,
253      * <code>doHead</code> returns an HTTP "Bad Request"
254      * message.
255      *
256      *
257      * @param req	the request object that is passed
258      *			to the servlet
259      *			
260      * @param resp	the response object that the servlet
261      *			uses to return the headers to the clien
262      *
263      * @exception IOException		if an input or output error occurs
264      *
265      * @exception ServletException	if the request for the HEAD
266      *					could not be handled
267      */
268 
269     protected void doHead(HttpServletRequest req, HttpServletResponse resp)
270 	throws ServletException, IOException
271     {
272 	NoBodyResponse response = new NoBodyResponse(resp);
273 	
274 	doGet(req, response);
275 	response.setContentLength();
276     }
277     
278 
279 
280 
281 
282     /**
283      *
284      * Called by the server (via the <code>service</code> method)
285      * to allow a servlet to handle a POST request.
286      *
287      * The HTTP POST method allows the client to send
288      * data of unlimited length to the Web server a single time
289      * and is useful when posting information such as
290      * credit card numbers.
291      *
292      * <p>When overriding this method, read the request data,
293      * write the response headers, get the response's writer or output
294      * stream object, and finally, write the response data. It's best 
295      * to include content type and encoding. When using a
296      * <code>PrintWriter</code> object to return the response, set the 
297      * content type before accessing the <code>PrintWriter</code> object. 
298      *
299      * <p>The servlet container must write the headers before committing the
300      * response, because in HTTP the headers must be sent before the 
301      * response body.
302      *
303      * <p>Where possible, set the Content-Length header (with the
304      * {@link javax.servlet.ServletResponse#setContentLength} method),
305      * to allow the servlet container to use a persistent connection 
306      * to return its response to the client, improving performance.
307      * The content length is automatically set if the entire response fits
308      * inside the response buffer.  
309      *
310      * <p>When using HTTP 1.1 chunked encoding (which means that the response
311      * has a Transfer-Encoding header), do not set the Content-Length header. 
312      *
313      * <p>This method does not need to be either safe or idempotent.
314      * Operations requested through POST can have side effects for
315      * which the user can be held accountable, for example, 
316      * updating stored data or buying items online.
317      *
318      * <p>If the HTTP POST request is incorrectly formatted,
319      * <code>doPost</code> returns an HTTP "Bad Request" message.
320      *
321      *
322      * @param req	an {@link HttpServletRequest} object that
323      *			contains the request the client has made
324      *			of the servlet
325      *
326      * @param resp	an {@link HttpServletResponse} object that
327      *			contains the response the servlet sends
328      *			to the client
329      * 
330      * @exception IOException	if an input or output error is 
331      *				detected when the servlet handles
332      *				the request
333      *
334      * @exception ServletException	if the request for the POST
335      *					could not be handled
336      *
337      *
338      * @see javax.servlet.ServletOutputStream
339      * @see javax.servlet.ServletResponse#setContentType
340      *
341      *
342      */
343 
344     protected void doPost(HttpServletRequest req, HttpServletResponse resp)
345 	throws ServletException, IOException
346     {
347 	String protocol = req.getProtocol();
348 	String msg = lStrings.getString("http.method_post_not_supported");
349 	if (protocol.endsWith("1.1")) {
350 	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
351 	} else {
352 	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
353 	}
354     }
355 
356 
357 
358 
359     /**
360      * Called by the server (via the <code>service</code> method)
361      * to allow a servlet to handle a PUT request.
362      *
363      * The PUT operation allows a client to 
364      * place a file on the server and is similar to 
365      * sending a file by FTP.
366      *
367      * <p>When overriding this method, leave intact
368      * any content headers sent with the request (including
369      * Content-Length, Content-Type, Content-Transfer-Encoding,
370      * Content-Encoding, Content-Base, Content-Language, Content-Location,
371      * Content-MD5, and Content-Range). If your method cannot
372      * handle a content header, it must issue an error message
373      * (HTTP 501 - Not Implemented) and discard the request.
374      * For more information on HTTP 1.1, see RFC 2616
375      * <a href="http://www.ietf.org/rfc/rfc2616.txt"></a>.
376      *
377      * <p>This method does not need to be either safe or idempotent.
378      * Operations that <code>doPut</code> performs can have side
379      * effects for which the user can be held accountable. When using
380      * this method, it may be useful to save a copy of the
381      * affected URL in temporary storage.
382      *
383      * <p>If the HTTP PUT request is incorrectly formatted,
384      * <code>doPut</code> returns an HTTP "Bad Request" message.
385      *
386      *
387      * @param req	the {@link HttpServletRequest} object that
388      *			contains the request the client made of
389      *			the servlet
390      *
391      * @param resp	the {@link HttpServletResponse} object that
392      *			contains the response the servlet returns
393      *			to the client
394      *
395      * @exception IOException	if an input or output error occurs
396      *				while the servlet is handling the
397      *				PUT request
398      *
399      * @exception ServletException	if the request for the PUT
400      *					cannot be handled
401      *
402      */
403   
404     protected void doPut(HttpServletRequest req, HttpServletResponse resp)
405 	throws ServletException, IOException
406     {
407 	String protocol = req.getProtocol();
408 	String msg = lStrings.getString("http.method_put_not_supported");
409 	if (protocol.endsWith("1.1")) {
410 	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
411 	} else {
412 	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
413 	}
414     }
415 
416 
417 
418 
419     /**
420      * 
421      * Called by the server (via the <code>service</code> method)
422      * to allow a servlet to handle a DELETE request.
423      *
424      * The DELETE operation allows a client to remove a document
425      * or Web page from the server.
426      * 
427      * <p>This method does not need to be either safe
428      * or idempotent. Operations requested through
429      * DELETE can have side effects for which users
430      * can be held accountable. When using
431      * this method, it may be useful to save a copy of the
432      * affected URL in temporary storage.
433      *
434      * <p>If the HTTP DELETE request is incorrectly formatted,
435      * <code>doDelete</code> returns an HTTP "Bad Request"
436      * message.
437      *
438      *
439      * @param req	the {@link HttpServletRequest} object that
440      *			contains the request the client made of
441      *			the servlet
442      *
443      *
444      * @param resp	the {@link HttpServletResponse} object that
445      *			contains the response the servlet returns
446      *			to the client				
447      *
448      *
449      * @exception IOException	if an input or output error occurs
450      *				while the servlet is handling the
451      *				DELETE request
452      *
453      * @exception ServletException	if the request for the
454      *					DELETE cannot be handled
455      *
456      */
457      
458     protected void doDelete(HttpServletRequest req,
459 			    HttpServletResponse resp)
460 	throws ServletException, IOException
461     {
462 	String protocol = req.getProtocol();
463 	String msg = lStrings.getString("http.method_delete_not_supported");
464 	if (protocol.endsWith("1.1")) {
465 	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
466 	} else {
467 	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
468 	}
469     }
470     
471 
472     private static Method[] getAllDeclaredMethods(Class c) {
473 
474         if (c.equals(javax.servlet.http.HttpServlet.class)) {
475             return null;
476         }
477 
478         Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
479         Method[] thisMethods = c.getDeclaredMethods();
480 	
481         if ((parentMethods != null) && (parentMethods.length > 0)) {
482             Method[] allMethods =
483                 new Method[parentMethods.length + thisMethods.length];
484 	    System.arraycopy(parentMethods, 0, allMethods, 0,
485                              parentMethods.length);
486 	    System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
487                              thisMethods.length);
488 
489 	    thisMethods = allMethods;
490 	}
491 
492 	return thisMethods;
493     }
494 
495 
496     /**
497      * Called by the server (via the <code>service</code> method)
498      * to allow a servlet to handle a OPTIONS request.
499      *
500      * The OPTIONS request determines which HTTP methods 
501      * the server supports and
502      * returns an appropriate header. For example, if a servlet
503      * overrides <code>doGet</code>, this method returns the
504      * following header:
505      *
506      * <p><code>Allow: GET, HEAD, TRACE, OPTIONS</code>
507      *
508      * <p>There's no need to override this method unless the
509      * servlet implements new HTTP methods, beyond those 
510      * implemented by HTTP 1.1.
511      *
512      * @param req	the {@link HttpServletRequest} object that
513      *			contains the request the client made of
514      *			the servlet
515      *
516      *
517      * @param resp	the {@link HttpServletResponse} object that
518      *			contains the response the servlet returns
519      *			to the client				
520      *
521      *
522      * @exception IOException	if an input or output error occurs
523      *				while the servlet is handling the
524      *				OPTIONS request
525      *
526      * @exception ServletException	if the request for the
527      *					OPTIONS cannot be handled
528      *
529      */
530          
531     protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
532 	throws ServletException, IOException
533     {
534 	Method[] methods = getAllDeclaredMethods(this.getClass());
535 	
536 	boolean ALLOW_GET = false;
537 	boolean ALLOW_HEAD = false;
538 	boolean ALLOW_POST = false;
539 	boolean ALLOW_PUT = false;
540 	boolean ALLOW_DELETE = false;
541 	boolean ALLOW_TRACE = true;
542 	boolean ALLOW_OPTIONS = true;
543 	
544 	for (int i=0; i<methods.length; i++) {
545 	    Method m = methods[i];
546 	    
547 	    if (m.getName().equals("doGet")) {
548 		ALLOW_GET = true;
549 		ALLOW_HEAD = true;
550 	    }
551 	    if (m.getName().equals("doPost")) 
552 		ALLOW_POST = true;
553 	    if (m.getName().equals("doPut"))
554 		ALLOW_PUT = true;
555 	    if (m.getName().equals("doDelete"))
556 		ALLOW_DELETE = true;
557 	    
558 	}
559 	
560 	String allow = null;
561 	if (ALLOW_GET)
562 	    if (allow==null) allow=METHOD_GET;
563 	if (ALLOW_HEAD)
564 	    if (allow==null) allow=METHOD_HEAD;
565 	    else allow += ", " + METHOD_HEAD;
566 	if (ALLOW_POST)
567 	    if (allow==null) allow=METHOD_POST;
568 	    else allow += ", " + METHOD_POST;
569 	if (ALLOW_PUT)
570 	    if (allow==null) allow=METHOD_PUT;
571 	    else allow += ", " + METHOD_PUT;
572 	if (ALLOW_DELETE)
573 	    if (allow==null) allow=METHOD_DELETE;
574 	    else allow += ", " + METHOD_DELETE;
575 	if (ALLOW_TRACE)
576 	    if (allow==null) allow=METHOD_TRACE;
577 	    else allow += ", " + METHOD_TRACE;
578 	if (ALLOW_OPTIONS)
579 	    if (allow==null) allow=METHOD_OPTIONS;
580 	    else allow += ", " + METHOD_OPTIONS;
581 	
582 	resp.setHeader("Allow", allow);
583     }
584     
585     
586     
587     
588     /**
589      * Called by the server (via the <code>service</code> method)
590      * to allow a servlet to handle a TRACE request.
591      *
592      * A TRACE returns the headers sent with the TRACE
593      * request to the client, so that they can be used in
594      * debugging. There's no need to override this method. 
595      *
596      *
597      *
598      * @param req	the {@link HttpServletRequest} object that
599      *			contains the request the client made of
600      *			the servlet
601      *
602      *
603      * @param resp	the {@link HttpServletResponse} object that
604      *			contains the response the servlet returns
605      *			to the client				
606      *
607      *
608      * @exception IOException	if an input or output error occurs
609      *				while the servlet is handling the
610      *				TRACE request
611      *
612      * @exception ServletException	if the request for the
613      *					TRACE cannot be handled
614      *
615      */
616 
617     protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 
618 	throws ServletException, IOException
619     {
620 	
621 	int responseLength;
622 	
623 	String CRLF = "\r\n";
624 	String responseString = "TRACE "+ req.getRequestURI()+
625 	    " " + req.getProtocol();
626 	
627 	Enumeration reqHeaderEnum = req.getHeaderNames();
628 	
629 	while( reqHeaderEnum.hasMoreElements() ) {
630 	    String headerName = (String)reqHeaderEnum.nextElement();
631 	    responseString += CRLF + headerName + ": " +
632 		req.getHeader(headerName); 
633 	}
634 	
635 	responseString += CRLF;
636 	
637 	responseLength = responseString.length();
638 	
639 	resp.setContentType("message/http");
640 	resp.setContentLength(responseLength);
641 	ServletOutputStream out = resp.getOutputStream();
642 	out.print(responseString);	
643 	out.close();
644 	return;
645     }		
646 
647 
648 
649 
650 
651     /**
652      *
653      * Receives standard HTTP requests from the public
654      * <code>service</code> method and dispatches
655      * them to the <code>do</code><i>XXX</i> methods defined in 
656      * this class. This method is an HTTP-specific version of the 
657      * {@link javax.servlet.Servlet#service} method. There's no
658      * need to override this method.
659      *
660      *
661      *
662      * @param req	the {@link HttpServletRequest} object that
663      *			contains the request the client made of
664      *			the servlet
665      *
666      *
667      * @param resp	the {@link HttpServletResponse} object that
668      *			contains the response the servlet returns
669      *			to the client				
670      *
671      *
672      * @exception IOException	if an input or output error occurs
673      *				while the servlet is handling the
674      *				HTTP request
675      *
676      * @exception ServletException	if the HTTP request
677      *					cannot be handled
678      * 
679      * @see 				javax.servlet.Servlet#service
680      *
681      */
682 
683     protected void service(HttpServletRequest req, HttpServletResponse resp)
684 	throws ServletException, IOException
685     {
686 	String method = req.getMethod();
687 
688 	if (method.equals(METHOD_GET)) {
689 	    long lastModified = getLastModified(req);
690 	    if (lastModified == -1) {
691 		// servlet doesn't support if-modified-since, no reason
692 		// to go through further expensive logic
693 		doGet(req, resp);
694 	    } else {
695 		long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
696 		if (ifModifiedSince < (lastModified / 1000 * 1000)) {
697 		    // If the servlet mod time is later, call doGet()
698                     // Round down to the nearest second for a proper compare
699                     // A ifModifiedSince of -1 will always be less
700 		    maybeSetLastModified(resp, lastModified);
701 		    doGet(req, resp);
702 		} else {
703 		    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
704 		}
705 	    }
706 
707 	} else if (method.equals(METHOD_HEAD)) {
708 	    long lastModified = getLastModified(req);
709 	    maybeSetLastModified(resp, lastModified);
710 	    doHead(req, resp);
711 
712 	} else if (method.equals(METHOD_POST)) {
713 	    doPost(req, resp);
714 	    
715 	} else if (method.equals(METHOD_PUT)) {
716 	    doPut(req, resp);	
717 	    
718 	} else if (method.equals(METHOD_DELETE)) {
719 	    doDelete(req, resp);
720 	    
721 	} else if (method.equals(METHOD_OPTIONS)) {
722 	    doOptions(req,resp);
723 	    
724 	} else if (method.equals(METHOD_TRACE)) {
725 	    doTrace(req,resp);
726 	    
727 	} else {
728 	    //
729 	    // Note that this means NO servlet supports whatever
730 	    // method was requested, anywhere on this server.
731 	    //
732 
733 	    String errMsg = lStrings.getString("http.method_not_implemented");
734 	    Object[] errArgs = new Object[1];
735 	    errArgs[0] = method;
736 	    errMsg = MessageFormat.format(errMsg, errArgs);
737 	    
738 	    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
739 	}
740     }
741     
742 
743 
744 
745 
746     /*
747      * Sets the Last-Modified entity header field, if it has not
748      * already been set and if the value is meaningful.  Called before
749      * doGet, to ensure that headers are set before response data is
750      * written.  A subclass might have set this header already, so we
751      * check.
752      */
753 
754     private void maybeSetLastModified(HttpServletResponse resp,
755 				      long lastModified) {
756 	if (resp.containsHeader(HEADER_LASTMOD))
757 	    return;
758 	if (lastModified >= 0)
759 	    resp.setDateHeader(HEADER_LASTMOD, lastModified);
760     }
761    
762    
763    
764     
765     /**
766      *
767      * Dispatches client requests to the protected
768      * <code>service</code> method. There's no need to
769      * override this method.
770      *
771      * 
772      * @param req	the {@link HttpServletRequest} object that
773      *			contains the request the client made of
774      *			the servlet
775      *
776      *
777      * @param res	the {@link HttpServletResponse} object that
778      *			contains the response the servlet returns
779      *			to the client				
780      *
781      *
782      * @exception IOException	if an input or output error occurs
783      *				while the servlet is handling the
784      *				HTTP request
785      *
786      * @exception ServletException	if the HTTP request cannot
787      *					be handled
788      *
789      * 
790      * @see javax.servlet.Servlet#service
791      *
792      */
793 
794     public void service(ServletRequest req, ServletResponse res)
795 	throws ServletException, IOException
796     {
797 	HttpServletRequest	request;
798 	HttpServletResponse	response;
799 	
800 	try {
801 	    request = (HttpServletRequest) req;
802 	    response = (HttpServletResponse) res;
803 	} catch (ClassCastException e) {
804 	    throw new ServletException("non-HTTP request or response");
805 	}
806 	service(request, response);
807     }
808 }
809 
810 
811 
812 
813 /*
814  * A response that includes no body, for use in (dumb) "HEAD" support.
815  * This just swallows that body, counting the bytes in order to set
816  * the content length appropriately.  All other methods delegate directly
817  * to the HTTP Servlet Response object used to construct this one.
818  */
819 // file private
820 class NoBodyResponse extends HttpServletResponseWrapper {
821     private NoBodyOutputStream		noBody;
822     private PrintWriter			writer;
823     private boolean			didSetContentLength;
824 
825     // file private
826     NoBodyResponse(HttpServletResponse r) {
827         super(r);
828 	noBody = new NoBodyOutputStream();
829     }
830 
831     // file private
832     void setContentLength() {
833 	if (!didSetContentLength)
834 	  super.setContentLength(noBody.getContentLength());
835     }
836 
837 
838     // SERVLET RESPONSE interface methods
839 
840     public void setContentLength(int len) {
841 	super.setContentLength(len);
842 	didSetContentLength = true;
843     }
844 
845     public ServletOutputStream getOutputStream() throws IOException
846       { return noBody; }
847 
848     public PrintWriter getWriter() throws UnsupportedEncodingException
849     {
850 	if (writer == null) {
851 	    OutputStreamWriter	w;
852 
853 	    w = new OutputStreamWriter(noBody, getCharacterEncoding());
854 	    writer = new PrintWriter(w);
855 	}
856 	return writer;
857     }
858 
859 }
860 
861 
862 
863 
864 
865 
866 
867 /*
868  * Servlet output stream that gobbles up all its data.
869  */
870  
871 // file private
872 class NoBodyOutputStream extends ServletOutputStream {
873 
874     private static final String LSTRING_FILE =
875 	"javax.servlet.http.LocalStrings";
876     private static ResourceBundle lStrings =
877 	ResourceBundle.getBundle(LSTRING_FILE);
878 
879     private int		contentLength = 0;
880 
881     // file private
882     NoBodyOutputStream() {}
883 
884     // file private
885     int getContentLength() {
886 	return contentLength;
887     }
888 
889     public void write(int b) {
890 	contentLength++;
891     }
892 
893     public void write(byte buf[], int offset, int len)
894 	throws IOException
895     {
896 	if (len >= 0) {
897 	    contentLength += len;
898 	} else {
899 	    // XXX
900 	    // isn't this really an IllegalArgumentException?
901 	    
902 	    String msg = lStrings.getString("err.io.negativelength");
903 	    throw new IOException("negative length");
904 	}
905     }
906 }