Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/coyote/Response.java


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  package org.apache.coyote;
18  
19  import java.io.IOException;
20  import java.util.Locale;
21  
22  import org.apache.tomcat.util.buf.ByteChunk;
23  import org.apache.tomcat.util.http.MimeHeaders;
24  
25  /**
26   * Response object.
27   * 
28   * @author James Duncan Davidson [duncan@eng.sun.com]
29   * @author Jason Hunter [jch@eng.sun.com]
30   * @author James Todd [gonzo@eng.sun.com]
31   * @author Harish Prabandham
32   * @author Hans Bergsten <hans@gefionsoftware.com>
33   * @author Remy Maucherat
34   */
35  public final class Response {
36  
37  
38      // ----------------------------------------------------------- Constructors
39  
40  
41      public Response() {
42      }
43  
44  
45      // ----------------------------------------------------- Class Variables
46  
47      /**
48       * Default locale as mandated by the spec.
49       */
50      private static Locale DEFAULT_LOCALE = Locale.getDefault();
51  
52  
53      // ----------------------------------------------------- Instance Variables
54  
55      /**
56       * Status code.
57       */
58      protected int status = 200;
59  
60  
61      /**
62       * Status message.
63       */
64      protected String message = null;
65  
66  
67      /**
68       * Response headers.
69       */
70      protected MimeHeaders headers = new MimeHeaders();
71  
72  
73      /**
74       * Associated output buffer.
75       */
76      protected OutputBuffer outputBuffer;
77  
78  
79      /**
80       * Notes.
81       */
82      protected Object notes[] = new Object[Constants.MAX_NOTES];
83  
84  
85      /**
86       * Committed flag.
87       */
88      protected boolean commited = false;
89  
90  
91      /**
92       * Action hook.
93       */
94      public ActionHook hook;
95  
96  
97      /**
98       * HTTP specific fields.
99       */
100     protected String contentType = null;
101     protected String contentLanguage = null;
102     protected String characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
103     protected long contentLength = -1;
104     private Locale locale = DEFAULT_LOCALE;
105 
106     // General informations
107     private long bytesWritten=0;
108 
109     /**
110      * Holds request error exception.
111      */
112     protected Exception errorException = null;
113 
114     /**
115      * Has the charset been explicitly set.
116      */
117     protected boolean charsetSet = false;
118 
119     /**
120      * Request error URI.
121      */
122     protected String errorURI = null;
123 
124     protected Request req;
125 
126     // ------------------------------------------------------------- Properties
127 
128     public Request getRequest() {
129         return req;
130     }
131 
132     public void setRequest( Request req ) {
133         this.req=req;
134     }
135 
136     public OutputBuffer getOutputBuffer() {
137         return outputBuffer;
138     }
139 
140 
141     public void setOutputBuffer(OutputBuffer outputBuffer) {
142         this.outputBuffer = outputBuffer;
143     }
144 
145 
146     public MimeHeaders getMimeHeaders() {
147         return headers;
148     }
149 
150 
151     public ActionHook getHook() {
152         return hook;
153     }
154 
155 
156     public void setHook(ActionHook hook) {
157         this.hook = hook;
158     }
159 
160 
161     // -------------------- Per-Response "notes" --------------------
162 
163 
164     public final void setNote(int pos, Object value) {
165         notes[pos] = value;
166     }
167 
168 
169     public final Object getNote(int pos) {
170         return notes[pos];
171     }
172 
173 
174     // -------------------- Actions --------------------
175 
176 
177     public void action(ActionCode actionCode, Object param) {
178         if (hook != null) {
179             if( param==null ) 
180                 hook.action(actionCode, this);
181             else
182                 hook.action(actionCode, param);
183         }
184     }
185 
186 
187     // -------------------- State --------------------
188 
189 
190     public int getStatus() {
191         return status;
192     }
193 
194     
195     /** 
196      * Set the response status 
197      */ 
198     public void setStatus( int status ) {
199         this.status = status;
200     }
201 
202 
203     /**
204      * Get the status message.
205      */
206     public String getMessage() {
207         return message;
208     }
209 
210 
211     /**
212      * Set the status message.
213      */
214     public void setMessage(String message) {
215         this.message = message;
216     }
217 
218 
219     public boolean isCommitted() {
220         return commited;
221     }
222 
223 
224     public void setCommitted(boolean v) {
225         this.commited = v;
226     }
227 
228 
229     // -----------------Error State --------------------
230 
231 
232     /** 
233      * Set the error Exception that occurred during
234      * request processing.
235      */
236     public void setErrorException(Exception ex) {
237     errorException = ex;
238     }
239 
240 
241     /** 
242      * Get the Exception that occurred during request
243      * processing.
244      */
245     public Exception getErrorException() {
246         return errorException;
247     }
248 
249 
250     public boolean isExceptionPresent() {
251         return ( errorException != null );
252     }
253 
254 
255     /** 
256      * Set request URI that caused an error during
257      * request processing.
258      */
259     public void setErrorURI(String uri) {
260         errorURI = uri;
261     }
262 
263 
264     /** Get the request URI that caused the original error.
265      */
266     public String getErrorURI() {
267         return errorURI;
268     }
269 
270 
271     // -------------------- Methods --------------------
272     
273     
274     public void reset() 
275         throws IllegalStateException {
276         
277         // Reset the headers only if this is the main request,
278         // not for included
279         contentType = null;;
280         locale = DEFAULT_LOCALE;
281         contentLanguage = null;
282         characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
283         contentLength = -1;
284         charsetSet = false;
285 
286         status = 200;
287         message = null;
288         headers.clear();
289         
290         // Force the PrintWriter to flush its data to the output
291         // stream before resetting the output stream
292         //
293         // Reset the stream
294         if (commited) {
295             //String msg = sm.getString("servletOutputStreamImpl.reset.ise"); 
296             throw new IllegalStateException();
297         }
298         
299         action(ActionCode.ACTION_RESET, this);
300     }
301 
302 
303     public void finish() throws IOException {
304         action(ActionCode.ACTION_CLOSE, this);
305     }
306 
307 
308     public void acknowledge() throws IOException {
309         action(ActionCode.ACTION_ACK, this);
310     }
311 
312 
313     // -------------------- Headers --------------------
314     public boolean containsHeader(String name) {
315         return headers.getHeader(name) != null;
316     }
317 
318 
319     public void setHeader(String name, String value) {
320         char cc=name.charAt(0);
321         if( cc=='C' || cc=='c' ) {
322             if( checkSpecialHeader(name, value) )
323             return;
324         }
325         headers.setValue(name).setString( value);
326     }
327 
328 
329     public void addHeader(String name, String value) {
330         char cc=name.charAt(0);
331         if( cc=='C' || cc=='c' ) {
332             if( checkSpecialHeader(name, value) )
333             return;
334         }
335         headers.addValue(name).setString( value );
336     }
337 
338     
339     /** 
340      * Set internal fields for special header names. 
341      * Called from set/addHeader.
342      * Return true if the header is special, no need to set the header.
343      */
344     private boolean checkSpecialHeader( String name, String value) {
345         // XXX Eliminate redundant fields !!!
346         // ( both header and in special fields )
347         if( name.equalsIgnoreCase( "Content-Type" ) ) {
348             setContentType( value );
349             return true;
350         }
351         if( name.equalsIgnoreCase( "Content-Length" ) ) {
352             try {
353                 long cL=Long.parseLong( value );
354                 setContentLength( cL );
355                 return true;
356             } catch( NumberFormatException ex ) {
357                 // Do nothing - the spec doesn't have any "throws" 
358                 // and the user might know what he's doing
359                 return false;
360             }
361         }
362         if( name.equalsIgnoreCase( "Content-Language" ) ) {
363             // XXX XXX Need to construct Locale or something else
364         }
365         return false;
366     }
367 
368 
369     /** Signal that we're done with the headers, and body will follow.
370      *  Any implementation needs to notify ContextManager, to allow
371      *  interceptors to fix headers.
372      */
373     public void sendHeaders() throws IOException {
374         action(ActionCode.ACTION_COMMIT, this);
375         commited = true;
376     }
377 
378 
379     // -------------------- I18N --------------------
380 
381 
382     public Locale getLocale() {
383         return locale;
384     }
385 
386     /**
387      * Called explicitely by user to set the Content-Language and
388      * the default encoding
389      */
390     public void setLocale(Locale locale) {
391 
392         if (locale == null) {
393             return;  // throw an exception?
394         }
395 
396         // Save the locale for use by getLocale()
397         this.locale = locale;
398 
399         // Set the contentLanguage for header output
400         contentLanguage = locale.getLanguage();
401         if ((contentLanguage != null) && (contentLanguage.length() > 0)) {
402             String country = locale.getCountry();
403             StringBuffer value = new StringBuffer(contentLanguage);
404             if ((country != null) && (country.length() > 0)) {
405                 value.append('-');
406                 value.append(country);
407             }
408             contentLanguage = value.toString();
409         }
410 
411     }
412 
413     /**
414      * Return the content language.
415      */
416     public String getContentLanguage() {
417         return contentLanguage;
418     }
419 
420     /*
421      * Overrides the name of the character encoding used in the body
422      * of the response. This method must be called prior to writing output
423      * using getWriter().
424      *
425      * @param charset String containing the name of the chararacter encoding.
426      */
427     public void setCharacterEncoding(String charset) {
428 
429         if (isCommitted())
430             return;
431         if (charset == null)
432             return;
433 
434         characterEncoding = charset;
435         charsetSet=true;
436     }
437 
438     public String getCharacterEncoding() {
439         return characterEncoding;
440     }
441 
442     /**
443      * Sets the content type.
444      *
445      * This method must preserve any response charset that may already have 
446      * been set via a call to response.setContentType(), response.setLocale(),
447      * or response.setCharacterEncoding().
448      *
449      * @param type the content type
450      */
451     public void setContentType(String type) {
452 
453         int semicolonIndex = -1;
454 
455         if (type == null) {
456             this.contentType = null;
457             return;
458         }
459 
460         /*
461          * Remove the charset param (if any) from the Content-Type, and use it
462          * to set the response encoding.
463          * The most recent response encoding setting will be appended to the
464          * response's Content-Type (as its charset param) by getContentType();
465          */
466         boolean hasCharset = false;
467         int len = type.length();
468         int index = type.indexOf(';');
469         while (index != -1) {
470             semicolonIndex = index;
471             index++;
472             while (index < len && Character.isSpace(type.charAt(index))) {
473                 index++;
474             }
475             if (index+8 < len
476                     && type.charAt(index) == 'c'
477                     && type.charAt(index+1) == 'h'
478                     && type.charAt(index+2) == 'a'
479                     && type.charAt(index+3) == 'r'
480                     && type.charAt(index+4) == 's'
481                     && type.charAt(index+5) == 'e'
482                     && type.charAt(index+6) == 't'
483                     && type.charAt(index+7) == '=') {
484                 hasCharset = true;
485                 break;
486             }
487             index = type.indexOf(';', index);
488         }
489 
490         if (!hasCharset) {
491             this.contentType = type;
492             return;
493         }
494 
495         this.contentType = type.substring(0, semicolonIndex);
496         String tail = type.substring(index+8);
497         int nextParam = tail.indexOf(';');
498         String charsetValue = null;
499         if (nextParam != -1) {
500             this.contentType += tail.substring(nextParam);
501             charsetValue = tail.substring(0, nextParam);
502         } else {
503             charsetValue = tail;
504         }
505 
506         // The charset value may be quoted, but must not contain any quotes.
507         if (charsetValue != null && charsetValue.length() > 0) {
508             charsetSet=true;
509             charsetValue = charsetValue.replace('"', ' ');
510             this.characterEncoding = charsetValue.trim();
511         }
512     }
513 
514     public String getContentType() {
515 
516         String ret = contentType;
517 
518         if (ret != null 
519             && characterEncoding != null
520             && charsetSet) {
521             ret = ret + ";charset=" + characterEncoding;
522         }
523 
524         return ret;
525     }
526     
527     public void setContentLength(int contentLength) {
528         this.contentLength = contentLength;
529     }
530 
531     public void setContentLength(long contentLength) {
532         this.contentLength = contentLength;
533     }
534 
535     public int getContentLength() {
536         long length = getContentLengthLong();
537         
538         if (length < Integer.MAX_VALUE) {
539             return (int) length;
540         }
541         return -1;
542     }
543     
544     public long getContentLengthLong() {
545         return contentLength;
546     }
547 
548 
549     /** 
550      * Write a chunk of bytes.
551      */
552     public void doWrite(ByteChunk chunk/*byte buffer[], int pos, int count*/)
553         throws IOException
554     {
555         outputBuffer.doWrite(chunk, this);
556         bytesWritten+=chunk.getLength();
557     }
558 
559     // --------------------
560     
561     public void recycle() {
562         
563         contentType = null;
564         contentLanguage = null;
565         locale = DEFAULT_LOCALE;
566         characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
567         charsetSet = false;
568         contentLength = -1;
569         status = 200;
570         message = null;
571         commited = false;
572         errorException = null;
573         errorURI = null;
574         headers.clear();
575 
576         // update counters
577         bytesWritten=0;
578     }
579 
580     public long getBytesWritten() {
581         return bytesWritten;
582     }
583 
584     public void setBytesWritten(long bytesWritten) {
585         this.bytesWritten = bytesWritten;
586     }
587 }