KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > HttpMessage


1 // ========================================================================
2
// $Id: HttpMessage.java,v 1.41 2006/04/04 22:28:02 gregwilkins Exp $
3
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
4
// ------------------------------------------------------------------------
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
// ========================================================================
15

16 package org.mortbay.http;
17
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.io.OutputStream JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.io.Writer JavaDoc;
23 import java.util.Collections JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29
30 import org.apache.commons.logging.Log;
31 import org.mortbay.log.LogFactory;
32 import org.mortbay.util.LogSupport;
33 import org.mortbay.util.QuotedStringTokenizer;
34 import org.mortbay.util.TypeUtil;
35
36
37 /* ------------------------------------------------------------ */
38 /** HTTP Message base.
39  * This class forms the basis of HTTP requests and replies. It provides
40  * header fields, content and optional trailer fields, while managing the
41  * state of the message.
42  *
43  * @version $Id: HttpMessage.java,v 1.41 2006/04/04 22:28:02 gregwilkins Exp $
44  * @author Greg Wilkins (gregw)
45  */

46
47 public abstract class HttpMessage
48 {
49     private static Log log = LogFactory.getLog(HttpMessage.class);
50
51     /* ------------------------------------------------------------ */
52     public final static String JavaDoc __SCHEME ="http";
53     public final static String JavaDoc __SSL_SCHEME ="https";
54     
55     /* ------------------------------------------------------------ */
56     public final static String JavaDoc __HTTP_0_9 ="HTTP/0.9";
57     public final static String JavaDoc __HTTP_1_0 ="HTTP/1.0";
58     public final static String JavaDoc __HTTP_1_1 ="HTTP/1.1";
59     public final static String JavaDoc __HTTP_1_X ="HTTP/1.";
60
61     /* ------------------------------------------------------------ */
62     public interface HeaderWriter
63     {
64         void writeHeader(HttpMessage httpMessage)
65             throws IOException JavaDoc;
66     }
67     
68     /* ------------------------------------------------------------ */
69     /* ------------------------------------------------------------ */
70     /** Message States.
71      */

72     public final static int
73         __MSG_EDITABLE=0, // Created locally, all set methods enabled
74
__MSG_BAD=1, // Bad message/
75
__MSG_RECEIVED=2, // Received from connection.
76
__MSG_SENDING=3, // Headers sent.
77
__MSG_SENT=4; // Entity and trailers sent.
78

79     public final static String JavaDoc[] __state =
80     {
81         "EDITABLE",
82         "BAD",
83         "RECEIVED",
84         "SENDING",
85         "SENT"
86     };
87
88     /* ------------------------------------------------------------ */
89     protected int _state=__MSG_EDITABLE;
90     protected String JavaDoc _version;
91     protected int _dotVersion;
92     protected HttpFields _header=new HttpFields();
93     protected HttpConnection _connection;
94     protected String JavaDoc _characterEncoding;
95     protected String JavaDoc _mimeType;
96     protected Object JavaDoc _wrapper;
97     protected Map JavaDoc _attributes;
98
99     /* ------------------------------------------------------------ */
100     /** Constructor.
101      */

102     protected HttpMessage()
103     {}
104     
105     /* ------------------------------------------------------------ */
106     /** Constructor.
107      */

108     protected HttpMessage(HttpConnection connection)
109     {
110         _connection=connection;
111     }
112
113     /* ------------------------------------------------------------ */
114     /** Set a wrapper object.
115      * A wrapper object is an object associated with this message and
116      * presents it with an different interface. The
117      * primary example of a HttpRequest facade is ServletHttpRequest.
118      * A single facade object may be associated with the message with
119      * this call and retrieved with the getFacade method.
120      */

121     public void setWrapper(Object JavaDoc wrapper)
122     {
123         _wrapper=wrapper;
124     }
125
126     /* ------------------------------------------------------------ */
127     /** Get an associated wrapper object.
128      * @return Wrapper message or null.
129      */

130     public Object JavaDoc getWrapper()
131     {
132         return _wrapper;
133     }
134     
135     /* ------------------------------------------------------------ */
136     protected void reset()
137     {
138         _state=__MSG_EDITABLE;
139         _header.clear();
140     }
141     
142     /* ------------------------------------------------------------ */
143     public HttpConnection getHttpConnection()
144     {
145         return _connection;
146     }
147
148     /* ------------------------------------------------------------ */
149     public InputStream JavaDoc getInputStream()
150     {
151         if (_connection==null)
152             return null;
153         return _connection.getInputStream();
154     }
155     
156     /* ------------------------------------------------------------ */
157     public OutputStream JavaDoc getOutputStream()
158     {
159         if (_connection==null)
160             return null;
161         return _connection.getOutputStream();
162     }
163     
164     /* ------------------------------------------------------------ */
165     /** Get the message state.
166      * <PRE>
167      * __MSG_EDITABLE = 0 - Created locally, all set methods enabled
168      * __MSG_BAD = 1 - Bad message or send failure.
169      * __MSG_RECEIVED = 2 - Received from connection.
170      * __MSG_SENDING = 3 - Headers sent.
171      * __MSG_SENT = 4 - Entity and trailers sent.
172      * </PRE>
173      * @return the state.
174      */

175     public int getState()
176     {
177         return _state;
178     }
179     
180     /* ------------------------------------------------------------ */
181     /** Set the message state.
182      * This method should be used by experts only as it can prevent
183      * normal handling of a request/response.
184      * @param state The new state
185      * @return the last state.
186      */

187     public int setState(int state)
188     {
189         int last=_state;
190         _state=state;
191         return last;
192     }
193
194
195     /* ------------------------------------------------------------ */
196     /** Get the protocol version.
197      * @return return the version.
198      */

199     public String JavaDoc getVersion()
200     {
201         return _version;
202     }
203     /* ------------------------------------------------------------ */
204     /** Get the protocol version.
205      * @return return the version dot (0.9=-1 1.0=0 1.1=1)
206      */

207     public int getDotVersion()
208     {
209         return _dotVersion;
210     }
211
212     /* ------------------------------------------------------------ */
213     /** Get field names.
214      * @return Enumeration of Field Names
215      */

216     public Enumeration JavaDoc getFieldNames()
217     {
218         return _header.getFieldNames();
219     }
220
221     /* ------------------------------------------------------------ */
222     /** Does the header or trailer contain a field?
223      * @param name Name of the field
224      * @return True if contained in header or trailer.
225      */

226     public boolean containsField(String JavaDoc name)
227     {
228         return _header.containsKey(name);
229     }
230     
231     /* ------------------------------------------------------------ */
232     /** Get a message field.
233      * Get a field from a message header. If no header field is found,
234      * trailer fields are searched.
235      * @param name The field name
236      * @return field value or null
237      */

238     public String JavaDoc getField(String JavaDoc name)
239     {
240         return _header.get(name);
241     }
242     
243     /* ------------------------------------------------------------ */
244     /** Get a multi valued message field.
245      * Get a field from a message header.
246      * @param name The field name
247      * @return Enumeration of field values or null
248      */

249     public Enumeration JavaDoc getFieldValues(String JavaDoc name)
250     {
251         return _header.getValues(name);
252     }
253     
254     /* ------------------------------------------------------------ */
255     /** Get a multi valued message field.
256      * Get a field from a message header.
257      * @param name The field name
258      * @param separators String of separators.
259      * @return Enumeration of field values or null
260      */

261     public Enumeration JavaDoc getFieldValues(String JavaDoc name,String JavaDoc separators)
262     {
263         return _header.getValues(name,separators);
264     }
265     
266
267     /* ------------------------------------------------------------ */
268     /** Set a field value.
269      * If the message is editable, then a header field is set. Otherwise
270      * if the message is sending and a HTTP/1.1 version, then a trailer
271      * field is set.
272      * @param name Name of field
273      * @param value New value of field
274      * @return Old value of field
275      */

276     public String JavaDoc setField(String JavaDoc name, String JavaDoc value)
277     {
278         if (_state!=__MSG_EDITABLE)
279             return null;
280
281         if (HttpFields.__ContentType.equalsIgnoreCase(name))
282         {
283             String JavaDoc old=_header.get(name);
284             setContentType(value);
285             return old;
286         }
287         
288         return _header.put(name,value);
289     }
290     
291     /* ------------------------------------------------------------ */
292     /** Set a multi-value field value.
293      * If the message is editable, then a header field is set. Otherwise
294      * if the meesage is sending and a HTTP/1.1 version, then a trailer
295      * field is set.
296      * @param name Name of field
297      * @param value New values of field
298      */

299     public void setField(String JavaDoc name, List JavaDoc value)
300     {
301         if (_state!=__MSG_EDITABLE)
302             return;
303         _header.put(name,value);
304     }
305     
306     /* ------------------------------------------------------------ */
307     /** Add to a multi-value field value.
308      * If the message is editable, then a header field is set. Otherwise
309      * if the meesage is sending and a HTTP/1.1 version, then a trailer
310      * field is set.
311      * @param name Name of field
312      * @param value New value to add to the field
313      * @exception IllegalStateException Not editable or sending 1.1
314      * with trailers
315      */

316     public void addField(String JavaDoc name, String JavaDoc value)
317         throws IllegalStateException JavaDoc
318     {
319         if (_state!=__MSG_EDITABLE)
320             return;
321         _header.add(name,value);
322     }
323     
324     /* -------------------------------------------------------------- */
325     /** Get a field as an integer value.
326      * Look in header and trailer fields.
327      * Returns the value of an integer field, or -1 if not found.
328      * The case of the field name is ignored.
329      * @param name the case-insensitive field name
330      */

331     public int getIntField(String JavaDoc name)
332     {
333         return _header.getIntField(name);
334     }
335     
336     /* -------------------------------------------------------------- */
337     /** Sets the value of an integer field.
338      * Header or Trailer fields are set depending on message state.
339      * @param name the field name
340      * @param value the field integer value
341      */

342     public void setIntField(String JavaDoc name, int value)
343     {
344         if (_state!=__MSG_EDITABLE)
345             return;
346         _header.put(name, TypeUtil.toString(value));
347     }
348     
349     /* -------------------------------------------------------------- */
350     /** Adds the value of an integer field.
351      * Header or Trailer fields are set depending on message state.
352      * @param name the field name
353      * @param value the field integer value
354      */

355     public void addIntField(String JavaDoc name, int value)
356     {
357         if (_state!=__MSG_EDITABLE)
358             return;
359         _header.add(name, TypeUtil.toString(value));
360     }
361     
362     /* -------------------------------------------------------------- */
363     /** Get a header as a date value.
364      * Look in header and trailer fields.
365      * Returns the value of a date field, or -1 if not found.
366      * The case of the field name is ignored.
367      * @param name the case-insensitive field name
368      */

369     public long getDateField(String JavaDoc name)
370     {
371         return _header.getDateField(name);
372     }
373     
374
375     /* -------------------------------------------------------------- */
376     /** Sets the value of a date field.
377      * Header or Trailer fields are set depending on message state.
378      * @param name the field name
379      * @param date the field date value
380      */

381     public void setDateField(String JavaDoc name, Date JavaDoc date)
382     {
383         if (_state!=__MSG_EDITABLE)
384             return;
385         _header.putDateField(name,date);
386     }
387     
388     /* -------------------------------------------------------------- */
389     /** Adds the value of a date field.
390      * Header or Trailer fields are set depending on message state.
391      * @param name the field name
392      * @param date the field date value
393      */

394     public void addDateField(String JavaDoc name, Date JavaDoc date)
395     {
396         if (_state!=__MSG_EDITABLE)
397             return;
398         _header.addDateField(name,date);
399     }
400     
401     /* -------------------------------------------------------------- */
402     /** Sets the value of a date field.
403      * Header or Trailer fields are set depending on message state.
404      * @param name the field name
405      * @param date the field date value
406      */

407     public void setDateField(String JavaDoc name, long date)
408     {
409         if (_state!=__MSG_EDITABLE)
410             return;
411         _header.putDateField(name,date);
412     }
413     
414     /* -------------------------------------------------------------- */
415     /** Add the value of a date field.
416      * Header or Trailer fields are set depending on message state.
417      * @param name the field name
418      * @param date the field date value
419      * @exception IllegalStateException Not editable or sending 1.1
420      * with trailers
421      */

422     public void addDateField(String JavaDoc name, long date)
423     {
424         if (_state!=__MSG_EDITABLE)
425             return;
426         _header.addDateField(name,date);
427     }
428     
429
430     /* ------------------------------------------------------------ */
431     /** Remove a field.
432      * If the message is editable, then a header field is removed. Otherwise
433      * if the message is sending and a HTTP/1.1 version, then a trailer
434      * field is removed.
435      * @param name Name of field
436      * @return Old value of field
437      */

438     public String JavaDoc removeField(String JavaDoc name)
439         throws IllegalStateException JavaDoc
440     {
441         if (_state!=__MSG_EDITABLE)
442             return null;
443         return _header.remove(name);
444     }
445     
446     /* ------------------------------------------------------------ */
447     /** Set the request version
448      * @param version the HTTP version string (eg HTTP/1.1)
449      * @exception IllegalStateException message is not EDITABLE
450      */

451     public void setVersion(String JavaDoc version)
452     {
453         if (_state!=__MSG_EDITABLE)
454             throw new IllegalStateException JavaDoc("Not EDITABLE");
455         if (version.equalsIgnoreCase(__HTTP_1_1))
456         {
457             _dotVersion=1;
458             _version=__HTTP_1_1;
459         }
460         else if (version.equalsIgnoreCase(__HTTP_1_0))
461         {
462             _dotVersion=0;
463             _version=__HTTP_1_0;
464         }
465         else if (version.equalsIgnoreCase(__HTTP_0_9))
466         {
467             _dotVersion=-1;
468             _version=__HTTP_0_9;
469         }
470         else
471             throw new IllegalArgumentException JavaDoc("Unknown version");
472     }
473     
474     /* ------------------------------------------------------------ */
475     /** Get the HTTP header fields.
476      * @return Header or null
477      */

478     public HttpFields getHeader()
479     {
480         if (_state!=__MSG_EDITABLE)
481             throw new IllegalStateException JavaDoc("Can't get header in "+__state[_state]);
482         
483         return _header;
484     }
485     
486     
487     /* -------------------------------------------------------------- */
488     public int getContentLength()
489     {
490         return getIntField(HttpFields.__ContentLength);
491     }
492     
493     /* ------------------------------------------------------------ */
494     public void setContentLength(int len)
495     {
496         setIntField(HttpFields.__ContentLength,len);
497     }
498     
499     /* -------------------------------------------------------------- */
500     /** Character Encoding.
501      * The character encoding is extracted from the ContentType field
502      * when set.
503      * @return Character Encoding or null
504      */

505     public String JavaDoc getCharacterEncoding()
506     {
507         return _characterEncoding;
508     }
509     
510     /* ------------------------------------------------------------ */
511     /** Set Character Encoding.
512      * @param encoding An encoding that can override the encoding set
513      * from the ContentType field.
514      */

515     public void setCharacterEncoding(String JavaDoc encoding,boolean setField)
516     {
517         if (isCommitted())
518             return;
519         
520         if (encoding==null)
521         {
522             // Clear any encoding.
523
if (_characterEncoding!=null)
524             {
525                 _characterEncoding=null;
526                 if (setField)
527                     _header.put(HttpFields.__ContentType,_mimeType);
528             }
529         }
530         else
531         {
532             // No, so just add this one to the mimetype
533
_characterEncoding=encoding;
534             if (setField && _mimeType!=null)
535             {
536                     _header.put(HttpFields.__ContentType,
537                                 _mimeType+";charset="+
538                                 QuotedStringTokenizer.quote(_characterEncoding,";= "));
539             }
540         }
541     }
542     
543     /* -------------------------------------------------------------- */
544     public String JavaDoc getContentType()
545     {
546         return getField(HttpFields.__ContentType);
547     }
548     
549     /* ------------------------------------------------------------ */
550     public void setContentType(String JavaDoc contentType)
551     {
552         if (isCommitted())
553             return;
554         
555         if (contentType==null)
556         {
557             _mimeType=null;
558             _header.remove(HttpFields.__ContentType);
559         }
560         else
561         {
562             // Look for encoding in contentType
563
int i0=contentType.indexOf(';');
564             
565             if (i0>0)
566             {
567                 // Strip params off mimetype
568
_mimeType=contentType.substring(0,i0).trim();
569                 
570                 // Look for charset
571
int i1=contentType.indexOf("charset=",i0);
572                 if (i1>=0)
573                 {
574                     i1+=8;
575                     int i2 = contentType.indexOf(' ',i1);
576                     _characterEncoding = (0<i2)
577                     ? contentType.substring(i1,i2)
578                                     : contentType.substring(i1);
579                     _characterEncoding = QuotedStringTokenizer.unquote(_characterEncoding);
580                 }
581                 else // No encoding in the params.
582
{
583                     if (_characterEncoding!=null)
584                         // Add any previously set encoding.
585
contentType+=";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
586                 }
587             }
588             else // No encoding and no other params
589
{
590                 _mimeType=contentType;
591                 // Add any previously set encoding.
592
if (_characterEncoding!=null)
593                     contentType+=";charset="+QuotedStringTokenizer.quote(_characterEncoding,";= ");
594             }
595             
596             _header.put(HttpFields.__ContentType,contentType);
597         }
598     }
599     
600     /* ------------------------------------------------------------ */
601     public void updateMimeType()
602     {
603         _mimeType=null;
604         _characterEncoding=null;
605             
606         String JavaDoc contentType= _header.get(HttpFields.__ContentType);
607         if (contentType!=null)
608         {
609             // Look for encoding in contentType
610
int i0=contentType.indexOf(';');
611             
612             if (i0>0)
613             {
614                 // Strip params off mimetype
615
_mimeType=contentType.substring(0,i0).trim();
616
617                 // Look for charset
618
int i1=contentType.indexOf("charset=",i0);
619                 if (i1>=0)
620                 {
621                     i1+=8;
622                     int i2 = contentType.indexOf(' ',i1);
623                     _characterEncoding = (0<i2)
624                         ? contentType.substring(i1,i2)
625                         : contentType.substring(i1);
626                     _characterEncoding = QuotedStringTokenizer.unquote(_characterEncoding);
627                 }
628             }
629             else
630             {
631                 _mimeType=contentType;
632             }
633         }
634     }
635     
636     /* -------------------------------------------------------------- */
637     /** Mime Type.
638      * The mime type is extracted from the contenttype field when set.
639      * @return Content type without parameters
640      */

641     public String JavaDoc getMimeType()
642     {
643         return _mimeType;
644     }
645     
646     /* ------------------------------------------------------------ */
647     /** Recycle the message.
648      */

649     void recycle(HttpConnection connection)
650     {
651         _state=__MSG_EDITABLE;
652         _version=__HTTP_1_1;
653         _dotVersion=1;
654         _header.clear();
655         _connection=connection;
656         _characterEncoding=null;
657         _mimeType=null;
658         if (_attributes!=null)
659             _attributes.clear();
660     }
661     
662     /* ------------------------------------------------------------ */
663     /** Destroy the message.
664      * Help the garbage collector by nulling everything that we can.
665      */

666     public void destroy()
667     {
668         recycle(null);
669         if (_header!=null)
670             _header.destroy();
671         _header=null;
672     }
673     
674     /* ------------------------------------------------------------ */
675     /** Convert to String.
676      * The message header is converted to a String.
677      * @return String
678      */

679     public synchronized String JavaDoc toString()
680     {
681         StringWriter JavaDoc writer = new StringWriter JavaDoc();
682
683         int save_state=_state;
684         try{
685             _state=__MSG_EDITABLE;
686             writeHeader(writer);
687         }
688         catch(IOException JavaDoc e)
689         {
690             log.warn(LogSupport.EXCEPTION,e);
691         }
692         finally
693         {
694             _state=save_state;
695         }
696         return writer.toString();
697     }
698
699
700     /* ------------------------------------------------------------ */
701     /** Write the message header.
702      * @param writer
703      */

704     abstract void writeHeader(Writer JavaDoc writer)
705         throws IOException JavaDoc;
706
707
708     /* ------------------------------------------------------------ */
709     public boolean isCommitted()
710     {
711         return _state==__MSG_SENDING || _state==__MSG_SENT;
712     }
713     
714     /* ------------------------------------------------------------ */
715     /**
716      * @return true if the message has been modified.
717      */

718     public boolean isDirty()
719     {
720         HttpOutputStream out=(HttpOutputStream)getOutputStream();
721         return _state!=__MSG_EDITABLE || ( out!=null && out.isWritten());
722     }
723     
724     /* ------------------------------------------------------------ */
725     /** Get a request attribute.
726      * @param name Attribute name
727      * @return Attribute value
728      */

729     public Object JavaDoc getAttribute(String JavaDoc name)
730     {
731         if (_attributes==null)
732             return null;
733         return _attributes.get(name);
734     }
735
736     /* ------------------------------------------------------------ */
737     /** Set a request attribute.
738      * @param name Attribute name
739      * @param attribute Attribute value
740      * @return Previous Attribute value
741      */

742     public Object JavaDoc setAttribute(String JavaDoc name, Object JavaDoc attribute)
743     {
744         if (_attributes==null)
745             _attributes=new HashMap JavaDoc(11);
746         return _attributes.put(name,attribute);
747     }
748
749     /* ------------------------------------------------------------ */
750     /** Get Attribute names.
751      * @return Enumeration of Strings
752      */

753     public Enumeration JavaDoc getAttributeNames()
754     {
755         if (_attributes==null)
756             return Collections.enumeration(Collections.EMPTY_LIST);
757         return Collections.enumeration(_attributes.keySet());
758     }
759
760     /* ------------------------------------------------------------ */
761     /** Remove a request attribute.
762      * @param name Attribute name
763      */

764     public void removeAttribute(String JavaDoc name)
765     {
766         if (_attributes!=null)
767             _attributes.remove(name);
768     }
769 }
770
Popular Tags