View Javadoc

1   /*
2    * Copyright 1999,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.commons.messagelet.impl;
19  
20  
21  import java.io.IOException;
22  import java.text.SimpleDateFormat;
23  import java.util.ArrayList;
24  import java.util.Date;
25  import java.util.HashMap;
26  import java.util.Locale;
27  import java.util.TimeZone;
28  
29  import javax.servlet.http.Cookie;
30  import javax.servlet.http.HttpServletResponse;
31  
32  
33  /**
34   * Based on the HttpRequestBase code from Catalina.
35   *
36   * @author Craig R. McClanahan
37   * @author James Strachan
38   * @version $Revision: 155459 $ $Date: 2005-02-26 13:24:44 +0000 (Sat, 26 Feb 2005) $
39   */
40  
41  public class HttpServletResponseImpl extends ServletResponseImpl implements HttpServletResponse {
42  
43      /**
44       * The set of Cookies associated with this Response.
45       */
46      protected ArrayList cookies = new ArrayList();
47  
48  
49      /**
50       * The date format we will use for creating date headers.
51       */
52      protected static final SimpleDateFormat format =
53          new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz",Locale.US);
54      static {
55          format.setTimeZone(TimeZone.getTimeZone("GMT"));
56      };
57  
58  
59      /**
60       * The HTTP headers explicitly added via addHeader(), but not including
61       * those to be added with setContentLength(), setContentType(), and so on.
62       * This collection is keyed by the header name, and the elements are
63       * ArrayLists containing the associated values that have been set.
64       */
65      protected HashMap headers = new HashMap();
66  
67  
68  
69      /**
70       * The error message set by <code>sendError()</code>.
71       */
72      protected String message = getStatusMessage(HttpServletResponse.SC_OK);
73  
74  
75      /**
76       * The HTTP status code associated with this Response.
77       */
78      protected int status = HttpServletResponse.SC_OK;
79  
80  
81      /**
82       * The time zone with which to construct date headers.
83       */
84      protected static final TimeZone zone = TimeZone.getTimeZone("GMT");
85  
86  
87  
88      // --------------------------------------------------------- Public Methods
89  
90  
91      /**
92       * Return an array of all cookies set for this response, or
93       * a zero-length array if no cookies have been set.
94       */
95      public Cookie[] getCookies() {
96  
97          synchronized (cookies) {
98              return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
99          }
100 
101     }
102 
103 
104     /**
105      * Return the value for the specified header, or <code>null</code> if this
106      * header has not been set.  If more than one value was added for this
107      * name, only the first is returned; use getHeaderValues() to retrieve all
108      * of them.
109      *
110      * @param name Header name to look up
111      */
112     public String getHeader(String name) {
113 
114         ArrayList values = null;
115         synchronized (headers) {
116             values = (ArrayList) headers.get(name);
117         }
118         if (values != null)
119             return ((String) values.get(0));
120         else
121             return (null);
122 
123     }
124 
125 
126     /**
127      * Return an array of all the header names set for this response, or
128      * a zero-length array if no headers have been set.
129      */
130     public String[] getHeaderNames() {
131 
132         synchronized (headers) {
133             String results[] = new String[headers.size()];
134             return ((String[]) headers.keySet().toArray(results));
135         }
136 
137     }
138 
139 
140     /**
141      * Return an array of all the header values associated with the
142      * specified header name, or an zero-length array if there are no such
143      * header values.
144      *
145      * @param name Header name to look up
146      */
147     public String[] getHeaderValues(String name) {
148 
149         ArrayList values = null;
150         synchronized (headers) {
151             values = (ArrayList) headers.get(name);
152         }
153         if (values == null)
154             return (new String[0]);
155         String results[] = new String[values.size()];
156         return ((String[]) values.toArray(results));
157 
158     }
159 
160 
161     /**
162      * Return the error message that was set with <code>sendError()</code>
163      * for this Response.
164      */
165     public String getMessage() {
166 
167         return (this.message);
168 
169     }
170 
171 
172     /**
173      * Return the HTTP status code associated with this Response.
174      */
175     public int getStatus() {
176 
177         return (this.status);
178 
179     }
180 
181 
182     /**
183      * Release all object references, and initialize instance variables, in
184      * preparation for reuse of this object.
185      */
186     public void recycle() {
187         //super.recycle();
188         cookies.clear();
189         headers.clear();
190         message = getStatusMessage(HttpServletResponse.SC_OK);
191         status = HttpServletResponse.SC_OK;
192 
193     }
194 
195 
196     /**
197      * Reset this response, and specify the values for the HTTP status code
198      * and corresponding message.
199      *
200      * @exception IllegalStateException if this response has already been
201      *  committed
202      */
203     public void reset(int status, String message) {
204 
205         reset();
206         setStatus(status, message);
207 
208     }
209 
210 
211     // ------------------------------------------------------ Protected Methods
212 
213 
214     /**
215      * Returns a default status message for the specified HTTP status code.
216      *
217      * @param status The status code for which a message is desired
218      */
219     protected String getStatusMessage(int status) {
220 
221         switch (status) {
222         case SC_OK:
223             return ("OK");
224         case SC_ACCEPTED:
225             return ("Accepted");
226         case SC_BAD_GATEWAY:
227             return ("Bad Gateway");
228         case SC_BAD_REQUEST:
229             return ("Bad Request");
230         case SC_CONFLICT:
231             return ("Conflict");
232         case SC_CONTINUE:
233             return ("Continue");
234         case SC_CREATED:
235             return ("Created");
236         case SC_EXPECTATION_FAILED:
237             return ("Expectation Failed");
238         case SC_FORBIDDEN:
239             return ("Forbidden");
240         case SC_GATEWAY_TIMEOUT:
241             return ("Gateway Timeout");
242         case SC_GONE:
243             return ("Gone");
244         case SC_HTTP_VERSION_NOT_SUPPORTED:
245             return ("HTTP Version Not Supported");
246         case SC_INTERNAL_SERVER_ERROR:
247             return ("Internal Server Error");
248         case SC_LENGTH_REQUIRED:
249             return ("Length Required");
250         case SC_METHOD_NOT_ALLOWED:
251             return ("Method Not Allowed");
252         case SC_MOVED_PERMANENTLY:
253             return ("Moved Permanently");
254         case SC_MOVED_TEMPORARILY:
255             return ("Moved Temporarily");
256         case SC_MULTIPLE_CHOICES:
257             return ("Multiple Choices");
258         case SC_NO_CONTENT:
259             return ("No Content");
260         case SC_NON_AUTHORITATIVE_INFORMATION:
261             return ("Non-Authoritative Information");
262         case SC_NOT_ACCEPTABLE:
263             return ("Not Acceptable");
264         case SC_NOT_FOUND:
265             return ("Not Found");
266         case SC_NOT_IMPLEMENTED:
267             return ("Not Implemented");
268         case SC_NOT_MODIFIED:
269             return ("Not Modified");
270         case SC_PARTIAL_CONTENT:
271             return ("Partial Content");
272         case SC_PAYMENT_REQUIRED:
273             return ("Payment Required");
274         case SC_PRECONDITION_FAILED:
275             return ("Precondition Failed");
276         case SC_PROXY_AUTHENTICATION_REQUIRED:
277             return ("Proxy Authentication Required");
278         case SC_REQUEST_ENTITY_TOO_LARGE:
279             return ("Request Entity Too Large");
280         case SC_REQUEST_TIMEOUT:
281             return ("Request Timeout");
282         case SC_REQUEST_URI_TOO_LONG:
283             return ("Request URI Too Long");
284         case SC_REQUESTED_RANGE_NOT_SATISFIABLE:
285             return ("Requested Range Not Satisfiable");
286         case SC_RESET_CONTENT:
287             return ("Reset Content");
288         case SC_SEE_OTHER:
289             return ("See Other");
290         case SC_SERVICE_UNAVAILABLE:
291             return ("Service Unavailable");
292         case SC_SWITCHING_PROTOCOLS:
293             return ("Switching Protocols");
294         case SC_UNAUTHORIZED:
295             return ("Unauthorized");
296         case SC_UNSUPPORTED_MEDIA_TYPE:
297             return ("Unsupported Media Type");
298         case SC_USE_PROXY:
299             return ("Use Proxy");
300         case 207:       // WebDAV
301             return ("Multi-Status");
302         case 422:       // WebDAV
303             return ("Unprocessable Entity");
304         case 423:       // WebDAV
305             return ("Locked");
306         case 507:       // WebDAV
307             return ("Insufficient Storage");
308         default:
309             return ("HTTP Response Status " + status);
310         }
311 
312     }
313 
314 
315 
316     // ------------------------------------------------ ServletResponse Methods
317 
318 
319     /**
320      * Flush the buffer and commit this response.  If this is the first output,
321      * send the HTTP headers prior to the user data.
322      *
323      * @exception IOException if an input/output error occurs
324      */
325     public void flushBuffer() throws IOException {
326 /*
327         if (!isCommitted()) {
328             sendHeaders();
329         }
330 */
331         super.flushBuffer();
332 
333     }
334 
335 
336     /**
337      * Clear any content written to the buffer.  In addition, all cookies
338      * and headers are cleared, and the status is reset.
339      *
340      * @exception IllegalStateException if this response has already
341      *  been committed
342      */
343     public void reset() {
344 
345         if (included)
346             return;     // Ignore any call from an included servlet
347 
348         super.reset();
349         cookies.clear();
350         headers.clear();
351         message = null;
352         status = HttpServletResponse.SC_OK;
353 
354     }
355 
356 
357     /**
358      * Set the content length (in bytes) for this Response.
359      *
360      * @param length The new content length
361      */
362     public void setContentLength(int length) {
363 
364         if (isCommitted())
365             return;
366 
367         if (included)
368             return;     // Ignore any call from an included servlet
369 
370         super.setContentLength(length);
371 
372     }
373 
374 
375 
376     /**
377      * Set the content type for this Response.
378      *
379      * @param type The new content type
380      */
381     public void setContentType(String type) {
382 
383         if (isCommitted())
384             return;
385 
386         if (included)
387             return;     // Ignore any call from an included servlet
388 
389         super.setContentType(type);
390 
391     }
392 
393 
394     /**
395      * Set the Locale that is appropriate for this response, including
396      * setting the appropriate character encoding.
397      *
398      * @param locale The new locale
399      */
400     public void setLocale(Locale locale) {
401 
402         if (isCommitted())
403             return;
404 
405         if (included)
406             return;     // Ignore any call from an included servlet
407 
408         super.setLocale(locale);
409         String language = locale.getLanguage();
410         if ((language != null) && (language.length() > 0)) {
411             String country = locale.getCountry();
412             StringBuffer value = new StringBuffer(language);
413             if ((country != null) && (country.length() > 0)) {
414                 value.append('-');
415                 value.append(country);
416             }
417             setHeader("Content-Language", value.toString());
418         }
419 
420     }
421 
422 
423     // -------------------------------------------- HttpServletResponse Methods
424 
425 
426     /**
427      * Add the specified Cookie to those that will be included with
428      * this Response.
429      *
430      * @param cookie Cookie to be added
431      */
432     public void addCookie(Cookie cookie) {
433 
434         if (isCommitted())
435             return;
436 
437         if (included)
438             return;     // Ignore any call from an included servlet
439 
440         synchronized (cookies) {
441             cookies.add(cookie);
442         }
443 
444     }
445 
446 
447     /**
448      * Add the specified date header to the specified value.
449      *
450      * @param name Name of the header to set
451      * @param value Date value to be set
452      */
453     public void addDateHeader(String name, long value) {
454 
455         if (isCommitted())
456             return;
457 
458         if (included)
459             return;     // Ignore any call from an included servlet
460 
461         addHeader(name, format.format(new Date(value)));
462 
463     }
464 
465 
466     /**
467      * Add the specified header to the specified value.
468      *
469      * @param name Name of the header to set
470      * @param value Value to be set
471      */
472     public void addHeader(String name, String value) {
473 
474         if (isCommitted())
475             return;
476 
477         if (included)
478             return;     // Ignore any call from an included servlet
479 
480         synchronized (headers) {
481             ArrayList values = (ArrayList) headers.get(name);
482             if (values == null) {
483                 values = new ArrayList();
484                 headers.put(name, values);
485             }
486             values.add(value);
487         }
488 
489     }
490 
491 
492     /**
493      * Add the specified integer header to the specified value.
494      *
495      * @param name Name of the header to set
496      * @param value Integer value to be set
497      */
498     public void addIntHeader(String name, int value) {
499 
500         if (isCommitted())
501             return;
502 
503         if (included)
504             return;     // Ignore any call from an included servlet
505 
506         addHeader(name, "" + value);
507 
508     }
509 
510 
511     /**
512      * Has the specified header been set already in this response?
513      *
514      * @param name Name of the header to check
515      */
516     public boolean containsHeader(String name) {
517 
518         synchronized (headers) {
519             return (headers.get(name) != null);
520         }
521 
522     }
523 
524 
525     /**
526      * Encode the session identifier associated with this response
527      * into the specified redirect URL, if necessary.
528      *
529      * @param url URL to be encoded
530      */
531     public String encodeRedirectURL(String url) {
532 
533         return (url);
534 
535     }
536 
537 
538     /**
539      * Encode the session identifier associated with this response
540      * into the specified redirect URL, if necessary.
541      *
542      * @param url URL to be encoded
543      *
544      * @deprecated As of Version 2.1 of the Java Servlet API, use
545      *  <code>encodeRedirectURL()</code> instead.
546      */
547     public String encodeRedirectUrl(String url) {
548 
549         return (encodeRedirectURL(url));
550 
551     }
552 
553 
554     /**
555      * Encode the session identifier associated with this response
556      * into the specified URL, if necessary.
557      *
558      * @param url URL to be encoded
559      */
560     public String encodeURL(String url) {
561         
562         return (url);
563     }
564 
565 
566     /**
567      * Encode the session identifier associated with this response
568      * into the specified URL, if necessary.
569      *
570      * @param url URL to be encoded
571      *
572      * @deprecated As of Version 2.1 of the Java Servlet API, use
573      *  <code>encodeURL()</code> instead.
574      */
575     public String encodeUrl(String url) {
576 
577         return (encodeURL(url));
578 
579     }
580 
581 
582     /**
583      * Send an error response with the specified status and a
584      * default message.
585      *
586      * @param status HTTP status code to send
587      *
588      * @exception IllegalStateException if this response has
589      *  already been committed
590      * @exception IOException if an input/output error occurs
591      */
592     public void sendError(int status) throws IOException {
593 
594         sendError(status, getStatusMessage(status));
595 
596     }
597 
598 
599     /**
600      * Send an error response with the specified status and message.
601      *
602      * @param status HTTP status code to send
603      * @param message Corresponding message to send
604      *
605      * @exception IllegalStateException if this response has
606      *  already been committed
607      * @exception IOException if an input/output error occurs
608      */
609     public void sendError(int status, String message) throws IOException {
610 
611         if (isCommitted()) {
612             throw new IllegalStateException( "Cannot send error, already committed" );
613         }
614 
615         if (included) {
616             return;     // Ignore any call from an included servlet
617         }
618 
619         //setError();
620 
621         // Record the status code and message.
622         this.status = status;
623         this.message = message;
624 
625         // Clear any data content that has been buffered
626         resetBuffer();
627 
628         // Cause the response to be committed
629         /* Per spec clarification, no default content type is set
630         String contentType = getContentType();
631         if ((contentType == null) || "text/plain".equals(contentType))
632             setContentType("text/html");
633         */
634 
635         // Temporarily comment out the following flush so that
636         // default error reports can still set the content type
637         // FIXME - this stuff needs to be refactored
638         /*
639         try {
640             flushBuffer();
641         } catch (IOException e) {
642             ;
643         }
644         */
645 
646     }
647 
648 
649     /**
650      * Send a temporary redirect to the specified redirect location URL.
651      *
652      * @param location Location URL to redirect to
653      *
654      * @exception IllegalStateException if this response has
655      *  already been committed
656      * @exception IOException if an input/output error occurs
657      */
658     public void sendRedirect(String location) throws IOException {
659 
660         if (isCommitted()) {
661             throw new IllegalStateException( "Cannot send error, already committed" );
662         }
663         if (included)
664             return;     // Ignore any call from an included servlet
665 
666         // Clear any data content that has been buffered
667         resetBuffer();
668 
669         // Generate a temporary redirect to the specified location
670         //String absolute = toAbsolute(location);
671         String absolute = location;
672         setStatus(SC_MOVED_TEMPORARILY);
673         setHeader("Location", absolute);
674 
675     }
676 
677 
678     /**
679      * Set the specified date header to the specified value.
680      *
681      * @param name Name of the header to set
682      * @param value Date value to be set
683      */
684     public void setDateHeader(String name, long value) {
685 
686         if (isCommitted())
687             return;
688 
689         if (included)
690             return;     // Ignore any call from an included servlet
691 
692         setHeader(name, format.format(new Date(value)));
693 
694     }
695 
696 
697     /**
698      * Set the specified header to the specified value.
699      *
700      * @param name Name of the header to set
701      * @param value Value to be set
702      */
703     public void setHeader(String name, String value) {
704 
705         if (isCommitted())
706             return;
707 
708         if (included)
709             return;     // Ignore any call from an included servlet
710 
711         ArrayList values = new ArrayList();
712         values.add(value);
713         synchronized (headers) {
714             headers.put(name, values);
715         }
716 
717         String match = name.toLowerCase();
718         if (match.equals("content-length")) {
719             int contentLength = -1;
720             try {
721                 contentLength = Integer.parseInt(value);
722             } catch (NumberFormatException e) {
723                 ;
724             }
725             if (contentLength >= 0)
726                 setContentLength(contentLength);
727         } else if (match.equals("content-type")) {
728             setContentType(value);
729         }
730 
731     }
732 
733 
734     /**
735      * Set the specified integer header to the specified value.
736      *
737      * @param name Name of the header to set
738      * @param value Integer value to be set
739      */
740     public void setIntHeader(String name, int value) {
741 
742         if (isCommitted())
743             return;
744 
745         if (included)
746             return;     // Ignore any call from an included servlet
747 
748         setHeader(name, "" + value);
749 
750     }
751 
752 
753     /**
754      * Set the HTTP status to be returned with this response.
755      *
756      * @param status The new HTTP status
757      */
758     public void setStatus(int status) {
759 
760         setStatus(status, getStatusMessage(status));
761 
762     }
763 
764 
765     /**
766      * Set the HTTP status and message to be returned with this response.
767      *
768      * @param status The new HTTP status
769      * @param message The associated text message
770      *
771      * @deprecated As of Version 2.1 of the Java Servlet API, this method
772      *  has been deprecated due to the ambiguous meaning of the message
773      *  parameter.
774      */
775     public void setStatus(int status, String message) {
776 
777         if (included)
778             return;     // Ignore any call from an included servlet
779 
780         this.status = status;
781         this.message = message;
782 
783     }
784 
785 
786 }