1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.connector;
20
21
22 import java.io.InputStream;
23 import java.io.IOException;
24 import java.io.BufferedReader;
25 import java.io.UnsupportedEncodingException;
26 import java.security.Principal;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Enumeration;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.TimeZone;
35 import java.util.TreeMap;
36
37 import javax.security.auth.Subject;
38 import javax.servlet.FilterChain;
39 import javax.servlet.RequestDispatcher;
40 import javax.servlet.ServletContext;
41 import javax.servlet.ServletInputStream;
42 import javax.servlet.ServletRequestAttributeEvent;
43 import javax.servlet.ServletRequestAttributeListener;
44 import javax.servlet.http.Cookie;
45 import javax.servlet.http.HttpServletRequest;
46 import javax.servlet.http.HttpSession;
47
48 import org.apache.tomcat.util.buf.B2CConverter;
49 import org.apache.tomcat.util.buf.ByteChunk;
50 import org.apache.tomcat.util.buf.MessageBytes;
51 import org.apache.tomcat.util.buf.StringCache;
52 import org.apache.tomcat.util.http.Cookies;
53 import org.apache.tomcat.util.http.FastHttpDateFormat;
54 import org.apache.tomcat.util.http.Parameters;
55 import org.apache.tomcat.util.http.ServerCookie;
56 import org.apache.tomcat.util.http.mapper.MappingData;
57
58 import org.apache.coyote.ActionCode;
59
60 import org.apache.catalina.Context;
61 import org.apache.catalina.Globals;
62 import org.apache.catalina.Host;
63 import org.apache.catalina.Manager;
64 import org.apache.catalina.Realm;
65 import org.apache.catalina.Session;
66 import org.apache.catalina.Wrapper;
67 import org.apache.catalina.core.ApplicationFilterFactory;
68 import org.apache.catalina.realm.GenericPrincipal;
69 import org.apache.catalina.util.Enumerator;
70 import org.apache.catalina.util.ParameterMap;
71 import org.apache.catalina.util.RequestUtil;
72 import org.apache.catalina.util.StringManager;
73 import org.apache.catalina.util.StringParser;
74
75
76 /**
77 * Wrapper object for the Coyote request.
78 *
79 * @author Remy Maucherat
80 * @author Craig R. McClanahan
81 * @version $Revision: 892545 $ $Date: 2009-12-20 02:04:17 +0100 (Sun, 20 Dec 2009) $
82 */
83
84 public class Request
85 implements HttpServletRequest {
86
87
88 // ----------------------------------------------------------- Constructors
89
90
91 static {
92 // Ensure that classes are loaded for SM
93 new StringCache.ByteEntry();
94 new StringCache.CharEntry();
95 }
96
97 public Request() {
98
99 formats[0].setTimeZone(GMT_ZONE);
100 formats[1].setTimeZone(GMT_ZONE);
101 formats[2].setTimeZone(GMT_ZONE);
102
103 }
104
105
106 // ------------------------------------------------------------- Properties
107
108
109 /**
110 * Coyote request.
111 */
112 protected org.apache.coyote.Request coyoteRequest;
113
114 /**
115 * Set the Coyote request.
116 *
117 * @param coyoteRequest The Coyote request
118 */
119 public void setCoyoteRequest(org.apache.coyote.Request coyoteRequest) {
120 this.coyoteRequest = coyoteRequest;
121 inputBuffer.setRequest(coyoteRequest);
122 }
123
124 /**
125 * Get the Coyote request.
126 */
127 public org.apache.coyote.Request getCoyoteRequest() {
128 return (this.coyoteRequest);
129 }
130
131
132 // ----------------------------------------------------- Variables
133
134
135 protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
136
137
138 /**
139 * The string manager for this package.
140 */
141 protected static StringManager sm =
142 StringManager.getManager(Constants.Package);
143
144
145 /**
146 * The set of cookies associated with this Request.
147 */
148 protected Cookie[] cookies = null;
149
150
151 /**
152 * The set of SimpleDateFormat formats to use in getDateHeader().
153 *
154 * Notice that because SimpleDateFormat is not thread-safe, we can't
155 * declare formats[] as a static variable.
156 */
157 protected SimpleDateFormat formats[] = {
158 new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
159 new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
160 new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
161 };
162
163
164 /**
165 * The default Locale if none are specified.
166 */
167 protected static Locale defaultLocale = Locale.getDefault();
168
169
170 /**
171 * The attributes associated with this Request, keyed by attribute name.
172 */
173 protected HashMap attributes = new HashMap();
174
175
176 /**
177 * List of read only attributes for this Request.
178 */
179 private HashMap readOnlyAttributes = new HashMap();
180
181
182 /**
183 * The preferred Locales assocaited with this Request.
184 */
185 protected ArrayList locales = new ArrayList();
186
187
188 /**
189 * Internal notes associated with this request by Catalina components
190 * and event listeners.
191 */
192 private transient HashMap notes = new HashMap();
193
194
195 /**
196 * Authentication type.
197 */
198 protected String authType = null;
199
200
201 /**
202 * Associated event.
203 */
204 protected CometEventImpl event = null;
205
206
207 /**
208 * Comet state
209 */
210 protected boolean comet = false;
211
212
213 /**
214 * The current dispatcher type.
215 */
216 protected Object dispatcherType = null;
217
218
219 /**
220 * The associated input buffer.
221 */
222 protected InputBuffer inputBuffer = new InputBuffer();
223
224
225 /**
226 * ServletInputStream.
227 */
228 protected CoyoteInputStream inputStream =
229 new CoyoteInputStream(inputBuffer);
230
231
232 /**
233 * Reader.
234 */
235 protected CoyoteReader reader = new CoyoteReader(inputBuffer);
236
237
238 /**
239 * Using stream flag.
240 */
241 protected boolean usingInputStream = false;
242
243
244 /**
245 * Using writer flag.
246 */
247 protected boolean usingReader = false;
248
249
250 /**
251 * User principal.
252 */
253 protected Principal userPrincipal = null;
254
255
256 /**
257 * Session parsed flag.
258 */
259 protected boolean sessionParsed = false;
260
261
262 /**
263 * Request parameters parsed flag.
264 */
265 protected boolean parametersParsed = false;
266
267
268 /**
269 * Cookies parsed flag.
270 */
271 protected boolean cookiesParsed = false;
272
273
274 /**
275 * Secure flag.
276 */
277 protected boolean secure = false;
278
279
280 /**
281 * The Subject associated with the current AccessControllerContext
282 */
283 protected transient Subject subject = null;
284
285
286 /**
287 * Post data buffer.
288 */
289 protected static int CACHED_POST_LEN = 8192;
290 protected byte[] postData = null;
291
292
293 /**
294 * Hash map used in the getParametersMap method.
295 */
296 protected ParameterMap parameterMap = new ParameterMap();
297
298
299 /**
300 * The currently active session for this request.
301 */
302 protected Session session = null;
303
304
305 /**
306 * The current request dispatcher path.
307 */
308 protected Object requestDispatcherPath = null;
309
310
311 /**
312 * Was the requested session ID received in a cookie?
313 */
314 protected boolean requestedSessionCookie = false;
315
316
317 /**
318 * The requested session ID (if any) for this request.
319 */
320 protected String requestedSessionId = null;
321
322
323 /**
324 * Was the requested session ID received in a URL?
325 */
326 protected boolean requestedSessionURL = false;
327
328
329 /**
330 * Parse locales.
331 */
332 protected boolean localesParsed = false;
333
334
335 /**
336 * The string parser we will use for parsing request lines.
337 */
338 private StringParser parser = new StringParser();
339
340
341 /**
342 * Local port
343 */
344 protected int localPort = -1;
345
346 /**
347 * Remote address.
348 */
349 protected String remoteAddr = null;
350
351
352 /**
353 * Remote host.
354 */
355 protected String remoteHost = null;
356
357
358 /**
359 * Remote port
360 */
361 protected int remotePort = -1;
362
363 /**
364 * Local address
365 */
366 protected String localAddr = null;
367
368
369 /**
370 * Local address
371 */
372 protected String localName = null;
373
374
375 // --------------------------------------------------------- Public Methods
376
377
378 /**
379 * Release all object references, and initialize instance variables, in
380 * preparation for reuse of this object.
381 */
382 public void recycle() {
383
384 context = null;
385 wrapper = null;
386
387 dispatcherType = null;
388 requestDispatcherPath = null;
389
390 comet = false;
391 if (event != null) {
392 event.clear();
393 event = null;
394 }
395
396 authType = null;
397 inputBuffer.recycle();
398 usingInputStream = false;
399 usingReader = false;
400 userPrincipal = null;
401 subject = null;
402 sessionParsed = false;
403 parametersParsed = false;
404 cookiesParsed = false;
405 locales.clear();
406 localesParsed = false;
407 secure = false;
408 remoteAddr = null;
409 remoteHost = null;
410 remotePort = -1;
411 localPort = -1;
412 localAddr = null;
413 localName = null;
414
415 attributes.clear();
416 notes.clear();
417 cookies = null;
418
419 if (session != null) {
420 session.endAccess();
421 }
422 session = null;
423 requestedSessionCookie = false;
424 requestedSessionId = null;
425 requestedSessionURL = false;
426
427 if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
428 parameterMap = new ParameterMap();
429 } else {
430 parameterMap.setLocked(false);
431 parameterMap.clear();
432 }
433
434 mappingData.recycle();
435
436 if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
437 if (facade != null) {
438 facade.clear();
439 facade = null;
440 }
441 if (inputStream != null) {
442 inputStream.clear();
443 inputStream = null;
444 }
445 if (reader != null) {
446 reader.clear();
447 reader = null;
448 }
449 }
450
451 }
452
453
454 /**
455 * Clear cached encoders (to save memory for Comet requests).
456 */
457 public void clearEncoders() {
458 inputBuffer.clearEncoders();
459 }
460
461
462 /**
463 * Clear cached encoders (to save memory for Comet requests).
464 */
465 public boolean read()
466 throws IOException {
467 return (inputBuffer.realReadBytes(null, 0, 0) > 0);
468 }
469
470
471 // -------------------------------------------------------- Request Methods
472
473
474 /**
475 * Associated Catalina connector.
476 */
477 protected Connector connector;
478
479 /**
480 * Return the Connector through which this Request was received.
481 */
482 public Connector getConnector() {
483 return (this.connector);
484 }
485
486 /**
487 * Set the Connector through which this Request was received.
488 *
489 * @param connector The new connector
490 */
491 public void setConnector(Connector connector) {
492 this.connector = connector;
493 }
494
495
496 /**
497 * Associated context.
498 */
499 protected Context context = null;
500
501 /**
502 * Return the Context within which this Request is being processed.
503 */
504 public Context getContext() {
505 return (this.context);
506 }
507
508
509 /**
510 * Set the Context within which this Request is being processed. This
511 * must be called as soon as the appropriate Context is identified, because
512 * it identifies the value to be returned by <code>getContextPath()</code>,
513 * and thus enables parsing of the request URI.
514 *
515 * @param context The newly associated Context
516 */
517 public void setContext(Context context) {
518 this.context = context;
519 }
520
521
522 /**
523 * Filter chain associated with the request.
524 */
525 protected FilterChain filterChain = null;
526
527 /**
528 * Get filter chain associated with the request.
529 */
530 public FilterChain getFilterChain() {
531 return (this.filterChain);
532 }
533
534 /**
535 * Set filter chain associated with the request.
536 *
537 * @param filterChain new filter chain
538 */
539 public void setFilterChain(FilterChain filterChain) {
540 this.filterChain = filterChain;
541 }
542
543
544 /**
545 * Return the Host within which this Request is being processed.
546 */
547 public Host getHost() {
548 if (getContext() == null)
549 return null;
550 return (Host) getContext().getParent();
551 //return ((Host) mappingData.host);
552 }
553
554
555 /**
556 * Set the Host within which this Request is being processed. This
557 * must be called as soon as the appropriate Host is identified, and
558 * before the Request is passed to a context.
559 *
560 * @param host The newly associated Host
561 */
562 public void setHost(Host host) {
563 mappingData.host = host;
564 }
565
566
567 /**
568 * Descriptive information about this Request implementation.
569 */
570 protected static final String info =
571 "org.apache.coyote.catalina.CoyoteRequest/1.0";
572
573 /**
574 * Return descriptive information about this Request implementation and
575 * the corresponding version number, in the format
576 * <code><description>/<version></code>.
577 */
578 public String getInfo() {
579 return (info);
580 }
581
582
583 /**
584 * Mapping data.
585 */
586 protected MappingData mappingData = new MappingData();
587
588 /**
589 * Return mapping data.
590 */
591 public MappingData getMappingData() {
592 return (mappingData);
593 }
594
595
596 /**
597 * The facade associated with this request.
598 */
599 protected RequestFacade facade = null;
600
601 /**
602 * Return the <code>ServletRequest</code> for which this object
603 * is the facade. This method must be implemented by a subclass.
604 */
605 public HttpServletRequest getRequest() {
606 if (facade == null) {
607 facade = new RequestFacade(this);
608 }
609 return (facade);
610 }
611
612
613 /**
614 * The response with which this request is associated.
615 */
616 protected org.apache.catalina.connector.Response response = null;
617
618 /**
619 * Return the Response with which this Request is associated.
620 */
621 public org.apache.catalina.connector.Response getResponse() {
622 return (this.response);
623 }
624
625 /**
626 * Set the Response with which this Request is associated.
627 *
628 * @param response The new associated response
629 */
630 public void setResponse(org.apache.catalina.connector.Response response) {
631 this.response = response;
632 }
633
634 /**
635 * Return the input stream associated with this Request.
636 */
637 public InputStream getStream() {
638 if (inputStream == null) {
639 inputStream = new CoyoteInputStream(inputBuffer);
640 }
641 return inputStream;
642 }
643
644 /**
645 * Set the input stream associated with this Request.
646 *
647 * @param stream The new input stream
648 */
649 public void setStream(InputStream stream) {
650 // Ignore
651 }
652
653
654 /**
655 * URI byte to char converter (not recycled).
656 */
657 protected B2CConverter URIConverter = null;
658
659 /**
660 * Return the URI converter.
661 */
662 protected B2CConverter getURIConverter() {
663 return URIConverter;
664 }
665
666 /**
667 * Set the URI converter.
668 *
669 * @param URIConverter the new URI connverter
670 */
671 protected void setURIConverter(B2CConverter URIConverter) {
672 this.URIConverter = URIConverter;
673 }
674
675
676 /**
677 * Associated wrapper.
678 */
679 protected Wrapper wrapper = null;
680
681 /**
682 * Return the Wrapper within which this Request is being processed.
683 */
684 public Wrapper getWrapper() {
685 return (this.wrapper);
686 }
687
688
689 /**
690 * Set the Wrapper within which this Request is being processed. This
691 * must be called as soon as the appropriate Wrapper is identified, and
692 * before the Request is ultimately passed to an application servlet.
693 * @param wrapper The newly associated Wrapper
694 */
695 public void setWrapper(Wrapper wrapper) {
696 this.wrapper = wrapper;
697 }
698
699
700 // ------------------------------------------------- Request Public Methods
701
702
703 /**
704 * Create and return a ServletInputStream to read the content
705 * associated with this Request.
706 *
707 * @exception IOException if an input/output error occurs
708 */
709 public ServletInputStream createInputStream()
710 throws IOException {
711 if (inputStream == null) {
712 inputStream = new CoyoteInputStream(inputBuffer);
713 }
714 return inputStream;
715 }
716
717
718 /**
719 * Perform whatever actions are required to flush and close the input
720 * stream or reader, in a single operation.
721 *
722 * @exception IOException if an input/output error occurs
723 */
724 public void finishRequest() throws IOException {
725 // The reader and input stream don't need to be closed
726 }
727
728
729 /**
730 * Return the object bound with the specified name to the internal notes
731 * for this request, or <code>null</code> if no such binding exists.
732 *
733 * @param name Name of the note to be returned
734 */
735 public Object getNote(String name) {
736 return (notes.get(name));
737 }
738
739
740 /**
741 * Return an Iterator containing the String names of all notes bindings
742 * that exist for this request.
743 */
744 public Iterator getNoteNames() {
745 return (notes.keySet().iterator());
746 }
747
748
749 /**
750 * Remove any object bound to the specified name in the internal notes
751 * for this request.
752 *
753 * @param name Name of the note to be removed
754 */
755 public void removeNote(String name) {
756 notes.remove(name);
757 }
758
759
760 /**
761 * Bind an object to a specified name in the internal notes associated
762 * with this request, replacing any existing binding for this name.
763 *
764 * @param name Name to which the object should be bound
765 * @param value Object to be bound to the specified name
766 */
767 public void setNote(String name, Object value) {
768 notes.put(name, value);
769 }
770
771
772 /**
773 * Set the content length associated with this Request.
774 *
775 * @param length The new content length
776 */
777 public void setContentLength(int length) {
778 // Not used
779 }
780
781
782 /**
783 * Set the content type (and optionally the character encoding)
784 * associated with this Request. For example,
785 * <code>text/html; charset=ISO-8859-4</code>.
786 *
787 * @param type The new content type
788 */
789 public void setContentType(String type) {
790 // Not used
791 }
792
793
794 /**
795 * Set the protocol name and version associated with this Request.
796 *
797 * @param protocol Protocol name and version
798 */
799 public void setProtocol(String protocol) {
800 // Not used
801 }
802
803
804 /**
805 * Set the IP address of the remote client associated with this Request.
806 *
807 * @param remoteAddr The remote IP address
808 */
809 public void setRemoteAddr(String remoteAddr) {
810 this.remoteAddr = remoteAddr;
811 }
812
813
814 /**
815 * Set the fully qualified name of the remote client associated with this
816 * Request.
817 *
818 * @param remoteHost The remote host name
819 */
820 public void setRemoteHost(String remoteHost) {
821 this.remoteHost = remoteHost;
822 }
823
824
825 /**
826 * Set the name of the scheme associated with this request. Typical values
827 * are <code>http</code>, <code>https</code>, and <code>ftp</code>.
828 *
829 * @param scheme The scheme
830 */
831 public void setScheme(String scheme) {
832 // Not used
833 }
834
835
836 /**
837 * Set the value to be returned by <code>isSecure()</code>
838 * for this Request.
839 *
840 * @param secure The new isSecure value
841 */
842 public void setSecure(boolean secure) {
843 this.secure = secure;
844 }
845
846
847 /**
848 * Set the name of the server (virtual host) to process this request.
849 *
850 * @param name The server name
851 */
852 public void setServerName(String name) {
853 coyoteRequest.serverName().setString(name);
854 }
855
856
857 /**
858 * Set the port number of the server to process this request.
859 *
860 * @param port The server port
861 */
862 public void setServerPort(int port) {
863 coyoteRequest.setServerPort(port);
864 }
865
866
867 // ------------------------------------------------- ServletRequest Methods
868
869
870 /**
871 * Return the specified request attribute if it exists; otherwise, return
872 * <code>null</code>.
873 *
874 * @param name Name of the request attribute to return
875 */
876 public Object getAttribute(String name) {
877
878 if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
879 return (dispatcherType == null)
880 ? ApplicationFilterFactory.REQUEST_INTEGER
881 : dispatcherType;
882 } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
883 return (requestDispatcherPath == null)
884 ? getRequestPathMB().toString()
885 : requestDispatcherPath.toString();
886 }
887
888 Object attr=attributes.get(name);
889
890 if(attr!=null)
891 return(attr);
892
893 attr = coyoteRequest.getAttribute(name);
894 if(attr != null)
895 return attr;
896 if( isSSLAttribute(name) ) {
897 coyoteRequest.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE,
898 coyoteRequest);
899 attr = coyoteRequest.getAttribute(Globals.CERTIFICATES_ATTR);
900 if( attr != null) {
901 attributes.put(Globals.CERTIFICATES_ATTR, attr);
902 }
903 attr = coyoteRequest.getAttribute(Globals.CIPHER_SUITE_ATTR);
904 if(attr != null) {
905 attributes.put(Globals.CIPHER_SUITE_ATTR, attr);
906 }
907 attr = coyoteRequest.getAttribute(Globals.KEY_SIZE_ATTR);
908 if(attr != null) {
909 attributes.put(Globals.KEY_SIZE_ATTR, attr);
910 }
911 attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_ID_ATTR);
912 if(attr != null) {
913 attributes.put(Globals.SSL_SESSION_ID_ATTR, attr);
914 }
915 attr = attributes.get(name);
916 }
917 return attr;
918 }
919
920
921 /**
922 * Test if a given name is one of the special Servlet-spec SSL attributes.
923 */
924 static boolean isSSLAttribute(String name) {
925 return Globals.CERTIFICATES_ATTR.equals(name) ||
926 Globals.CIPHER_SUITE_ATTR.equals(name) ||
927 Globals.KEY_SIZE_ATTR.equals(name) ||
928 Globals.SSL_SESSION_ID_ATTR.equals(name);
929 }
930
931 /**
932 * Return the names of all request attributes for this Request, or an
933 * empty <code>Enumeration</code> if there are none. Note that the attribute
934 * names return will only be those for the attributes set via
935 * {@link #setAttribute(String, Object)}. Tomcat internal attributes will
936 * not be included although they are accessible via
937 * {@link #getAttribute(String)}. The Tomcat internal attributes include:
938 * <ul>
939 * <li>{@link Globals.DISPATCHER_TYPE_ATTR}</li>
940 * <li>{@link Globals.DISPATCHER_REQUEST_PATH_ATTR}</li>
941 * <li>{@link Globals.ASYNC_SUPPORTED_ATTR}</li>
942 * <li>{@link Globals.CERTIFICATES_ATTR} (SSL connections only)</li>
943 * <li>{@link Globals.CIPHER_SUITE_ATTR} (SSL connections only)</li>
944 * <li>{@link Globals.KEY_SIZE_ATTR} (SSL connections only)</li>
945 * <li>{@link Globals.SSL_SESSION_ID_ATTR} (SSL connections only)</li>
946 * <li>{@link Globals.SSL_SESSION_MGR_ATTR} (SSL connections only)</li>
947 * </ul>
948 * The underlying connector may also expose request attributes. These all
949 * have names starting with "org.apache.tomcat" and include:
950 * <ul>
951 * <li>org.apache.tomcat.sendfile.support</li>
952 * <li>org.apache.tomcat.comet.support</li>
953 * <li>org.apache.tomcat.comet.timeout.support</li>
954 * </ul>
955 * Connector implementations may return some, all or none of these
956 * attributes and may also support additional attributes.
957 */
958 public Enumeration getAttributeNames() {
959 if (isSecure()) {
960 getAttribute(Globals.CERTIFICATES_ATTR);
961 }
962 return new Enumerator(attributes.keySet(), true);
963 }
964
965
966 /**
967 * Return the character encoding for this Request.
968 */
969 public String getCharacterEncoding() {
970 return (coyoteRequest.getCharacterEncoding());
971 }
972
973
974 /**
975 * Return the content length for this Request.
976 */
977 public int getContentLength() {
978 return (coyoteRequest.getContentLength());
979 }
980
981
982 /**
983 * Return the content type for this Request.
984 */
985 public String getContentType() {
986 return (coyoteRequest.getContentType());
987 }
988
989
990 /**
991 * Return the servlet input stream for this Request. The default
992 * implementation returns a servlet input stream created by
993 * <code>createInputStream()</code>.
994 *
995 * @exception IllegalStateException if <code>getReader()</code> has
996 * already been called for this request
997 * @exception IOException if an input/output error occurs
998 */
999 public ServletInputStream getInputStream() throws IOException {
1000
1001 if (usingReader)
1002 throw new IllegalStateException
1003 (sm.getString("coyoteRequest.getInputStream.ise"));
1004
1005 usingInputStream = true;
1006 if (inputStream == null) {
1007 inputStream = new CoyoteInputStream(inputBuffer);
1008 }
1009 return inputStream;
1010
1011 }
1012
1013
1014 /**
1015 * Return the preferred Locale that the client will accept content in,
1016 * based on the value for the first <code>Accept-Language</code> header
1017 * that was encountered. If the request did not specify a preferred
1018 * language, the server's default Locale is returned.
1019 */
1020 public Locale getLocale() {
1021
1022 if (!localesParsed)
1023 parseLocales();
1024
1025 if (locales.size() > 0) {
1026 return ((Locale) locales.get(0));
1027 } else {
1028 return (defaultLocale);
1029 }
1030
1031 }
1032
1033
1034 /**
1035 * Return the set of preferred Locales that the client will accept
1036 * content in, based on the values for any <code>Accept-Language</code>
1037 * headers that were encountered. If the request did not specify a
1038 * preferred language, the server's default Locale is returned.
1039 */
1040 public Enumeration getLocales() {
1041
1042 if (!localesParsed)
1043 parseLocales();
1044
1045 if (locales.size() > 0)
1046 return (new Enumerator(locales));
1047 ArrayList results = new ArrayList();
1048 results.add(defaultLocale);
1049 return (new Enumerator(results));
1050
1051 }
1052
1053
1054 /**
1055 * Return the value of the specified request parameter, if any; otherwise,
1056 * return <code>null</code>. If there is more than one value defined,
1057 * return only the first one.
1058 *
1059 * @param name Name of the desired request parameter
1060 */
1061 public String getParameter(String name) {
1062
1063 if (!parametersParsed)
1064 parseParameters();
1065
1066 return coyoteRequest.getParameters().getParameter(name);
1067
1068 }
1069
1070
1071
1072 /**
1073 * Returns a <code>Map</code> of the parameters of this request.
1074 * Request parameters are extra information sent with the request.
1075 * For HTTP servlets, parameters are contained in the query string
1076 * or posted form data.
1077 *
1078 * @return A <code>Map</code> containing parameter names as keys
1079 * and parameter values as map values.
1080 */
1081 public Map getParameterMap() {
1082
1083 if (parameterMap.isLocked())
1084 return parameterMap;
1085
1086 Enumeration enumeration = getParameterNames();
1087 while (enumeration.hasMoreElements()) {
1088 String name = enumeration.nextElement().toString();
1089 String[] values = getParameterValues(name);
1090 parameterMap.put(name, values);
1091 }
1092
1093 parameterMap.setLocked(true);
1094
1095 return parameterMap;
1096
1097 }
1098
1099
1100 /**
1101 * Return the names of all defined request parameters for this request.
1102 */
1103 public Enumeration getParameterNames() {
1104
1105 if (!parametersParsed)
1106 parseParameters();
1107
1108 return coyoteRequest.getParameters().getParameterNames();
1109
1110 }
1111
1112
1113 /**
1114 * Return the defined values for the specified request parameter, if any;
1115 * otherwise, return <code>null</code>.
1116 *
1117 * @param name Name of the desired request parameter
1118 */
1119 public String[] getParameterValues(String name) {
1120
1121 if (!parametersParsed)
1122 parseParameters();
1123
1124 return coyoteRequest.getParameters().getParameterValues(name);
1125
1126 }
1127
1128
1129 /**
1130 * Return the protocol and version used to make this Request.
1131 */
1132 public String getProtocol() {
1133 return coyoteRequest.protocol().toString();
1134 }
1135
1136
1137 /**
1138 * Read the Reader wrapping the input stream for this Request. The
1139 * default implementation wraps a <code>BufferedReader</code> around the
1140 * servlet input stream returned by <code>createInputStream()</code>.
1141 *
1142 * @exception IllegalStateException if <code>getInputStream()</code>
1143 * has already been called for this request
1144 * @exception IOException if an input/output error occurs
1145 */
1146 public BufferedReader getReader() throws IOException {
1147
1148 if (usingInputStream)
1149 throw new IllegalStateException
1150 (sm.getString("coyoteRequest.getReader.ise"));
1151
1152 usingReader = true;
1153 inputBuffer.checkConverter();
1154 if (reader == null) {
1155 reader = new CoyoteReader(inputBuffer);
1156 }
1157 return reader;
1158
1159 }
1160
1161
1162 /**
1163 * Return the real path of the specified virtual path.
1164 *
1165 * @param path Path to be translated
1166 *
1167 * @deprecated As of version 2.1 of the Java Servlet API, use
1168 * <code>ServletContext.getRealPath()</code>.
1169 */
1170 public String getRealPath(String path) {
1171
1172 if (context == null)
1173 return (null);
1174 ServletContext servletContext = context.getServletContext();
1175 if (servletContext == null)
1176 return (null);
1177 else {
1178 try {
1179 return (servletContext.getRealPath(path));
1180 } catch (IllegalArgumentException e) {
1181 return (null);
1182 }
1183 }
1184
1185 }
1186
1187
1188 /**
1189 * Return the remote IP address making this Request.
1190 */
1191 public String getRemoteAddr() {
1192 if (remoteAddr == null) {
1193 coyoteRequest.action
1194 (ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, coyoteRequest);
1195 remoteAddr = coyoteRequest.remoteAddr().toString();
1196 }
1197 return remoteAddr;
1198 }
1199
1200
1201 /**
1202 * Return the remote host name making this Request.
1203 */
1204 public String getRemoteHost() {
1205 if (remoteHost == null) {
1206 if (!connector.getEnableLookups()) {
1207 remoteHost = getRemoteAddr();
1208 } else {
1209 coyoteRequest.action
1210 (ActionCode.ACTION_REQ_HOST_ATTRIBUTE, coyoteRequest);
1211 remoteHost = coyoteRequest.remoteHost().toString();
1212 }
1213 }
1214 return remoteHost;
1215 }
1216
1217 /**
1218 * Returns the Internet Protocol (IP) source port of the client
1219 * or last proxy that sent the request.
1220 */
1221 public int getRemotePort(){
1222 if (remotePort == -1) {
1223 coyoteRequest.action
1224 (ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE, coyoteRequest);
1225 remotePort = coyoteRequest.getRemotePort();
1226 }
1227 return remotePort;
1228 }
1229
1230 /**
1231 * Returns the host name of the Internet Protocol (IP) interface on
1232 * which the request was received.
1233 */
1234 public String getLocalName(){
1235 if (localName == null) {
1236 coyoteRequest.action
1237 (ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE, coyoteRequest);
1238 localName = coyoteRequest.localName().toString();
1239 }
1240 return localName;
1241 }
1242
1243 /**
1244 * Returns the Internet Protocol (IP) address of the interface on
1245 * which the request was received.
1246 */
1247 public String getLocalAddr(){
1248 if (localAddr == null) {
1249 coyoteRequest.action
1250 (ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE, coyoteRequest);
1251 localAddr = coyoteRequest.localAddr().toString();
1252 }
1253 return localAddr;
1254 }
1255
1256
1257 /**
1258 * Returns the Internet Protocol (IP) port number of the interface
1259 * on which the request was received.
1260 */
1261 public int getLocalPort(){
1262 if (localPort == -1){
1263 coyoteRequest.action
1264 (ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE, coyoteRequest);
1265 localPort = coyoteRequest.getLocalPort();
1266 }
1267 return localPort;
1268 }
1269
1270 /**
1271 * Return a RequestDispatcher that wraps the resource at the specified
1272 * path, which may be interpreted as relative to the current request path.
1273 *
1274 * @param path Path of the resource to be wrapped
1275 */
1276 public RequestDispatcher getRequestDispatcher(String path) {
1277
1278 if (context == null)
1279 return (null);
1280
1281 // If the path is already context-relative, just pass it through
1282 if (path == null)
1283 return (null);
1284 else if (path.startsWith("/"))
1285 return (context.getServletContext().getRequestDispatcher(path));
1286
1287 // Convert a request-relative path to a context-relative one
1288 String servletPath = (String) getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
1289 if (servletPath == null)
1290 servletPath = getServletPath();
1291
1292 // Add the path info, if there is any
1293 String pathInfo = getPathInfo();
1294 String requestPath = null;
1295
1296 if (pathInfo == null) {
1297 requestPath = servletPath;
1298 } else {
1299 requestPath = servletPath + pathInfo;
1300 }
1301
1302 int pos = requestPath.lastIndexOf('/');
1303 String relative = null;
1304 if (pos >= 0) {
1305 relative = requestPath.substring(0, pos + 1) + path;
1306 } else {
1307 relative = requestPath + path;
1308 }
1309
1310 return (context.getServletContext().getRequestDispatcher(relative));
1311
1312 }
1313
1314
1315 /**
1316 * Return the scheme used to make this Request.
1317 */
1318 public String getScheme() {
1319 return (coyoteRequest.scheme().toString());
1320 }
1321
1322
1323 /**
1324 * Return the server name responding to this Request.
1325 */
1326 public String getServerName() {
1327 return (coyoteRequest.serverName().toString());
1328 }
1329
1330
1331 /**
1332 * Return the server port responding to this Request.
1333 */
1334 public int getServerPort() {
1335 return (coyoteRequest.getServerPort());
1336 }
1337
1338
1339 /**
1340 * Was this request received on a secure connection?
1341 */
1342 public boolean isSecure() {
1343 return (secure);
1344 }
1345
1346
1347 /**
1348 * Remove the specified request attribute if it exists.
1349 *
1350 * @param name Name of the request attribute to remove
1351 */
1352 public void removeAttribute(String name) {
1353 Object value = null;
1354 boolean found = false;
1355
1356 // Remove the specified attribute
1357 // Check for read only attribute
1358 // requests are per thread so synchronization unnecessary
1359 if (readOnlyAttributes.containsKey(name)) {
1360 return;
1361 }
1362
1363 // Pass special attributes to the native layer
1364 if (name.startsWith("org.apache.tomcat.")) {
1365 coyoteRequest.getAttributes().remove(name);
1366 }
1367
1368 found = attributes.containsKey(name);
1369 if (found) {
1370 value = attributes.get(name);
1371 attributes.remove(name);
1372 } else {
1373 return;
1374 }
1375
1376 // Notify interested application event listeners
1377 Object listeners[] = context.getApplicationEventListeners();
1378 if ((listeners == null) || (listeners.length == 0))
1379 return;
1380 ServletRequestAttributeEvent event =
1381 new ServletRequestAttributeEvent(context.getServletContext(),
1382 getRequest(), name, value);
1383 for (int i = 0; i < listeners.length; i++) {
1384 if (!(listeners[i] instanceof ServletRequestAttributeListener))
1385 continue;
1386 ServletRequestAttributeListener listener =
1387 (ServletRequestAttributeListener) listeners[i];
1388 try {
1389 listener.attributeRemoved(event);
1390 } catch (Throwable t) {
1391 context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);
1392 // Error valve will pick this execption up and display it to user
1393 attributes.put( Globals.EXCEPTION_ATTR, t );
1394 }
1395 }
1396 }
1397
1398
1399 /**
1400 * Set the specified request attribute to the specified value.
1401 *
1402 * @param name Name of the request attribute to set
1403 * @param value The associated value
1404 */
1405 public void setAttribute(String name, Object value) {
1406
1407 // Name cannot be null
1408 if (name == null)
1409 throw new IllegalArgumentException
1410 (sm.getString("coyoteRequest.setAttribute.namenull"));
1411
1412 // Null value is the same as removeAttribute()
1413 if (value == null) {
1414 removeAttribute(name);
1415 return;
1416 }
1417
1418 if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
1419 dispatcherType = value;
1420 return;
1421 } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
1422 requestDispatcherPath = value;
1423 return;
1424 }
1425
1426 Object oldValue = null;
1427 boolean replaced = false;
1428
1429 // Add or replace the specified attribute
1430 // Check for read only attribute
1431 // requests are per thread so synchronization unnecessary
1432 if (readOnlyAttributes.containsKey(name)) {
1433 return;
1434 }
1435
1436 oldValue = attributes.put(name, value);
1437 if (oldValue != null) {
1438 replaced = true;
1439 }
1440
1441 // Pass special attributes to the native layer
1442 if (name.startsWith("org.apache.tomcat.")) {
1443 coyoteRequest.setAttribute(name, value);
1444 }
1445
1446 // Notify interested application event listeners
1447 Object listeners[] = context.getApplicationEventListeners();
1448 if ((listeners == null) || (listeners.length == 0))
1449 return;
1450 ServletRequestAttributeEvent event = null;
1451 if (replaced)
1452 event =
1453 new ServletRequestAttributeEvent(context.getServletContext(),
1454 getRequest(), name, oldValue);
1455 else
1456 event =
1457 new ServletRequestAttributeEvent(context.getServletContext(),
1458 getRequest(), name, value);
1459
1460 for (int i = 0; i < listeners.length; i++) {
1461 if (!(listeners[i] instanceof ServletRequestAttributeListener))
1462 continue;
1463 ServletRequestAttributeListener listener =
1464 (ServletRequestAttributeListener) listeners[i];
1465 try {
1466 if (replaced) {
1467 listener.attributeReplaced(event);
1468 } else {
1469 listener.attributeAdded(event);
1470 }
1471 } catch (Throwable t) {
1472 context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t);
1473 // Error valve will pick this execption up and display it to user
1474 attributes.put( Globals.EXCEPTION_ATTR, t );
1475 }
1476 }
1477 }
1478
1479
1480 /**
1481 * Overrides the name of the character encoding used in the body of
1482 * this request. This method must be called prior to reading request
1483 * parameters or reading input using <code>getReader()</code>.
1484 *
1485 * @param enc The character encoding to be used
1486 *
1487 * @exception UnsupportedEncodingException if the specified encoding
1488 * is not supported
1489 *
1490 * @since Servlet 2.3
1491 */
1492 public void setCharacterEncoding(String enc)
1493 throws UnsupportedEncodingException {
1494
1495 if (usingReader)
1496 return;
1497
1498 // Ensure that the specified encoding is valid
1499 byte buffer[] = new byte[1];
1500 buffer[0] = (byte) 'a';
1501 String dummy = new String(buffer, enc);
1502
1503 // Save the validated encoding
1504 coyoteRequest.setCharacterEncoding(enc);
1505
1506 }
1507
1508
1509 // ---------------------------------------------------- HttpRequest Methods
1510
1511
1512 /**
1513 * Add a Cookie to the set of Cookies associated with this Request.
1514 *
1515 * @param cookie The new cookie
1516 */
1517 public void addCookie(Cookie cookie) {
1518
1519 if (!cookiesParsed)
1520 parseCookies();
1521
1522 int size = 0;
1523 if (cookies != null) {
1524 size = cookies.length;
1525 }
1526
1527 Cookie[] newCookies = new Cookie[size + 1];
1528 for (int i = 0; i < size; i++) {
1529 newCookies[i] = cookies[i];
1530 }
1531 newCookies[size] = cookie;
1532
1533 cookies = newCookies;
1534
1535 }
1536
1537
1538 /**
1539 * Add a Header to the set of Headers associated with this Request.
1540 *
1541 * @param name The new header name
1542 * @param value The new header value
1543 */
1544 public void addHeader(String name, String value) {
1545 // Not used
1546 }
1547
1548
1549 /**
1550 * Add a Locale to the set of preferred Locales for this Request. The
1551 * first added Locale will be the first one returned by getLocales().
1552 *
1553 * @param locale The new preferred Locale
1554 */
1555 public void addLocale(Locale locale) {
1556 locales.add(locale);
1557 }
1558
1559
1560 /**
1561 * Add a parameter name and corresponding set of values to this Request.
1562 * (This is used when restoring the original request on a form based
1563 * login).
1564 *
1565 * @param name Name of this request parameter
1566 * @param values Corresponding values for this request parameter
1567 */
1568 public void addParameter(String name, String values[]) {
1569 coyoteRequest.getParameters().addParameterValues(name, values);
1570 }
1571
1572
1573 /**
1574 * Clear the collection of Cookies associated with this Request.
1575 */
1576 public void clearCookies() {
1577 cookiesParsed = true;
1578 cookies = null;
1579 }
1580
1581
1582 /**
1583 * Clear the collection of Headers associated with this Request.
1584 */
1585 public void clearHeaders() {
1586 // Not used
1587 }
1588
1589
1590 /**
1591 * Clear the collection of Locales associated with this Request.
1592 */
1593 public void clearLocales() {
1594 locales.clear();
1595 }
1596
1597
1598 /**
1599 * Clear the collection of parameters associated with this Request.
1600 */
1601 public void clearParameters() {
1602 // Not used
1603 }
1604
1605
1606 /**
1607 * Set the authentication type used for this request, if any; otherwise
1608 * set the type to <code>null</code>. Typical values are "BASIC",
1609 * "DIGEST", or "SSL".
1610 *
1611 * @param type The authentication type used
1612 */
1613 public void setAuthType(String type) {
1614 this.authType = type;
1615 }
1616
1617
1618 /**
1619 * Set the context path for this Request. This will normally be called
1620 * when the associated Context is mapping the Request to a particular
1621 * Wrapper.
1622 *
1623 * @param path The context path
1624 */
1625 public void setContextPath(String path) {
1626
1627 if (path == null) {
1628 mappingData.contextPath.setString("");
1629 } else {
1630 mappingData.contextPath.setString(path);
1631 }
1632
1633 }
1634
1635
1636 /**
1637 * Set the HTTP request method used for this Request.
1638 *
1639 * @param method The request method
1640 */
1641 public void setMethod(String method) {
1642 // Not used
1643 }
1644
1645
1646 /**
1647 * Set the query string for this Request. This will normally be called
1648 * by the HTTP Connector, when it parses the request headers.
1649 *
1650 * @param query The query string
1651 */
1652 public void setQueryString(String query) {
1653 // Not used
1654 }
1655
1656
1657 /**
1658 * Set the path information for this Request. This will normally be called
1659 * when the associated Context is mapping the Request to a particular
1660 * Wrapper.
1661 *
1662 * @param path The path information
1663 */
1664 public void setPathInfo(String path) {
1665 mappingData.pathInfo.setString(path);
1666 }
1667
1668
1669 /**
1670 * Set a flag indicating whether or not the requested session ID for this
1671 * request came in through a cookie. This is normally called by the
1672 * HTTP Connector, when it parses the request headers.
1673 *
1674 * @param flag The new flag
1675 */
1676 public void setRequestedSessionCookie(boolean flag) {
1677
1678 this.requestedSessionCookie = flag;
1679
1680 }
1681
1682
1683 /**
1684 * Set the requested session ID for this request. This is normally called
1685 * by the HTTP Connector, when it parses the request headers.
1686 *
1687 * @param id The new session id
1688 */
1689 public void setRequestedSessionId(String id) {
1690
1691 this.requestedSessionId = id;
1692
1693 }
1694
1695
1696 /**
1697 * Set a flag indicating whether or not the requested session ID for this
1698 * request came in through a URL. This is normally called by the
1699 * HTTP Connector, when it parses the request headers.
1700 *
1701 * @param flag The new flag
1702 */
1703 public void setRequestedSessionURL(boolean flag) {
1704
1705 this.requestedSessionURL = flag;
1706
1707 }
1708
1709
1710 /**
1711 * Set the unparsed request URI for this Request. This will normally be
1712 * called by the HTTP Connector, when it parses the request headers.
1713 *
1714 * @param uri The request URI
1715 */
1716 public void setRequestURI(String uri) {
1717 // Not used
1718 }
1719
1720
1721 /**
1722 * Set the decoded request URI.
1723 *
1724 * @param uri The decoded request URI
1725 */
1726 public void setDecodedRequestURI(String uri) {
1727 // Not used
1728 }
1729
1730
1731 /**
1732 * Get the decoded request URI.
1733 *
1734 * @return the URL decoded request URI
1735 */
1736 public String getDecodedRequestURI() {
1737 return (coyoteRequest.decodedURI().toString());
1738 }
1739
1740
1741 /**
1742 * Get the decoded request URI.
1743 *
1744 * @return the URL decoded request URI
1745 */
1746 public MessageBytes getDecodedRequestURIMB() {
1747 return (coyoteRequest.decodedURI());
1748 }
1749
1750
1751 /**
1752 * Set the servlet path for this Request. This will normally be called
1753 * when the associated Context is mapping the Request to a particular
1754 * Wrapper.
1755 *
1756 * @param path The servlet path
1757 */
1758 public void setServletPath(String path) {
1759 if (path != null)
1760 mappingData.wrapperPath.setString(path);
1761 }
1762
1763
1764 /**
1765 * Set the Principal who has been authenticated for this Request. This
1766 * value is also used to calculate the value to be returned by the
1767 * <code>getRemoteUser()</code> method.
1768 *
1769 * @param principal The user Principal
1770 */
1771 public void setUserPrincipal(Principal principal) {
1772
1773 if (Globals.IS_SECURITY_ENABLED){
1774 HttpSession session = getSession(false);
1775 if ( (subject != null) &&
1776 (!subject.getPrincipals().contains(principal)) ){
1777 subject.getPrincipals().add(principal);
1778 } else if (session != null &&
1779 session.getAttribute(Globals.SUBJECT_ATTR) == null) {
1780 subject = new Subject();
1781 subject.getPrincipals().add(principal);
1782 }
1783 if (session != null){
1784 session.setAttribute(Globals.SUBJECT_ATTR, subject);
1785 }
1786 }
1787
1788 this.userPrincipal = principal;
1789 }
1790
1791
1792 // --------------------------------------------- HttpServletRequest Methods
1793
1794
1795 /**
1796 * Return the authentication type used for this Request.
1797 */
1798 public String getAuthType() {
1799 return (authType);
1800 }
1801
1802
1803 /**
1804 * Return the portion of the request URI used to select the Context
1805 * of the Request.
1806 */
1807 public String getContextPath() {
1808 return (mappingData.contextPath.toString());
1809 }
1810
1811
1812 /**
1813 * Get the context path.
1814 *
1815 * @return the context path
1816 */
1817 public MessageBytes getContextPathMB() {
1818 return (mappingData.contextPath);
1819 }
1820
1821
1822 /**
1823 * Return the set of Cookies received with this Request.
1824 */
1825 public Cookie[] getCookies() {
1826
1827 if (!cookiesParsed)
1828 parseCookies();
1829
1830 return cookies;
1831
1832 }
1833
1834
1835 /**
1836 * Set the set of cookies recieved with this Request.
1837 */
1838 public void setCookies(Cookie[] cookies) {
1839
1840 this.cookies = cookies;
1841
1842 }
1843
1844
1845 /**
1846 * Return the value of the specified date header, if any; otherwise
1847 * return -1.
1848 *
1849 * @param name Name of the requested date header
1850 *
1851 * @exception IllegalArgumentException if the specified header value
1852 * cannot be converted to a date
1853 */
1854 public long getDateHeader(String name) {
1855
1856 String value = getHeader(name);
1857 if (value == null)
1858 return (-1L);
1859
1860 // Attempt to convert the date header in a variety of formats
1861 long result = FastHttpDateFormat.parseDate(value, formats);
1862 if (result != (-1L)) {
1863 return result;
1864 }
1865 throw new IllegalArgumentException(value);
1866
1867 }
1868
1869
1870 /**
1871 * Return the first value of the specified header, if any; otherwise,
1872 * return <code>null</code>
1873 *
1874 * @param name Name of the requested header
1875 */
1876 public String getHeader(String name) {
1877 return coyoteRequest.getHeader(name);
1878 }
1879
1880
1881 /**
1882 * Return all of the values of the specified header, if any; otherwise,
1883 * return an empty enumeration.
1884 *
1885 * @param name Name of the requested header
1886 */
1887 public Enumeration getHeaders(String name) {
1888 return coyoteRequest.getMimeHeaders().values(name);
1889 }
1890
1891
1892 /**
1893 * Return the names of all headers received with this request.
1894 */
1895 public Enumeration getHeaderNames() {
1896 return coyoteRequest.getMimeHeaders().names();
1897 }
1898
1899
1900 /**
1901 * Return the value of the specified header as an integer, or -1 if there
1902 * is no such header for this request.
1903 *
1904 * @param name Name of the requested header
1905 *
1906 * @exception IllegalArgumentException if the specified header value
1907 * cannot be converted to an integer
1908 */
1909 public int getIntHeader(String name) {
1910
1911 String value = getHeader(name);
1912 if (value == null) {
1913 return (-1);
1914 } else {
1915 return (Integer.parseInt(value));
1916 }
1917
1918 }
1919
1920
1921 /**
1922 * Return the HTTP request method used in this Request.
1923 */
1924 public String getMethod() {
1925 return coyoteRequest.method().toString();
1926 }
1927
1928
1929 /**
1930 * Return the path information associated with this Request.
1931 */
1932 public String getPathInfo() {
1933 return (mappingData.pathInfo.toString());
1934 }
1935
1936
1937 /**
1938 * Get the path info.
1939 *
1940 * @return the path info
1941 */
1942 public MessageBytes getPathInfoMB() {
1943 return (mappingData.pathInfo);
1944 }
1945
1946
1947 /**
1948 * Return the extra path information for this request, translated
1949 * to a real path.
1950 */
1951 public String getPathTranslated() {
1952
1953 if (context == null)
1954 return (null);
1955
1956 if (getPathInfo() == null) {
1957 return (null);
1958 } else {
1959 return (context.getServletContext().getRealPath(getPathInfo()));
1960 }
1961
1962 }
1963
1964
1965 /**
1966 * Return the query string associated with this request.
1967 */
1968 public String getQueryString() {
1969 String queryString = coyoteRequest.queryString().toString();
1970 if (queryString == null || queryString.equals("")) {
1971 return (null);
1972 } else {
1973 return queryString;
1974 }
1975 }
1976
1977
1978 /**
1979 * Return the name of the remote user that has been authenticated
1980 * for this Request.
1981 */
1982 public String getRemoteUser() {
1983
1984 if (userPrincipal != null) {
1985 return (userPrincipal.getName());
1986 } else {
1987 return (null);
1988 }
1989
1990 }
1991
1992
1993 /**
1994 * Get the request path.
1995 *
1996 * @return the request path
1997 */
1998 public MessageBytes getRequestPathMB() {
1999 return (mappingData.requestPath);
2000 }
2001
2002
2003 /**
2004 * Return the session identifier included in this request, if any.
2005 */
2006 public String getRequestedSessionId() {
2007 return (requestedSessionId);
2008 }
2009
2010
2011 /**
2012 * Return the request URI for this request.
2013 */
2014 public String getRequestURI() {
2015 return coyoteRequest.requestURI().toString();
2016 }
2017
2018
2019 /**
2020 * Reconstructs the URL the client used to make the request.
2021 * The returned URL contains a protocol, server name, port
2022 * number, and server path, but it does not include query
2023 * string parameters.
2024 * <p>
2025 * Because this method returns a <code>StringBuffer</code>,
2026 * not a <code>String</code>, you can modify the URL easily,
2027 * for example, to append query parameters.
2028 * <p>
2029 * This method is useful for creating redirect messages and
2030 * for reporting errors.
2031 *
2032 * @return A <code>StringBuffer</code> object containing the
2033 * reconstructed URL
2034 */
2035 public StringBuffer getRequestURL() {
2036
2037 StringBuffer url = new StringBuffer();
2038 String scheme = getScheme();
2039 int port = getServerPort();
2040 if (port < 0)
2041 port = 80; // Work around java.net.URL bug
2042
2043 url.append(scheme);
2044 url.append("://");
2045 url.append(getServerName());
2046 if ((scheme.equals("http") && (port != 80))
2047 || (scheme.equals("https") && (port != 443))) {
2048 url.append(':');
2049 url.append(port);
2050 }
2051 url.append(getRequestURI());
2052
2053 return (url);
2054
2055 }
2056
2057
2058 /**
2059 * Return the portion of the request URI used to select the servlet
2060 * that will process this request.
2061 */
2062 public String getServletPath() {
2063 return (mappingData.wrapperPath.toString());
2064 }
2065
2066
2067 /**
2068 * Get the servlet path.
2069 *
2070 * @return the servlet path
2071 */
2072 public MessageBytes getServletPathMB() {
2073 return (mappingData.wrapperPath);
2074 }
2075
2076
2077 /**
2078 * Return the session associated with this Request, creating one
2079 * if necessary.
2080 */
2081 public HttpSession getSession() {
2082 Session session = doGetSession(true);
2083 if (session != null) {
2084 return session.getSession();
2085 } else {
2086 return null;
2087 }
2088 }
2089
2090
2091 /**
2092 * Return the session associated with this Request, creating one
2093 * if necessary and requested.
2094 *
2095 * @param create Create a new session if one does not exist
2096 */
2097 public HttpSession getSession(boolean create) {
2098 Session session = doGetSession(create);
2099 if (session != null) {
2100 return session.getSession();
2101 } else {
2102 return null;
2103 }
2104 }
2105
2106
2107 /**
2108 * Return <code>true</code> if the session identifier included in this
2109 * request came from a cookie.
2110 */
2111 public boolean isRequestedSessionIdFromCookie() {
2112
2113 if (requestedSessionId != null)
2114 return (requestedSessionCookie);
2115 else
2116 return (false);
2117
2118 }
2119
2120
2121 /**
2122 * Return <code>true</code> if the session identifier included in this
2123 * request came from the request URI.
2124 */
2125 public boolean isRequestedSessionIdFromURL() {
2126
2127 if (requestedSessionId != null)
2128 return (requestedSessionURL);
2129 else
2130 return (false);
2131
2132 }
2133
2134
2135 /**
2136 * Return <code>true</code> if the session identifier included in this
2137 * request came from the request URI.
2138 *
2139 * @deprecated As of Version 2.1 of the Java Servlet API, use
2140 * <code>isRequestedSessionIdFromURL()</code> instead.
2141 */
2142 public boolean isRequestedSessionIdFromUrl() {
2143 return (isRequestedSessionIdFromURL());
2144 }
2145
2146
2147 /**
2148 * Return <code>true</code> if the session identifier included in this
2149 * request identifies a valid session.
2150 */
2151 public boolean isRequestedSessionIdValid() {
2152
2153 if (requestedSessionId == null)
2154 return (false);
2155 if (context == null)
2156 return (false);
2157 Manager manager = context.getManager();
2158 if (manager == null)
2159 return (false);
2160 Session session = null;
2161 try {
2162 session = manager.findSession(requestedSessionId);
2163 } catch (IOException e) {
2164 session = null;
2165 }
2166 if ((session != null) && session.isValid())
2167 return (true);
2168 else
2169 return (false);
2170
2171 }
2172
2173
2174 /**
2175 * Return <code>true</code> if the authenticated user principal
2176 * possesses the specified role name.
2177 *
2178 * @param role Role name to be validated
2179 */
2180 public boolean isUserInRole(String role) {
2181
2182 // Have we got an authenticated principal at all?
2183 if (userPrincipal == null)
2184 return (false);
2185
2186 // Identify the Realm we will use for checking role assignmenets
2187 if (context == null)
2188 return (false);
2189 Realm realm = context.getRealm();
2190 if (realm == null)
2191 return (false);
2192
2193 // Check for a role alias defined in a <security-role-ref> element
2194 if (wrapper != null) {
2195 String realRole = wrapper.findSecurityReference(role);
2196 if ((realRole != null) &&
2197 realm.hasRole(userPrincipal, realRole))
2198 return (true);
2199 }
2200
2201 // Check for a role defined directly as a <security-role>
2202 return (realm.hasRole(userPrincipal, role));
2203
2204 }
2205
2206
2207 /**
2208 * Return the principal that has been authenticated for this Request.
2209 */
2210 public Principal getPrincipal() {
2211 return (userPrincipal);
2212 }
2213
2214
2215 /**
2216 * Return the principal that has been authenticated for this Request.
2217 */
2218 public Principal getUserPrincipal() {
2219 if (userPrincipal instanceof GenericPrincipal) {
2220 return ((GenericPrincipal) userPrincipal).getUserPrincipal();
2221 } else {
2222 return (userPrincipal);
2223 }
2224 }
2225
2226
2227 /**
2228 * Return the session associated with this Request, creating one
2229 * if necessary.
2230 */
2231 public Session getSessionInternal() {
2232 return doGetSession(true);
2233 }
2234
2235
2236 /**
2237 * Change the ID of the session that this request is associated with. There
2238 * are several things that may trigger an ID change. These include moving
2239 * between nodes in a cluster and session fixation prevention during the
2240 * authentication process.
2241 *
2242 * @param session The session to change the session ID for
2243 */
2244 public void changeSessionId(String newSessionId) {
2245 // This should only ever be called if there was an old session ID but
2246 // double check to be sure
2247 if (requestedSessionId != null && requestedSessionId.length() > 0) {
2248 requestedSessionId = newSessionId;
2249 }
2250
2251 if (context != null && !context.getCookies())
2252 return;
2253
2254 if (response != null) {
2255 Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,
2256 newSessionId);
2257 newCookie.setMaxAge(-1);
2258 String contextPath = null;
2259 if (!response.getConnector().getEmptySessionPath()
2260 && (context != null)) {
2261 contextPath = context.getEncodedPath();
2262 }
2263 if ((contextPath != null) && (contextPath.length() > 0)) {
2264 newCookie.setPath(contextPath);
2265 } else {
2266 newCookie.setPath("/");
2267 }
2268 if (isSecure()) {
2269 newCookie.setSecure(true);
2270 }
2271 if (context == null) {
2272 response.addCookieInternal(newCookie, false);
2273 } else {
2274 response.addCookieInternal(newCookie, context.getUseHttpOnly());
2275 }
2276 }
2277 }
2278
2279
2280 /**
2281 * Return the session associated with this Request, creating one
2282 * if necessary and requested.
2283 *
2284 * @param create Create a new session if one does not exist
2285 */
2286 public Session getSessionInternal(boolean create) {
2287 return doGetSession(create);
2288 }
2289
2290
2291 /**
2292 * Get the event associated with the request.
2293 * @return
2294 */
2295 public CometEventImpl getEvent() {
2296 if (event == null) {
2297 event = new CometEventImpl(this, response);
2298 }
2299 return event;
2300 }
2301
2302
2303 /**
2304 * Return true if the current request is handling Comet traffic.
2305 */
2306 public boolean isComet() {
2307 return comet;
2308 }
2309
2310
2311 /**
2312 * Set comet state.
2313 */
2314 public void setComet(boolean comet) {
2315 this.comet = comet;
2316 }
2317
2318 /**
2319 * return true if we have parsed parameters
2320 */
2321 public boolean isParametersParsed() {
2322 return parametersParsed;
2323 }
2324
2325 /**
2326 * Return true if bytes are available.
2327 */
2328 public boolean getAvailable() {
2329 return (inputBuffer.available() > 0);
2330 }
2331
2332 public void cometClose() {
2333 coyoteRequest.action(ActionCode.ACTION_COMET_CLOSE,getEvent());
2334 }
2335
2336 public void setCometTimeout(long timeout) {
2337 coyoteRequest.action(ActionCode.ACTION_COMET_SETTIMEOUT,new Long(timeout));
2338 }
2339
2340 // ------------------------------------------------------ Protected Methods
2341
2342
2343 protected Session doGetSession(boolean create) {
2344
2345 // There cannot be a session if no context has been assigned yet
2346 if (context == null)
2347 return (null);
2348
2349 // Return the current session if it exists and is valid
2350 if ((session != null) && !session.isValid())
2351 session = null;
2352 if (session != null)
2353 return (session);
2354
2355 // Return the requested session if it exists and is valid
2356 Manager manager = null;
2357 if (context != null)
2358 manager = context.getManager();
2359 if (manager == null)
2360 return (null); // Sessions are not supported
2361 if (requestedSessionId != null) {
2362 try {
2363 session = manager.findSession(requestedSessionId);
2364 } catch (IOException e) {
2365 session = null;
2366 }
2367 if ((session != null) && !session.isValid())
2368 session = null;
2369 if (session != null) {
2370 session.access();
2371 return (session);
2372 }
2373 }
2374
2375 // Create a new session if requested and the response is not committed
2376 if (!create)
2377 return (null);
2378 if ((context != null) && (response != null) &&
2379 context.getCookies() &&
2380 response.getResponse().isCommitted()) {
2381 throw new IllegalStateException
2382 (sm.getString("coyoteRequest.sessionCreateCommitted"));
2383 }
2384
2385 // Attempt to reuse session id if one was submitted in a cookie
2386 // Do not reuse the session id if it is from a URL, to prevent possible
2387 // phishing attacks
2388 if (connector.getEmptySessionPath()
2389 && isRequestedSessionIdFromCookie()) {
2390 session = manager.createSession(getRequestedSessionId());
2391 } else {
2392 session = manager.createSession(null);
2393 }
2394
2395 // Creating a new session cookie based on that session
2396 if ((session != null) && (getContext() != null)
2397 && getContext().getCookies()) {
2398 Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
2399 session.getIdInternal());
2400 configureSessionCookie(cookie);
2401 response.addCookieInternal(cookie, context.getUseHttpOnly());
2402 }
2403
2404 if (session != null) {
2405 session.access();
2406 return (session);
2407 } else {
2408 return (null);
2409 }
2410
2411 }
2412
2413 /**
2414 * Configures the given JSESSIONID cookie.
2415 *
2416 * @param cookie The JSESSIONID cookie to be configured
2417 */
2418 protected void configureSessionCookie(Cookie cookie) {
2419 cookie.setMaxAge(-1);
2420 String contextPath = null;
2421 if (!connector.getEmptySessionPath() && (getContext() != null)) {
2422 contextPath = getContext().getEncodedPath();
2423 }
2424 if ((contextPath != null) && (contextPath.length() > 0)) {
2425 cookie.setPath(contextPath);
2426 } else {
2427 cookie.setPath("/");
2428 }
2429 if (isSecure()) {
2430 cookie.setSecure(true);
2431 }
2432 }
2433
2434 protected String unescape(String s) {
2435 if (s==null) return null;
2436 if (s.indexOf('\\') == -1) return s;
2437 StringBuffer buf = new StringBuffer();
2438 for (int i=0; i<s.length(); i++) {
2439 char c = s.charAt(i);
2440 if (c!='\\') buf.append(c);
2441 else {
2442 if (++i >= s.length()) throw new IllegalArgumentException();//invalid escape, hence invalid cookie
2443 c = s.charAt(i);
2444 buf.append(c);
2445 }
2446 }
2447 return buf.toString();
2448 }
2449
2450 /**
2451 * Parse cookies.
2452 */
2453 protected void parseCookies() {
2454
2455 cookiesParsed = true;
2456
2457 Cookies serverCookies = coyoteRequest.getCookies();
2458 int count = serverCookies.getCookieCount();
2459 if (count <= 0)
2460 return;
2461
2462 cookies = new Cookie[count];
2463
2464 int idx=0;
2465 for (int i = 0; i < count; i++) {
2466 ServerCookie scookie = serverCookies.getCookie(i);
2467 try {
2468 /*
2469 we must unescape the '\\' escape character
2470 */
2471 Cookie cookie = new Cookie(scookie.getName().toString(),null);
2472 int version = scookie.getVersion();
2473 cookie.setVersion(version);
2474 cookie.setValue(unescape(scookie.getValue().toString()));
2475 cookie.setPath(unescape(scookie.getPath().toString()));
2476 String domain = scookie.getDomain().toString();
2477 if (domain!=null) cookie.setDomain(unescape(domain));//avoid NPE
2478 String comment = scookie.getComment().toString();
2479 cookie.setComment(version==1?unescape(comment):null);
2480 cookies[idx++] = cookie;
2481 } catch(IllegalArgumentException e) {
2482 // Ignore bad cookie
2483 }
2484 }
2485 if( idx < count ) {
2486 Cookie [] ncookies = new Cookie[idx];
2487 System.arraycopy(cookies, 0, ncookies, 0, idx);
2488 cookies = ncookies;
2489 }
2490
2491 }
2492
2493 /**
2494 * Parse request parameters.
2495 */
2496 protected void parseParameters() {
2497
2498 parametersParsed = true;
2499
2500 Parameters parameters = coyoteRequest.getParameters();
2501
2502 // getCharacterEncoding() may have been overridden to search for
2503 // hidden form field containing request encoding
2504 String enc = getCharacterEncoding();
2505
2506 boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
2507 if (enc != null) {
2508 parameters.setEncoding(enc);
2509 if (useBodyEncodingForURI) {
2510 parameters.setQueryStringEncoding(enc);
2511 }
2512 } else {
2513 parameters.setEncoding
2514 (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
2515 if (useBodyEncodingForURI) {
2516 parameters.setQueryStringEncoding
2517 (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
2518 }
2519 }
2520
2521 parameters.handleQueryParameters();
2522
2523 if (usingInputStream || usingReader)
2524 return;
2525
2526 if (!getMethod().equalsIgnoreCase("POST"))
2527 return;
2528
2529 String contentType = getContentType();
2530 if (contentType == null)
2531 contentType = "";
2532 int semicolon = contentType.indexOf(';');
2533 if (semicolon >= 0) {
2534 contentType = contentType.substring(0, semicolon).trim();
2535 } else {
2536 contentType = contentType.trim();
2537 }
2538 if (!("application/x-www-form-urlencoded".equals(contentType)))
2539 return;
2540
2541 int len = getContentLength();
2542
2543 if (len > 0) {
2544 int maxPostSize = connector.getMaxPostSize();
2545 if ((maxPostSize > 0) && (len > maxPostSize)) {
2546 if (context.getLogger().isDebugEnabled()) {
2547 context.getLogger().debug(
2548 sm.getString("coyoteRequest.postTooLarge"));
2549 }
2550 return;
2551 }
2552 byte[] formData = null;
2553 if (len < CACHED_POST_LEN) {
2554 if (postData == null)
2555 postData = new byte[CACHED_POST_LEN];
2556 formData = postData;
2557 } else {
2558 formData = new byte[len];
2559 }
2560 try {
2561 if (readPostBody(formData, len) != len) {
2562 return;
2563 }
2564 } catch (IOException e) {
2565 // Client disconnect
2566 if (context.getLogger().isDebugEnabled()) {
2567 context.getLogger().debug(
2568 sm.getString("coyoteRequest.parseParameters"), e);
2569 }
2570 return;
2571 }
2572 parameters.processParameters(formData, 0, len);
2573 } else if ("chunked".equalsIgnoreCase(
2574 coyoteRequest.getHeader("transfer-encoding"))) {
2575 byte[] formData = null;
2576 try {
2577 formData = readChunkedPostBody();
2578 } catch (IOException e) {
2579 // Client disconnect
2580 if (context.getLogger().isDebugEnabled()) {
2581 context.getLogger().debug(
2582 sm.getString("coyoteRequest.parseParameters"), e);
2583 }
2584 return;
2585 }
2586 parameters.processParameters(formData, 0, formData.length);
2587 }
2588
2589 }
2590
2591
2592 /**
2593 * Read post body in an array.
2594 */
2595 protected int readPostBody(byte body[], int len)
2596 throws IOException {
2597
2598 int offset = 0;
2599 do {
2600 int inputLen = getStream().read(body, offset, len - offset);
2601 if (inputLen <= 0) {
2602 return offset;
2603 }
2604 offset += inputLen;
2605 } while ((len - offset) > 0);
2606 return len;
2607
2608 }
2609
2610
2611 /**
2612 * Read chunked post body.
2613 */
2614 protected byte[] readChunkedPostBody() throws IOException {
2615 ByteChunk body = new ByteChunk();
2616
2617 byte[] buffer = new byte[CACHED_POST_LEN];
2618
2619 int len = 0;
2620 while (len > -1) {
2621 len = getStream().read(buffer, 0, CACHED_POST_LEN);
2622 if (connector.getMaxPostSize() > 0 &&
2623 (body.getLength() + len) > connector.getMaxPostSize()) {
2624 // Too much data
2625 throw new IllegalArgumentException(
2626 sm.getString("coyoteRequest.chunkedPostTooLarge"));
2627 }
2628 if (len > 0) {
2629 body.append(buffer, 0, len);
2630 }
2631 }
2632 if (body.getLength() < body.getBuffer().length) {
2633 int length = body.getLength();
2634 byte[] result = new byte[length];
2635 System.arraycopy(body.getBuffer(), 0, result, 0, length);
2636 return result;
2637 } else {
2638 return body.getBuffer();
2639 }
2640 }
2641
2642
2643 /**
2644 * Parse request locales.
2645 */
2646 protected void parseLocales() {
2647
2648 localesParsed = true;
2649
2650 Enumeration values = getHeaders("accept-language");
2651
2652 while (values.hasMoreElements()) {
2653 String value = values.nextElement().toString();
2654 parseLocalesHeader(value);
2655 }
2656
2657 }
2658
2659
2660 /**
2661 * Parse accept-language header value.
2662 */
2663 protected void parseLocalesHeader(String value) {
2664
2665 // Store the accumulated languages that have been requested in
2666 // a local collection, sorted by the quality value (so we can
2667 // add Locales in descending order). The values will be ArrayLists
2668 // containing the corresponding Locales to be added
2669 TreeMap locales = new TreeMap();
2670
2671 // Preprocess the value to remove all whitespace
2672 int white = value.indexOf(' ');
2673 if (white < 0)
2674 white = value.indexOf('\t');
2675 if (white >= 0) {
2676 StringBuffer sb = new StringBuffer();
2677 int len = value.length();
2678 for (int i = 0; i < len; i++) {
2679 char ch = value.charAt(i);
2680 if ((ch != ' ') && (ch != '\t'))
2681 sb.append(ch);
2682 }
2683 value = sb.toString();
2684 }
2685
2686 // Process each comma-delimited language specification
2687 parser.setString(value); // ASSERT: parser is available to us
2688 int length = parser.getLength();
2689 while (true) {
2690
2691 // Extract the next comma-delimited entry
2692 int start = parser.getIndex();
2693 if (start >= length)
2694 break;
2695 int end = parser.findChar(',');
2696 String entry = parser.extract(start, end).trim();
2697 parser.advance(); // For the following entry
2698
2699 // Extract the quality factor for this entry
2700 double quality = 1.0;
2701 int semi = entry.indexOf(";q=");
2702 if (semi >= 0) {
2703 try {
2704 quality = Double.parseDouble(entry.substring(semi + 3));
2705 } catch (NumberFormatException e) {
2706 quality = 0.0;
2707 }
2708 entry = entry.substring(0, semi);
2709 }
2710
2711 // Skip entries we are not going to keep track of
2712 if (quality < 0.00005)
2713 continue; // Zero (or effectively zero) quality factors
2714 if ("*".equals(entry))
2715 continue; // FIXME - "*" entries are not handled
2716
2717 // Extract the language and country for this entry
2718 String language = null;
2719 String country = null;
2720 String variant = null;
2721 int dash = entry.indexOf('-');
2722 if (dash < 0) {
2723 language = entry;
2724 country = "";
2725 variant = "";
2726 } else {
2727 language = entry.substring(0, dash);
2728 country = entry.substring(dash + 1);
2729 int vDash = country.indexOf('-');
2730 if (vDash > 0) {
2731 String cTemp = country.substring(0, vDash);
2732 variant = country.substring(vDash + 1);
2733 country = cTemp;
2734 } else {
2735 variant = "";
2736 }
2737 }
2738 if (!isAlpha(language) || !isAlpha(country) || !isAlpha(variant)) {
2739 continue;
2740 }
2741
2742 // Add a new Locale to the list of Locales for this quality level
2743 Locale locale = new Locale(language, country, variant);
2744 Double key = new Double(-quality); // Reverse the order
2745 ArrayList values = (ArrayList) locales.get(key);
2746 if (values == null) {
2747 values = new ArrayList();
2748 locales.put(key, values);
2749 }
2750 values.add(locale);
2751
2752 }
2753
2754 // Process the quality values in highest->lowest order (due to
2755 // negating the Double value when creating the key)
2756 Iterator keys = locales.keySet().iterator();
2757 while (keys.hasNext()) {
2758 Double key = (Double) keys.next();
2759 ArrayList list = (ArrayList) locales.get(key);
2760 Iterator values = list.iterator();
2761 while (values.hasNext()) {
2762 Locale locale = (Locale) values.next();
2763 addLocale(locale);
2764 }
2765 }
2766
2767 }
2768
2769
2770 protected static final boolean isAlpha(String value) {
2771 for (int i = 0; i < value.length(); i++) {
2772 char c = value.charAt(i);
2773 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
2774 return false;
2775 }
2776 }
2777 return true;
2778 }
2779
2780 }