001    /*
002     * Copyright 1999,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    
018    package org.apache.commons.messagelet.impl;
019    
020    
021    import java.io.BufferedReader;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.InputStreamReader;
025    import java.io.UnsupportedEncodingException;
026    import java.util.ArrayList;
027    import java.util.Enumeration;
028    import java.util.HashMap;
029    import java.util.Locale;
030    import java.util.Map;
031    
032    import javax.servlet.RequestDispatcher;
033    import javax.servlet.ServletContext;
034    import javax.servlet.ServletInputStream;
035    import javax.servlet.ServletRequest;
036    
037    import org.apache.commons.collections.iterators.IteratorEnumeration;
038    import org.apache.commons.collections.iterators.SingletonIterator;
039    
040    /**
041     * Based on the RequestBase code from Catalina.
042     *
043     * @author Craig R. McClanahan
044     * @author James Strachan
045     * @version $Revision: 155459 $ $Date: 2005-02-26 13:24:44 +0000 (Sat, 26 Feb 2005) $
046     */
047    
048    public class ServletRequestImpl implements ServletRequest {
049    
050    
051        // ----------------------------------------------------- Instance Variables
052    
053    
054        /**
055         * The attributes associated with this Request, keyed by attribute name.
056         */
057        protected HashMap attributes = new HashMap();
058    
059    
060        /**
061         * The authorization credentials sent with this Request.
062         */
063        protected String authorization = null;
064    
065    
066        /**
067         * The character encoding for this Request.
068         */
069        protected String characterEncoding = null;
070    
071    
072    
073        /**
074         * The content length associated with this request.
075         */
076        protected int contentLength = -1;
077    
078    
079        /**
080         * The content type associated with this request.
081         */
082        protected String contentType = null;
083    
084    
085        /**
086         * The default Locale if none are specified.
087         */
088        protected static Locale defaultLocale = Locale.getDefault();
089    
090    
091        /**
092         * The input stream associated with this Request.
093         */
094        protected InputStream input = null;
095    
096    
097        /**
098         * The preferred Locales assocaited with this Request.
099         */
100        protected ArrayList locales = new ArrayList();
101    
102    
103    
104        /**
105         * The protocol name and version associated with this Request.
106         */
107        protected String protocol = null;
108    
109    
110        /**
111         * The reader that has been returned by <code>getReader</code>, if any.
112         */
113        protected BufferedReader reader = null;
114    
115    
116        /**
117         * The remote address associated with this request.
118         */
119        protected String remoteAddr = null;
120    
121    
122        /**
123         * The fully qualified name of the remote host.
124         */
125        protected String remoteHost = null;
126    
127    
128    
129        /**
130         * The scheme associated with this Request.
131         */
132        protected String scheme = null;
133    
134    
135        /**
136         * Was this request received on a secure connection?
137         */
138        protected boolean secure = false;
139    
140    
141        /**
142         * The server name associated with this Request.
143         */
144        protected String serverName = null;
145    
146    
147        /**
148         * The server port associated with this Request.
149         */
150        protected int serverPort = -1;
151    
152    
153        /**
154         * The ServletInputStream that has been returned by
155         * <code>getInputStream()</code>, if any.
156         */
157        protected ServletInputStream stream = null;
158    
159    
160        /**
161         * The ServletContext which is used to dispatch further requests
162         */
163        protected ServletContext servletContext;
164    
165        
166        
167        public ServletRequestImpl(ServletContext servletContext) {
168            this.servletContext = servletContext;
169        }
170        
171        // ------------------------------------------------------------- Properties
172    
173    
174    
175        /**
176         * Return the input stream associated with this Request.
177         */
178        public InputStream getStream() {
179    
180            return (this.input);
181    
182        }
183    
184    
185        /**
186         * Set the input stream associated with this Request.
187         *
188         * @param input The new input stream
189         */
190        public void setStream(InputStream input) {
191    
192            this.input = input;
193    
194        }
195    
196    
197    
198        // --------------------------------------------------------- Public Methods
199    
200    
201        /**
202         * Add a Locale to the set of preferred Locales for this Request.  The
203         * first added Locale will be the first one returned by getLocales().
204         *
205         * @param locale The new preferred Locale
206         */
207        public void addLocale(Locale locale) {
208    
209            synchronized (locales) {
210                locales.add(locale);
211            }
212    
213        }
214    
215    
216        /**
217         * Create and return a ServletInputStream to read the content
218         * associated with this Request.  The default implementation creates an
219         * instance of RequestStream associated with this request, but this can
220         * be overridden if necessary.
221         *
222         * @exception IOException if an input/output error occurs
223         */
224        public ServletInputStream createInputStream() throws IOException {
225    
226            //return (new RequestStream(this));
227            return null;
228    
229        }
230    
231    
232        /**
233         * Perform whatever actions are required to flush and close the input
234         * stream or reader, in a single operation.
235         *
236         * @exception IOException if an input/output error occurs
237         */
238        public void finishRequest() throws IOException {
239    
240            // If a Reader has been acquired, close it
241            if (reader != null) {
242                try {
243                    reader.close();
244                } catch (IOException e) {
245                    ;
246                }
247            }
248    
249            // If a ServletInputStream has been acquired, close it
250            if (stream != null) {
251                try {
252                    stream.close();
253                } catch (IOException e) {
254                    ;
255                }
256            }
257    
258            // The underlying input stream (perhaps from a socket)
259            // is not our responsibility
260    
261        }
262    
263    
264        /**
265         * Set the content length associated with this Request.
266         *
267         * @param length The new content length
268         */
269        public void setContentLength(int length) {
270    
271            this.contentLength = length;
272    
273        }
274    
275    
276        /**
277         * Set the content type (and optionally the character encoding)
278         * associated with this Request.  For example,
279         * <code>text/html; charset=ISO-8859-4</code>.
280         *
281         * @param type The new content type
282         */
283        public void setContentType(String type) {
284    
285            this.contentType = type;
286            if (type.indexOf(';') >= 0) {
287                //characterEncoding = RequestUtil.parseCharacterEncoding(type);
288            }
289    
290        }
291    
292    
293    
294        /**
295         * Set the protocol name and version associated with this Request.
296         *
297         * @param protocol Protocol name and version
298         */
299        public void setProtocol(String protocol) {
300    
301            this.protocol = protocol;
302    
303        }
304    
305    
306        /**
307         * Set the IP address of the remote client associated with this Request.
308         *
309         * @param remoteAddr The remote IP address
310         */
311        public void setRemoteAddr(String remoteAddr) {
312    
313            this.remoteAddr = remoteAddr;
314    
315        }
316    
317    
318        /**
319         * Set the fully qualified name of the remote client associated with this
320         * Request.
321         *
322         * @param remoteHost The remote host name
323         */
324        public void setRemoteHost(String remoteHost) {
325    
326            this.remoteHost = remoteHost;
327    
328        }
329    
330    
331        /**
332         * Set the name of the scheme associated with this request.  Typical values
333         * are <code>http</code>, <code>https</code>, and <code>ftp</code>.
334         *
335         * @param scheme The scheme
336         */
337        public void setScheme(String scheme) {
338    
339            this.scheme = scheme;
340    
341        }
342    
343    
344        /**
345         * Set the value to be returned by <code>isSecure()</code>
346         * for this Request.
347         *
348         * @param secure The new isSecure value
349         */
350        public void setSecure(boolean secure) {
351    
352            this.secure = secure;
353    
354        }
355    
356    
357        /**
358         * Set the name of the server (virtual host) to process this request.
359         *
360         * @param name The server name
361         */
362        public void setServerName(String name) {
363    
364            this.serverName = name;
365    
366        }
367    
368    
369        /**
370         * Set the port number of the server to process this request.
371         *
372         * @param port The server port
373         */
374        public void setServerPort(int port) {
375    
376            this.serverPort = port;
377    
378        }
379    
380    
381        // ------------------------------------------------- ServletRequest Methods
382    
383    
384        /**
385         * Return the specified request attribute if it exists; otherwise, return
386         * <code>null</code>.
387         *
388         * @param name Name of the request attribute to return
389         */
390        public Object getAttribute(String name) {
391    
392            synchronized (attributes) {
393                return (attributes.get(name));
394            }
395    
396        }
397    
398    
399        /**
400         * Return the names of all request attributes for this Request, or an
401         * empty <code>Enumeration</code> if there are none.
402         */
403        public Enumeration getAttributeNames() {
404    
405            synchronized (attributes) {
406                return new IteratorEnumeration(attributes.keySet().iterator());
407            }
408    
409        }
410    
411    
412        /**
413         * Return the character encoding for this Request.
414         */
415        public String getCharacterEncoding() {
416    
417            if (characterEncoding== null) {
418                characterEncoding= "ISO-8859-1";
419            }
420            return (this.characterEncoding);
421        }
422    
423    
424        /**
425         * Return the content length for this Request.
426         */
427        public int getContentLength() {
428    
429            return (this.contentLength);
430    
431        }
432    
433    
434        /**
435         * Return the content type for this Request.
436         */
437        public String getContentType() {
438    
439            return (contentType);
440    
441        }
442    
443    
444        /**
445         * Return the servlet input stream for this Request.  The default
446         * implementation returns a servlet input stream created by
447         * <code>createInputStream()</code>.
448         *
449         * @exception IllegalStateException if <code>getReader()</code> has
450         *  already been called for this request
451         * @exception IOException if an input/output error occurs
452         */
453        public ServletInputStream getInputStream() throws IOException {
454    
455            if (reader != null) {
456                throw new IllegalStateException( "getReader() has already been called" );
457            }
458    
459            if (stream == null)
460                stream = createInputStream();
461            return (stream);
462    
463        }
464    
465    
466        /**
467         * Return the preferred Locale that the client will accept content in,
468         * based on the value for the first <code>Accept-Language</code> header
469         * that was encountered.  If the request did not specify a preferred
470         * language, the server's default Locale is returned.
471         */
472        public Locale getLocale() {
473    
474            synchronized (locales) {
475                if (locales.size() > 0)
476                    return ((Locale) locales.get(0));
477                else
478                    return (defaultLocale);
479            }
480    
481        }
482    
483    
484        /**
485         * Return the set of preferred Locales that the client will accept
486         * content in, based on the values for any <code>Accept-Language</code>
487         * headers that were encountered.  If the request did not specify a
488         * preferred language, the server's default Locale is returned.
489         */
490        public Enumeration getLocales() {
491    
492            synchronized (locales) {
493                if (locales.size() > 0) {
494                    return new IteratorEnumeration( locales.iterator() );
495                }
496            }
497            return new IteratorEnumeration( new SingletonIterator( defaultLocale ) );
498    
499        }
500    
501    
502        /**
503         * Return the value of the specified request parameter, if any; otherwise,
504         * return <code>null</code>.  If there is more than one value defined,
505         * return only the first one.
506         *
507         * @param name Name of the desired request parameter
508         */
509        public String getParameter(String name) {
510            String values[] = (String[]) getParameterMap().get(name);
511            if (values != null)
512                return (values[0]);
513            else
514                return (null);
515    
516        }
517    
518    
519        /**
520         * Return the defined values for the specified request parameter, if any;
521         * otherwise, return <code>null</code>.
522         *
523         * @param name Name of the desired request parameter
524         */
525        public String[] getParameterValues(String name) {
526            String values[] = (String[]) getParameterMap().get(name);
527            if (values != null)
528                return (values);
529            else
530                return (null);
531        }
532        
533    
534        /**
535         * Returns a <code>Map</code> of the parameters of this request.
536         * Request parameters are extra information sent with the request.
537         * For HTTP servlets, parameters are contained in the query string
538         * or posted form data.
539         *
540         * @return A <code>Map</code> containing parameter names as keys
541         *  and parameter values as map values.
542         */
543        public Map getParameterMap() {
544            return new HashMap();
545        }
546    
547    
548        /**
549         * Return the names of all defined request parameters for this request.
550         */
551        public Enumeration getParameterNames() {
552            return new IteratorEnumeration(getParameterMap().keySet().iterator());
553        }
554        
555    
556        /**
557         * Return the protocol and version used to make this Request.
558         */
559        public String getProtocol() {
560    
561            return (this.protocol);
562    
563        }
564    
565    
566        /**
567         * Read the Reader wrapping the input stream for this Request.  The
568         * default implementation wraps a <code>BufferedReader</code> around the
569         * servlet input stream returned by <code>createInputStream()</code>.
570         *
571         * @exception IllegalStateException if <code>getInputStream()</code>
572         *  has already been called for this request
573         * @exception IOException if an input/output error occurs
574         */
575        public BufferedReader getReader() throws IOException {
576    
577            if (stream != null) {
578                throw new IllegalStateException( "getInputStream() has already been called" );
579            }
580    
581            if (reader == null) {
582                String encoding = getCharacterEncoding();
583                InputStreamReader isr =
584                    new InputStreamReader(createInputStream(), encoding);
585                reader = new BufferedReader(isr);
586            }
587            return (reader);
588    
589        }
590    
591    
592        /**
593         * Return the real path of the specified virtual path.
594         *
595         * @param path Path to be translated
596         *
597         * @deprecated As of version 2.1 of the Java Servlet API, use
598         *  <code>ServletContext.getRealPath()</code>.
599         */
600        public String getRealPath(String path) {
601    
602            if (servletContext == null)
603                return (null);
604            else {
605                try {
606                    return (servletContext.getRealPath(path));
607                } catch (IllegalArgumentException e) {
608                    return (null);
609                }
610            }
611    
612        }
613    
614    
615        /**
616         * Return the remote IP address making this Request.
617         */
618        public String getRemoteAddr() {
619    
620            return (this.remoteAddr);
621    
622        }
623    
624    
625        /**
626         * Return the remote host name making this Request.
627         */
628        public String getRemoteHost() {
629    
630            return (this.remoteHost);
631    
632        }
633    
634    
635        /**
636         * Return a RequestDispatcher that wraps the resource at the specified
637         * path, which may be interpreted as relative to the current request path.
638         *
639         * @param path Path of the resource to be wrapped
640         */
641        public RequestDispatcher getRequestDispatcher(String path) {
642            return servletContext.getRequestDispatcher(path);
643        }
644    
645    
646        /**
647         * Return the scheme used to make this Request.
648         */
649        public String getScheme() {
650    
651            return (this.scheme);
652    
653        }
654    
655    
656        /**
657         * Return the server name responding to this Request.
658         */
659        public String getServerName() {
660    
661            return (this.serverName);
662    
663        }
664    
665    
666        /**
667         * Return the server port responding to this Request.
668         */
669        public int getServerPort() {
670    
671            return (this.serverPort);
672    
673        }
674    
675    
676        /**
677         * Was this request received on a secure connection?
678         */
679        public boolean isSecure() {
680    
681            return (this.secure);
682    
683        }
684    
685    
686        /**
687         * Remove the specified request attribute if it exists.
688         *
689         * @param name Name of the request attribute to remove
690         */
691        public void removeAttribute(String name) {
692    
693            synchronized (attributes) {
694                attributes.remove(name);
695            }
696    
697        }
698    
699    
700        /**
701         * Set the specified request attribute to the specified value.
702         *
703         * @param name Name of the request attribute to set
704         * @param value The associated value
705         */
706        public void setAttribute(String name, Object value) {
707    
708            // Name cannot be null
709            if (name == null) {
710                throw new IllegalArgumentException( "Attribute name cannot be null" );
711            }
712    
713            // Null value is the same as removeAttribute()
714            if (value == null) {
715                removeAttribute(name);
716                return;
717            }
718    
719            synchronized (attributes) {
720                attributes.put(name, value);
721            }
722    
723        }
724    
725    
726        /**
727         * Overrides the name of the character encoding used in the body of
728         * this request.  This method must be called prior to reading request
729         * parameters or reading input using <code>getReader()</code>.
730         *
731         * @param enc The character encoding to be used
732         *
733         * @exception UnsupportedEncodingException if the specified encoding
734         *  is not supported
735         *
736         * @since Servlet 2.3
737         */
738        public void setCharacterEncoding(String enc)
739            throws UnsupportedEncodingException {
740    
741            // Ensure that the specified encoding is valid
742            byte buffer[] = new byte[1];
743            buffer[0] = (byte) 'a';
744            String dummy = new String(buffer, enc);
745    
746            // Save the validated encoding
747            this.characterEncoding = enc;
748        }
749    
750        /**
751         * Log a message to the current ServletContext
752         *
753         * @param message Message to be logged
754         */
755        protected void log(String message) {
756    
757            servletContext.log(message);
758    
759        }
760    
761    
762        /**
763         * Log a message to the current ServletContext
764         *
765         * @param message Message to be logged
766         * @param throwable Associated exception
767         */
768        protected void log(String message, Throwable throwable) {
769    
770            servletContext.log(message, throwable);
771    
772        }
773    
774    }