001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package javax.servlet.http;
021    
022    import java.io.IOException;
023    import java.io.PrintWriter;
024    import java.io.OutputStreamWriter;
025    import java.io.UnsupportedEncodingException;
026    import java.lang.reflect.Method;
027    import java.text.MessageFormat;
028    import java.util.Enumeration;
029    import java.util.Locale;
030    import java.util.ResourceBundle;
031    
032    import javax.servlet.GenericServlet;
033    import javax.servlet.ServletException;
034    import javax.servlet.ServletOutputStream;
035    import javax.servlet.ServletRequest;
036    import javax.servlet.ServletResponse;
037    
038    
039    /**
040     * Provides an abstract class to be subclassed to create
041     * an HTTP servlet suitable for a Web site. A subclass of
042     * <code>HttpServlet</code> must override at least
043     * one method, usually one of these:
044     * <p/>
045     * <ul>
046     * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
047     * <li> <code>doPost</code>, for HTTP POST requests
048     * <li> <code>doPut</code>, for HTTP PUT requests
049     * <li> <code>doDelete</code>, for HTTP DELETE requests
050     * <li> <code>init</code> and <code>destroy</code>,
051     * to manage resources that are held for the life of the servlet
052     * <li> <code>getServletInfo</code>, which the servlet uses to
053     * provide information about itself
054     * </ul>
055     * <p/>
056     * <p>There's almost no reason to override the <code>service</code>
057     * method. <code>service</code> handles standard HTTP
058     * requests by dispatching them to the handler methods
059     * for each HTTP request type (the <code>do</code><i>XXX</i>
060     * methods listed above).
061     * <p/>
062     * <p>Likewise, there's almost no reason to override the
063     * <code>doOptions</code> and <code>doTrace</code> methods.
064     * <p/>
065     * <p>Servlets typically run on multithreaded servers,
066     * so be aware that a servlet must handle concurrent
067     * requests and be careful to synchronize access to shared resources.
068     * Shared resources include in-memory data such as
069     * instance or class variables and external objects
070     * such as files, database connections, and network
071     * connections.
072     * See the
073     * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
074     * Java Tutorial on Multithreaded Programming</a> for more
075     * information on handling multiple threads in a Java program.
076     *
077     * @version $Rev: 788194 $ $Date: 2009-06-24 18:05:48 -0400 (Wed, 24 Jun 2009) $
078     */
079    
080    public abstract class HttpServlet extends GenericServlet
081            implements java.io.Serializable {
082        private static final String METHOD_DELETE = "DELETE";
083        private static final String METHOD_HEAD = "HEAD";
084        private static final String METHOD_GET = "GET";
085        private static final String METHOD_OPTIONS = "OPTIONS";
086        private static final String METHOD_POST = "POST";
087        private static final String METHOD_PUT = "PUT";
088        private static final String METHOD_TRACE = "TRACE";
089    
090        private static final String HEADER_IFMODSINCE = "If-Modified-Since";
091        private static final String HEADER_LASTMOD = "Last-Modified";
092    
093        private static final String LSTRING_FILE =
094                "javax.servlet.http.LocalStrings";
095        private static ResourceBundle lStrings =
096                ResourceBundle.getBundle(LSTRING_FILE);
097    
098        /**
099         * Does nothing, because this is an abstract class.
100         */
101    
102        public HttpServlet() {
103        }
104    
105        /**
106         * Called by the server (via the <code>service</code> method) to
107         * allow a servlet to handle a GET request.
108         * <p/>
109         * <p>Overriding this method to support a GET request also
110         * automatically supports an HTTP HEAD request. A HEAD
111         * request is a GET request that returns no body in the
112         * response, only the request header fields.
113         * <p/>
114         * <p>When overriding this method, read the request data,
115         * write the response headers, get the response's writer or
116         * output stream object, and finally, write the response data.
117         * It's best to include content type and encoding. When using
118         * a <code>PrintWriter</code> object to return the response,
119         * set the content type before accessing the
120         * <code>PrintWriter</code> object.
121         * <p/>
122         * <p>The servlet container must write the headers before
123         * committing the response, because in HTTP the headers must be sent
124         * before the response body.
125         * <p/>
126         * <p>Where possible, set the Content-Length header (with the
127         * {@link javax.servlet.ServletResponse#setContentLength} method),
128         * to allow the servlet container to use a persistent connection
129         * to return its response to the client, improving performance.
130         * The content length is automatically set if the entire response fits
131         * inside the response buffer.
132         * <p/>
133         * <p>When using HTTP 1.1 chunked encoding (which means that the response
134         * has a Transfer-Encoding header), do not set the Content-Length header.
135         * <p/>
136         * <p>The GET method should be safe, that is, without
137         * any side effects for which users are held responsible.
138         * For example, most form queries have no side effects.
139         * If a client request is intended to change stored data,
140         * the request should use some other HTTP method.
141         * <p/>
142         * <p>The GET method should also be idempotent, meaning
143         * that it can be safely repeated. Sometimes making a
144         * method safe also makes it idempotent. For example,
145         * repeating queries is both safe and idempotent, but
146         * buying a product online or modifying data is neither
147         * safe nor idempotent.
148         * <p/>
149         * <p>If the request is incorrectly formatted, <code>doGet</code>
150         * returns an HTTP "Bad Request" message.
151         *
152         * @param req  an {@link HttpServletRequest} object that
153         *             contains the request the client has made
154         *             of the servlet
155         * @param resp an {@link HttpServletResponse} object that
156         *             contains the response the servlet sends
157         *             to the client
158         * @throws IOException      if an input or output error is
159         *                          detected when the servlet handles
160         *                          the GET request
161         * @throws ServletException if the request for the GET
162         *                          could not be handled
163         * @see javax.servlet.ServletResponse#setContentType
164         */
165        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
166                throws ServletException, IOException {
167            String protocol = req.getProtocol();
168            String msg = lStrings.getString("http.method_get_not_supported");
169            if (protocol.endsWith("1.1")) {
170                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
171            } else {
172                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
173            }
174        }
175    
176        /**
177         * Returns the time the <code>HttpServletRequest</code>
178         * object was last modified,
179         * in milliseconds since midnight January 1, 1970 GMT.
180         * If the time is unknown, this method returns a negative
181         * number (the default).
182         * <p/>
183         * <p>Servlets that support HTTP GET requests and can quickly determine
184         * their last modification time should override this method.
185         * This makes browser and proxy caches work more effectively,
186         * reducing the load on server and network resources.
187         *
188         * @param req the <code>HttpServletRequest</code>
189         *            object that is sent to the servlet
190         * @return a <code>long</code> integer specifying
191         * the time the <code>HttpServletRequest</code>
192         * object was last modified, in milliseconds
193         * since midnight, January 1, 1970 GMT, or
194         * -1 if the time is not known
195         */
196        protected long getLastModified(HttpServletRequest req) {
197            return -1;
198        }
199    
200        /**
201         * <p>Receives an HTTP HEAD request from the protected
202         * <code>service</code> method and handles the
203         * request.
204         * The client sends a HEAD request when it wants
205         * to see only the headers of a response, such as
206         * Content-Type or Content-Length. The HTTP HEAD
207         * method counts the output bytes in the response
208         * to set the Content-Length header accurately.
209         * <p/>
210         * <p>If you override this method, you can avoid computing
211         * the response body and just set the response headers
212         * directly to improve performance. Make sure that the
213         * <code>doHead</code> method you write is both safe
214         * and idempotent (that is, protects itself from being
215         * called multiple times for one HTTP HEAD request).
216         * <p/>
217         * <p>If the HTTP HEAD request is incorrectly formatted,
218         * <code>doHead</code> returns an HTTP "Bad Request"
219         * message.
220         *
221         * @param req  the request object that is passed
222         *             to the servlet
223         * @param resp the response object that the servlet
224         *             uses to return the headers to the clien
225         * @throws IOException      if an input or output error occurs
226         * @throws ServletException if the request for the HEAD
227         *                          could not be handled
228         */
229        protected void doHead(HttpServletRequest req, HttpServletResponse resp)
230                throws ServletException, IOException {
231            NoBodyResponse response = new NoBodyResponse(resp);
232    
233            doGet(req, response);
234            response.setContentLength();
235        }
236    
237        /**
238         * Called by the server (via the <code>service</code> method)
239         * to allow a servlet to handle a POST request.
240         * <p/>
241         * The HTTP POST method allows the client to send
242         * data of unlimited length to the Web server a single time
243         * and is useful when posting information such as
244         * credit card numbers.
245         * <p/>
246         * <p>When overriding this method, read the request data,
247         * write the response headers, get the response's writer or output
248         * stream object, and finally, write the response data. It's best
249         * to include content type and encoding. When using a
250         * <code>PrintWriter</code> object to return the response, set the
251         * content type before accessing the <code>PrintWriter</code> object.
252         * <p/>
253         * <p>The servlet container must write the headers before committing the
254         * response, because in HTTP the headers must be sent before the
255         * response body.
256         * <p/>
257         * <p>Where possible, set the Content-Length header (with the
258         * {@link javax.servlet.ServletResponse#setContentLength} method),
259         * to allow the servlet container to use a persistent connection
260         * to return its response to the client, improving performance.
261         * The content length is automatically set if the entire response fits
262         * inside the response buffer.
263         * <p/>
264         * <p>When using HTTP 1.1 chunked encoding (which means that the response
265         * has a Transfer-Encoding header), do not set the Content-Length header.
266         * <p/>
267         * <p>This method does not need to be either safe or idempotent.
268         * Operations requested through POST can have side effects for
269         * which the user can be held accountable, for example,
270         * updating stored data or buying items online.
271         * <p/>
272         * <p>If the HTTP POST request is incorrectly formatted,
273         * <code>doPost</code> returns an HTTP "Bad Request" message.
274         *
275         * @param req  an {@link HttpServletRequest} object that
276         *             contains the request the client has made
277         *             of the servlet
278         * @param resp an {@link HttpServletResponse} object that
279         *             contains the response the servlet sends
280         *             to the client
281         * @throws IOException      if an input or output error is
282         *                          detected when the servlet handles
283         *                          the request
284         * @throws ServletException if the request for the POST
285         *                          could not be handled
286         * @see javax.servlet.ServletOutputStream
287         * @see javax.servlet.ServletResponse#setContentType
288         */
289        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
290                throws ServletException, IOException {
291            String protocol = req.getProtocol();
292            String msg = lStrings.getString("http.method_post_not_supported");
293            if (protocol.endsWith("1.1")) {
294                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
295            } else {
296                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
297            }
298        }
299    
300        /**
301         * Called by the server (via the <code>service</code> method)
302         * to allow a servlet to handle a PUT request.
303         * <p/>
304         * The PUT operation allows a client to
305         * place a file on the server and is similar to
306         * sending a file by FTP.
307         * <p/>
308         * <p>When overriding this method, leave intact
309         * any content headers sent with the request (including
310         * Content-Length, Content-Type, Content-Transfer-Encoding,
311         * Content-Encoding, Content-Base, Content-Language, Content-Location,
312         * Content-MD5, and Content-Range). If your method cannot
313         * handle a content header, it must issue an error message
314         * (HTTP 501 - Not Implemented) and discard the request.
315         * For more information on HTTP 1.1, see RFC 2616
316         * <a href="http://www.ietf.org/rfc/rfc2616.txt"></a>.
317         * <p/>
318         * <p>This method does not need to be either safe or idempotent.
319         * Operations that <code>doPut</code> performs can have side
320         * effects for which the user can be held accountable. When using
321         * this method, it may be useful to save a copy of the
322         * affected URL in temporary storage.
323         * <p/>
324         * <p>If the HTTP PUT request is incorrectly formatted,
325         * <code>doPut</code> returns an HTTP "Bad Request" message.
326         *
327         * @param req  the {@link HttpServletRequest} object that
328         *             contains the request the client made of
329         *             the servlet
330         * @param resp the {@link HttpServletResponse} object that
331         *             contains the response the servlet returns
332         *             to the client
333         * @throws IOException      if an input or output error occurs
334         *                          while the servlet is handling the
335         *                          PUT request
336         * @throws ServletException if the request for the PUT
337         *                          cannot be handled
338         */
339        protected void doPut(HttpServletRequest req, HttpServletResponse resp)
340                throws ServletException, IOException {
341            String protocol = req.getProtocol();
342            String msg = lStrings.getString("http.method_put_not_supported");
343            if (protocol.endsWith("1.1")) {
344                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
345            } else {
346                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
347            }
348        }
349    
350        /**
351         * Called by the server (via the <code>service</code> method)
352         * to allow a servlet to handle a DELETE request.
353         * <p/>
354         * The DELETE operation allows a client to remove a document
355         * or Web page from the server.
356         * <p/>
357         * <p>This method does not need to be either safe
358         * or idempotent. Operations requested through
359         * DELETE can have side effects for which users
360         * can be held accountable. When using
361         * this method, it may be useful to save a copy of the
362         * affected URL in temporary storage.
363         * <p/>
364         * <p>If the HTTP DELETE request is incorrectly formatted,
365         * <code>doDelete</code> returns an HTTP "Bad Request"
366         * message.
367         *
368         * @param req  the {@link HttpServletRequest} object that
369         *             contains the request the client made of
370         *             the servlet
371         * @param resp the {@link HttpServletResponse} object that
372         *             contains the response the servlet returns
373         *             to the client
374         * @throws IOException      if an input or output error occurs
375         *                          while the servlet is handling the
376         *                          DELETE request
377         * @throws ServletException if the request for the
378         *                          DELETE cannot be handled
379         */
380        protected void doDelete(HttpServletRequest req,
381                                HttpServletResponse resp)
382                throws ServletException, IOException {
383            String protocol = req.getProtocol();
384            String msg = lStrings.getString("http.method_delete_not_supported");
385            if (protocol.endsWith("1.1")) {
386                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
387            } else {
388                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
389            }
390        }
391    
392        private static Method[] getAllDeclaredMethods(Class c) {
393    
394            if (c.equals(javax.servlet.http.HttpServlet.class)) {
395                return null;
396            }
397    
398            Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
399            Method[] thisMethods = c.getDeclaredMethods();
400    
401            if ((parentMethods != null) && (parentMethods.length > 0)) {
402                Method[] allMethods =
403                        new Method[parentMethods.length + thisMethods.length];
404                System.arraycopy(parentMethods, 0, allMethods, 0,
405                        parentMethods.length);
406                System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
407                        thisMethods.length);
408    
409                thisMethods = allMethods;
410            }
411    
412            return thisMethods;
413        }
414    
415        /**
416         * Called by the server (via the <code>service</code> method)
417         * to allow a servlet to handle a OPTIONS request.
418         * <p/>
419         * The OPTIONS request determines which HTTP methods
420         * the server supports and
421         * returns an appropriate header. For example, if a servlet
422         * overrides <code>doGet</code>, this method returns the
423         * following header:
424         * <p/>
425         * <p><code>Allow: GET, HEAD, TRACE, OPTIONS</code>
426         * <p/>
427         * <p>There's no need to override this method unless the
428         * servlet implements new HTTP methods, beyond those
429         * implemented by HTTP 1.1.
430         *
431         * @param req  the {@link HttpServletRequest} object that
432         *             contains the request the client made of
433         *             the servlet
434         * @param resp the {@link HttpServletResponse} object that
435         *             contains the response the servlet returns
436         *             to the client
437         * @throws IOException      if an input or output error occurs
438         *                          while the servlet is handling the
439         *                          OPTIONS request
440         * @throws ServletException if the request for the
441         *                          OPTIONS cannot be handled
442         */
443        protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
444                throws ServletException, IOException {
445            Method[] methods = getAllDeclaredMethods(this.getClass());
446    
447            boolean ALLOW_GET = false;
448            boolean ALLOW_HEAD = false;
449            boolean ALLOW_POST = false;
450            boolean ALLOW_PUT = false;
451            boolean ALLOW_DELETE = false;
452            boolean ALLOW_TRACE = true;
453            boolean ALLOW_OPTIONS = true;
454    
455            for (int i = 0; i < methods.length; i++) {
456                Method m = methods[i];
457    
458                if (m.getName().equals("doGet")) {
459                    ALLOW_GET = true;
460                    ALLOW_HEAD = true;
461                }
462                if (m.getName().equals("doPost"))
463                    ALLOW_POST = true;
464                if (m.getName().equals("doPut"))
465                    ALLOW_PUT = true;
466                if (m.getName().equals("doDelete"))
467                    ALLOW_DELETE = true;
468    
469            }
470    
471            String allow = null;
472            if (ALLOW_GET)
473                if (allow == null) allow = METHOD_GET;
474            if (ALLOW_HEAD)
475                if (allow == null) allow = METHOD_HEAD;
476                else allow += ", " + METHOD_HEAD;
477            if (ALLOW_POST)
478                if (allow == null) allow = METHOD_POST;
479                else allow += ", " + METHOD_POST;
480            if (ALLOW_PUT)
481                if (allow == null) allow = METHOD_PUT;
482                else allow += ", " + METHOD_PUT;
483            if (ALLOW_DELETE)
484                if (allow == null) allow = METHOD_DELETE;
485                else allow += ", " + METHOD_DELETE;
486            if (ALLOW_TRACE)
487                if (allow == null) allow = METHOD_TRACE;
488                else allow += ", " + METHOD_TRACE;
489            if (ALLOW_OPTIONS)
490                if (allow == null) allow = METHOD_OPTIONS;
491                else allow += ", " + METHOD_OPTIONS;
492    
493            resp.setHeader("Allow", allow);
494        }
495    
496        /**
497         * Called by the server (via the <code>service</code> method)
498         * to allow a servlet to handle a TRACE request.
499         * <p/>
500         * A TRACE returns the headers sent with the TRACE
501         * request to the client, so that they can be used in
502         * debugging. There's no need to override this method.
503         *
504         * @param req  the {@link HttpServletRequest} object that
505         *             contains the request the client made of
506         *             the servlet
507         * @param resp the {@link HttpServletResponse} object that
508         *             contains the response the servlet returns
509         *             to the client
510         * @throws IOException      if an input or output error occurs
511         *                          while the servlet is handling the
512         *                          TRACE request
513         * @throws ServletException if the request for the
514         *                          TRACE cannot be handled
515         */
516        protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
517                throws ServletException, IOException {
518    
519            int responseLength;
520    
521            String CRLF = "\r\n";
522            String responseString = "TRACE " + req.getRequestURI() +
523                    " " + req.getProtocol();
524    
525            Enumeration reqHeaderEnum = req.getHeaderNames();
526    
527            while (reqHeaderEnum.hasMoreElements()) {
528                String headerName = (String) reqHeaderEnum.nextElement();
529                responseString += CRLF + headerName + ": " +
530                        req.getHeader(headerName);
531            }
532    
533            responseString += CRLF;
534    
535            responseLength = responseString.length();
536    
537            resp.setContentType("message/http");
538            resp.setContentLength(responseLength);
539            ServletOutputStream out = resp.getOutputStream();
540            out.print(responseString);
541            out.close();
542            return;
543        }
544    
545        /**
546         * Receives standard HTTP requests from the public
547         * <code>service</code> method and dispatches
548         * them to the <code>do</code><i>XXX</i> methods defined in
549         * this class. This method is an HTTP-specific version of the
550         * {@link javax.servlet.Servlet#service} method. There's no
551         * need to override this method.
552         *
553         * @param req  the {@link HttpServletRequest} object that
554         *             contains the request the client made of
555         *             the servlet
556         * @param resp the {@link HttpServletResponse} object that
557         *             contains the response the servlet returns
558         *             to the client
559         * @throws IOException      if an input or output error occurs
560         *                          while the servlet is handling the
561         *                          HTTP request
562         * @throws ServletException if the HTTP request
563         *                          cannot be handled
564         * @see javax.servlet.Servlet#service
565         */
566        protected void service(HttpServletRequest req, HttpServletResponse resp)
567                throws ServletException, IOException {
568            String method = req.getMethod();
569    
570            if (method.equals(METHOD_GET)) {
571                long lastModified = getLastModified(req);
572                if (lastModified == -1) {
573                    // servlet doesn't support if-modified-since, no reason
574                    // to go through further expensive logic
575                    doGet(req, resp);
576                } else {
577                    long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
578                    if (ifModifiedSince < (lastModified / 1000 * 1000)) {
579                        // If the servlet mod time is later, call doGet()
580                        // Round down to the nearest second for a proper compare
581                        // A ifModifiedSince of -1 will always be less
582                        maybeSetLastModified(resp, lastModified);
583                        doGet(req, resp);
584                    } else {
585                        resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
586                    }
587                }
588    
589            } else if (method.equals(METHOD_HEAD)) {
590                long lastModified = getLastModified(req);
591                maybeSetLastModified(resp, lastModified);
592                doHead(req, resp);
593    
594            } else if (method.equals(METHOD_POST)) {
595                doPost(req, resp);
596    
597            } else if (method.equals(METHOD_PUT)) {
598                doPut(req, resp);
599    
600            } else if (method.equals(METHOD_DELETE)) {
601                doDelete(req, resp);
602    
603            } else if (method.equals(METHOD_OPTIONS)) {
604                doOptions(req, resp);
605    
606            } else if (method.equals(METHOD_TRACE)) {
607                doTrace(req, resp);
608    
609            } else {
610                //
611                // Note that this means NO servlet supports whatever
612                // method was requested, anywhere on this server.
613                //
614    
615                String errMsg = lStrings.getString("http.method_not_implemented");
616                Object[] errArgs = new Object[1];
617                errArgs[0] = method;
618                errMsg = MessageFormat.format(errMsg, errArgs);
619    
620                resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
621            }
622        }
623    
624        /*
625         * Sets the Last-Modified entity header field, if it has not
626         * already been set and if the value is meaningful.  Called before
627         * doGet, to ensure that headers are set before response data is
628         * written.  A subclass might have set this header already, so we
629         * check.
630         */
631        private void maybeSetLastModified(HttpServletResponse resp,
632                                          long lastModified) {
633            if (resp.containsHeader(HEADER_LASTMOD))
634                return;
635            if (lastModified >= 0)
636                resp.setDateHeader(HEADER_LASTMOD, lastModified);
637        }
638    
639        /**
640         * Dispatches client requests to the protected
641         * <code>service</code> method. There's no need to
642         * override this method.
643         *
644         * @param req the {@link HttpServletRequest} object that
645         *            contains the request the client made of
646         *            the servlet
647         * @param res the {@link HttpServletResponse} object that
648         *            contains the response the servlet returns
649         *            to the client
650         * @throws IOException      if an input or output error occurs
651         *                          while the servlet is handling the
652         *                          HTTP request
653         * @throws ServletException if the HTTP request cannot
654         *                          be handled
655         * @see javax.servlet.Servlet#service
656         */
657        public void service(ServletRequest req, ServletResponse res)
658                throws ServletException, IOException {
659            HttpServletRequest request;
660            HttpServletResponse response;
661    
662            try {
663                request = (HttpServletRequest) req;
664                response = (HttpServletResponse) res;
665            } catch (ClassCastException e) {
666                throw new ServletException("non-HTTP request or response");
667            }
668            service(request, response);
669        }
670    }
671    
672    /*
673     * A response that includes no body, for use in (dumb) "HEAD" support.
674     * This just swallows that body, counting the bytes in order to set
675     * the content length appropriately.  All other methods delegate directly
676     * to the HTTP Servlet Response object used to construct this one.
677     */
678    
679    // file private
680    class NoBodyResponse extends HttpServletResponseWrapper {
681        private NoBodyOutputStream noBody;
682        private PrintWriter writer;
683        private boolean didSetContentLength;
684    
685        // file private
686        NoBodyResponse(HttpServletResponse r) {
687            super(r);
688            noBody = new NoBodyOutputStream();
689        }
690    
691        // file private
692        void setContentLength() {
693            if (!didSetContentLength)
694                super.setContentLength(noBody.getContentLength());
695        }
696    
697        // SERVLET RESPONSE interface methods
698    
699        public void setContentLength(int len) {
700            super.setContentLength(len);
701            didSetContentLength = true;
702        }
703    
704        public ServletOutputStream getOutputStream() throws IOException {
705            return noBody;
706        }
707    
708        public PrintWriter getWriter() throws UnsupportedEncodingException {
709            if (writer == null) {
710                OutputStreamWriter w;
711    
712                w = new OutputStreamWriter(noBody, getCharacterEncoding());
713                writer = new PrintWriter(w);
714            }
715            return writer;
716        }
717    
718    }
719    
720    /*
721     * Servlet output stream that gobbles up all its data.
722     */
723    
724    // file private
725    class NoBodyOutputStream extends ServletOutputStream {
726    
727        private static final String LSTRING_FILE =
728                "javax.servlet.http.LocalStrings";
729        private static ResourceBundle lStrings =
730                ResourceBundle.getBundle(LSTRING_FILE);
731    
732        private int contentLength = 0;
733    
734        // file private
735        NoBodyOutputStream() {
736        }
737    
738        // file private
739        int getContentLength() {
740            return contentLength;
741        }
742    
743        public void write(int b) {
744            contentLength++;
745        }
746    
747        public void write(byte buf[], int offset, int len)
748                throws IOException {
749            if (len >= 0) {
750                contentLength += len;
751            } else {
752                // XXX
753                // isn't this really an IllegalArgumentException?
754    
755                String msg = lStrings.getString("err.io.negativelength");
756                throw new IOException("negative length");
757            }
758        }
759    }