001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.core;
019:
020: import java.io.IOException;
021: import java.util.ArrayList;
022: import java.util.Enumeration;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.NoSuchElementException;
027:
028: import javax.servlet.RequestDispatcher;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpServletRequestWrapper;
031: import javax.servlet.http.HttpSession;
032:
033: import org.apache.catalina.Context;
034: import org.apache.catalina.Globals;
035: import org.apache.catalina.Session;
036: import org.apache.catalina.Manager;
037: import org.apache.catalina.util.Enumerator;
038: import org.apache.catalina.util.RequestUtil;
039: import org.apache.catalina.util.StringManager;
040:
041: /**
042: * Wrapper around a <code>javax.servlet.http.HttpServletRequest</code>
043: * that transforms an application request object (which might be the original
044: * one passed to a servlet, or might be based on the 2.3
045: * <code>javax.servlet.http.HttpServletRequestWrapper</code> class)
046: * back into an internal <code>org.apache.catalina.HttpRequest</code>.
047: * <p>
048: * <strong>WARNING</strong>: Due to Java's lack of support for multiple
049: * inheritance, all of the logic in <code>ApplicationRequest</code> is
050: * duplicated in <code>ApplicationHttpRequest</code>. Make sure that you
051: * keep these two classes in synchronization when making changes!
052: *
053: * @author Craig R. McClanahan
054: * @author Remy Maucherat
055: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
056: */
057:
058: class ApplicationHttpRequest extends HttpServletRequestWrapper {
059:
060: // ------------------------------------------------------- Static Variables
061:
062: /**
063: * The set of attribute names that are special for request dispatchers.
064: */
065: protected static final String specials[] = {
066: Globals.INCLUDE_REQUEST_URI_ATTR,
067: Globals.INCLUDE_CONTEXT_PATH_ATTR,
068: Globals.INCLUDE_SERVLET_PATH_ATTR,
069: Globals.INCLUDE_PATH_INFO_ATTR,
070: Globals.INCLUDE_QUERY_STRING_ATTR,
071: Globals.FORWARD_REQUEST_URI_ATTR,
072: Globals.FORWARD_CONTEXT_PATH_ATTR,
073: Globals.FORWARD_SERVLET_PATH_ATTR,
074: Globals.FORWARD_PATH_INFO_ATTR,
075: Globals.FORWARD_QUERY_STRING_ATTR };
076:
077: /**
078: * The string manager for this package.
079: */
080: protected static StringManager sm = StringManager
081: .getManager(Constants.Package);
082:
083: // ----------------------------------------------------------- Constructors
084:
085: /**
086: * Construct a new wrapped request around the specified servlet request.
087: *
088: * @param request The servlet request being wrapped
089: */
090: public ApplicationHttpRequest(HttpServletRequest request,
091: Context context, boolean crossContext) {
092:
093: super (request);
094: this .context = context;
095: this .crossContext = crossContext;
096: setRequest(request);
097:
098: }
099:
100: // ----------------------------------------------------- Instance Variables
101:
102: /**
103: * The context for this request.
104: */
105: protected Context context = null;
106:
107: /**
108: * The context path for this request.
109: */
110: protected String contextPath = null;
111:
112: /**
113: * If this request is cross context, since this changes session accesss
114: * behavior.
115: */
116: protected boolean crossContext = false;
117:
118: /**
119: * The current dispatcher type.
120: */
121: protected Object dispatcherType = null;
122:
123: /**
124: * Descriptive information about this implementation.
125: */
126: protected static final String info = "org.apache.catalina.core.ApplicationHttpRequest/1.0";
127:
128: /**
129: * The request parameters for this request. This is initialized from the
130: * wrapped request, but updates are allowed.
131: */
132: protected Map parameters = null;
133:
134: /**
135: * Have the parameters for this request already been parsed?
136: */
137: private boolean parsedParams = false;
138:
139: /**
140: * The path information for this request.
141: */
142: protected String pathInfo = null;
143:
144: /**
145: * The query parameters for the current request.
146: */
147: private String queryParamString = null;
148:
149: /**
150: * The query string for this request.
151: */
152: protected String queryString = null;
153:
154: /**
155: * The current request dispatcher path.
156: */
157: protected Object requestDispatcherPath = null;
158:
159: /**
160: * The request URI for this request.
161: */
162: protected String requestURI = null;
163:
164: /**
165: * The servlet path for this request.
166: */
167: protected String servletPath = null;
168:
169: /**
170: * The currently active session for this request.
171: */
172: protected Session session = null;
173:
174: /**
175: * Special attributes.
176: */
177: protected Object[] specialAttributes = new Object[specials.length];
178:
179: // ------------------------------------------------- ServletRequest Methods
180:
181: /**
182: * Override the <code>getAttribute()</code> method of the wrapped request.
183: *
184: * @param name Name of the attribute to retrieve
185: */
186: public Object getAttribute(String name) {
187:
188: if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
189: return dispatcherType;
190: } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
191: if (requestDispatcherPath != null) {
192: return requestDispatcherPath.toString();
193: } else {
194: return null;
195: }
196: }
197:
198: int pos = getSpecial(name);
199: if (pos == -1) {
200: return getRequest().getAttribute(name);
201: } else {
202: if ((specialAttributes[pos] == null)
203: && (specialAttributes[5] == null) && (pos >= 5)) {
204: // If it's a forward special attribute, and null, it means this
205: // is an include, so we check the wrapped request since
206: // the request could have been forwarded before the include
207: return getRequest().getAttribute(name);
208: } else {
209: return specialAttributes[pos];
210: }
211: }
212:
213: }
214:
215: /**
216: * Override the <code>getAttributeNames()</code> method of the wrapped
217: * request.
218: */
219: public Enumeration getAttributeNames() {
220: return (new AttributeNamesEnumerator());
221: }
222:
223: /**
224: * Override the <code>removeAttribute()</code> method of the
225: * wrapped request.
226: *
227: * @param name Name of the attribute to remove
228: */
229: public void removeAttribute(String name) {
230:
231: if (!removeSpecial(name))
232: getRequest().removeAttribute(name);
233:
234: }
235:
236: /**
237: * Override the <code>setAttribute()</code> method of the
238: * wrapped request.
239: *
240: * @param name Name of the attribute to set
241: * @param value Value of the attribute to set
242: */
243: public void setAttribute(String name, Object value) {
244:
245: if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
246: dispatcherType = value;
247: return;
248: } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
249: requestDispatcherPath = value;
250: return;
251: }
252:
253: if (!setSpecial(name, value)) {
254: getRequest().setAttribute(name, value);
255: }
256:
257: }
258:
259: /**
260: * Return a RequestDispatcher that wraps the resource at the specified
261: * path, which may be interpreted as relative to the current request path.
262: *
263: * @param path Path of the resource to be wrapped
264: */
265: public RequestDispatcher getRequestDispatcher(String path) {
266:
267: if (context == null)
268: return (null);
269:
270: // If the path is already context-relative, just pass it through
271: if (path == null)
272: return (null);
273: else if (path.startsWith("/"))
274: return (context.getServletContext()
275: .getRequestDispatcher(path));
276:
277: // Convert a request-relative path to a context-relative one
278: String servletPath = (String) getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
279: if (servletPath == null)
280: servletPath = getServletPath();
281:
282: // Add the path info, if there is any
283: String pathInfo = getPathInfo();
284: String requestPath = null;
285:
286: if (pathInfo == null) {
287: requestPath = servletPath;
288: } else {
289: requestPath = servletPath + pathInfo;
290: }
291:
292: int pos = requestPath.lastIndexOf('/');
293: String relative = null;
294: if (pos >= 0) {
295: relative = RequestUtil.normalize(requestPath.substring(0,
296: pos + 1)
297: + path);
298: } else {
299: relative = RequestUtil.normalize(requestPath + path);
300: }
301:
302: return (context.getServletContext()
303: .getRequestDispatcher(relative));
304:
305: }
306:
307: // --------------------------------------------- HttpServletRequest Methods
308:
309: /**
310: * Override the <code>getContextPath()</code> method of the wrapped
311: * request.
312: */
313: public String getContextPath() {
314:
315: return (this .contextPath);
316:
317: }
318:
319: /**
320: * Override the <code>getParameter()</code> method of the wrapped request.
321: *
322: * @param name Name of the requested parameter
323: */
324: public String getParameter(String name) {
325:
326: parseParameters();
327:
328: Object value = parameters.get(name);
329: if (value == null)
330: return (null);
331: else if (value instanceof String[])
332: return (((String[]) value)[0]);
333: else if (value instanceof String)
334: return ((String) value);
335: else
336: return (value.toString());
337:
338: }
339:
340: /**
341: * Override the <code>getParameterMap()</code> method of the
342: * wrapped request.
343: */
344: public Map getParameterMap() {
345:
346: parseParameters();
347: return (parameters);
348:
349: }
350:
351: /**
352: * Override the <code>getParameterNames()</code> method of the
353: * wrapped request.
354: */
355: public Enumeration getParameterNames() {
356:
357: parseParameters();
358: return (new Enumerator(parameters.keySet()));
359:
360: }
361:
362: /**
363: * Override the <code>getParameterValues()</code> method of the
364: * wrapped request.
365: *
366: * @param name Name of the requested parameter
367: */
368: public String[] getParameterValues(String name) {
369:
370: parseParameters();
371: Object value = parameters.get(name);
372: if (value == null)
373: return ((String[]) null);
374: else if (value instanceof String[])
375: return ((String[]) value);
376: else if (value instanceof String) {
377: String values[] = new String[1];
378: values[0] = (String) value;
379: return (values);
380: } else {
381: String values[] = new String[1];
382: values[0] = value.toString();
383: return (values);
384: }
385:
386: }
387:
388: /**
389: * Override the <code>getPathInfo()</code> method of the wrapped request.
390: */
391: public String getPathInfo() {
392:
393: return (this .pathInfo);
394:
395: }
396:
397: /**
398: * Override the <code>getQueryString()</code> method of the wrapped
399: * request.
400: */
401: public String getQueryString() {
402:
403: return (this .queryString);
404:
405: }
406:
407: /**
408: * Override the <code>getRequestURI()</code> method of the wrapped
409: * request.
410: */
411: public String getRequestURI() {
412:
413: return (this .requestURI);
414:
415: }
416:
417: /**
418: * Override the <code>getRequestURL()</code> method of the wrapped
419: * request.
420: */
421: public StringBuffer getRequestURL() {
422:
423: StringBuffer url = new StringBuffer();
424: String scheme = getScheme();
425: int port = getServerPort();
426: if (port < 0)
427: port = 80; // Work around java.net.URL bug
428:
429: url.append(scheme);
430: url.append("://");
431: url.append(getServerName());
432: if ((scheme.equals("http") && (port != 80))
433: || (scheme.equals("https") && (port != 443))) {
434: url.append(':');
435: url.append(port);
436: }
437: url.append(getRequestURI());
438:
439: return (url);
440:
441: }
442:
443: /**
444: * Override the <code>getServletPath()</code> method of the wrapped
445: * request.
446: */
447: public String getServletPath() {
448:
449: return (this .servletPath);
450:
451: }
452:
453: /**
454: * Return the session associated with this Request, creating one
455: * if necessary.
456: */
457: public HttpSession getSession() {
458: return (getSession(true));
459: }
460:
461: /**
462: * Return the session associated with this Request, creating one
463: * if necessary and requested.
464: *
465: * @param create Create a new session if one does not exist
466: */
467: public HttpSession getSession(boolean create) {
468:
469: if (crossContext) {
470:
471: // There cannot be a session if no context has been assigned yet
472: if (context == null)
473: return (null);
474:
475: // Return the current session if it exists and is valid
476: if (session != null && session.isValid()) {
477: return (session.getSession());
478: }
479:
480: HttpSession other = super .getSession(false);
481: if (create && (other == null)) {
482: // First create a session in the first context: the problem is
483: // that the top level request is the only one which can
484: // create the cookie safely
485: other = super .getSession(true);
486: }
487: if (other != null) {
488: Session localSession = null;
489: try {
490: localSession = context.getManager().findSession(
491: other.getId());
492: } catch (IOException e) {
493: // Ignore
494: }
495: if (localSession == null && create) {
496: localSession = context.getManager().createSession(
497: other.getId());
498: }
499: if (localSession != null) {
500: localSession.access();
501: session = localSession;
502: return session.getSession();
503: }
504: }
505: return null;
506:
507: } else {
508: return super .getSession(create);
509: }
510:
511: }
512:
513: /**
514: * Returns true if the request specifies a JSESSIONID that is valid within
515: * the context of this ApplicationHttpRequest, false otherwise.
516: *
517: * @return true if the request specifies a JSESSIONID that is valid within
518: * the context of this ApplicationHttpRequest, false otherwise.
519: */
520: public boolean isRequestedSessionIdValid() {
521:
522: if (crossContext) {
523:
524: String requestedSessionId = getRequestedSessionId();
525: if (requestedSessionId == null)
526: return (false);
527: if (context == null)
528: return (false);
529: Manager manager = context.getManager();
530: if (manager == null)
531: return (false);
532: Session session = null;
533: try {
534: session = manager.findSession(requestedSessionId);
535: } catch (IOException e) {
536: session = null;
537: }
538: if ((session != null) && session.isValid()) {
539: return (true);
540: } else {
541: return (false);
542: }
543:
544: } else {
545: return super .isRequestedSessionIdValid();
546: }
547: }
548:
549: // -------------------------------------------------------- Package Methods
550:
551: /**
552: * Recycle this request
553: */
554: public void recycle() {
555: if (session != null) {
556: session.endAccess();
557: }
558: }
559:
560: /**
561: * Return descriptive information about this implementation.
562: */
563: public String getInfo() {
564:
565: return (info);
566:
567: }
568:
569: /**
570: * Perform a shallow copy of the specified Map, and return the result.
571: *
572: * @param orig Origin Map to be copied
573: */
574: Map copyMap(Map orig) {
575:
576: if (orig == null)
577: return (new HashMap());
578: HashMap dest = new HashMap();
579: Iterator keys = orig.keySet().iterator();
580: while (keys.hasNext()) {
581: String key = (String) keys.next();
582: dest.put(key, orig.get(key));
583: }
584: return (dest);
585:
586: }
587:
588: /**
589: * Set the context path for this request.
590: *
591: * @param contextPath The new context path
592: */
593: void setContextPath(String contextPath) {
594:
595: this .contextPath = contextPath;
596:
597: }
598:
599: /**
600: * Set the path information for this request.
601: *
602: * @param pathInfo The new path info
603: */
604: void setPathInfo(String pathInfo) {
605:
606: this .pathInfo = pathInfo;
607:
608: }
609:
610: /**
611: * Set the query string for this request.
612: *
613: * @param queryString The new query string
614: */
615: void setQueryString(String queryString) {
616:
617: this .queryString = queryString;
618:
619: }
620:
621: /**
622: * Set the request that we are wrapping.
623: *
624: * @param request The new wrapped request
625: */
626: void setRequest(HttpServletRequest request) {
627:
628: super .setRequest(request);
629:
630: // Initialize the attributes for this request
631: dispatcherType = request
632: .getAttribute(Globals.DISPATCHER_TYPE_ATTR);
633: requestDispatcherPath = request
634: .getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
635:
636: // Initialize the path elements for this request
637: contextPath = request.getContextPath();
638: pathInfo = request.getPathInfo();
639: queryString = request.getQueryString();
640: requestURI = request.getRequestURI();
641: servletPath = request.getServletPath();
642:
643: }
644:
645: /**
646: * Set the request URI for this request.
647: *
648: * @param requestURI The new request URI
649: */
650: void setRequestURI(String requestURI) {
651:
652: this .requestURI = requestURI;
653:
654: }
655:
656: /**
657: * Set the servlet path for this request.
658: *
659: * @param servletPath The new servlet path
660: */
661: void setServletPath(String servletPath) {
662:
663: this .servletPath = servletPath;
664:
665: }
666:
667: /**
668: * Parses the parameters of this request.
669: *
670: * If parameters are present in both the query string and the request
671: * content, they are merged.
672: */
673: void parseParameters() {
674:
675: if (parsedParams) {
676: return;
677: }
678:
679: parameters = new HashMap();
680: parameters = copyMap(getRequest().getParameterMap());
681: mergeParameters();
682: parsedParams = true;
683: }
684:
685: /**
686: * Save query parameters for this request.
687: *
688: * @param queryString The query string containing parameters for this
689: * request
690: */
691: void setQueryParams(String queryString) {
692: this .queryParamString = queryString;
693: }
694:
695: // ------------------------------------------------------ Protected Methods
696:
697: /**
698: * Is this attribute name one of the special ones that is added only for
699: * included servlets?
700: *
701: * @param name Attribute name to be tested
702: */
703: protected boolean isSpecial(String name) {
704:
705: for (int i = 0; i < specials.length; i++) {
706: if (specials[i].equals(name))
707: return (true);
708: }
709: return (false);
710:
711: }
712:
713: /**
714: * Get a special attribute.
715: *
716: * @return the special attribute pos, or -1 if it is not a special
717: * attribute
718: */
719: protected int getSpecial(String name) {
720: for (int i = 0; i < specials.length; i++) {
721: if (specials[i].equals(name)) {
722: return (i);
723: }
724: }
725: return (-1);
726: }
727:
728: /**
729: * Set a special attribute.
730: *
731: * @return true if the attribute was a special attribute, false otherwise
732: */
733: protected boolean setSpecial(String name, Object value) {
734: for (int i = 0; i < specials.length; i++) {
735: if (specials[i].equals(name)) {
736: specialAttributes[i] = value;
737: return (true);
738: }
739: }
740: return (false);
741: }
742:
743: /**
744: * Remove a special attribute.
745: *
746: * @return true if the attribute was a special attribute, false otherwise
747: */
748: protected boolean removeSpecial(String name) {
749: for (int i = 0; i < specials.length; i++) {
750: if (specials[i].equals(name)) {
751: specialAttributes[i] = null;
752: return (true);
753: }
754: }
755: return (false);
756: }
757:
758: /**
759: * Merge the two sets of parameter values into a single String array.
760: *
761: * @param values1 First set of values
762: * @param values2 Second set of values
763: */
764: protected String[] mergeValues(Object values1, Object values2) {
765:
766: ArrayList results = new ArrayList();
767:
768: if (values1 == null)
769: ;
770: else if (values1 instanceof String)
771: results.add(values1);
772: else if (values1 instanceof String[]) {
773: String values[] = (String[]) values1;
774: for (int i = 0; i < values.length; i++)
775: results.add(values[i]);
776: } else
777: results.add(values1.toString());
778:
779: if (values2 == null)
780: ;
781: else if (values2 instanceof String)
782: results.add(values2);
783: else if (values2 instanceof String[]) {
784: String values[] = (String[]) values2;
785: for (int i = 0; i < values.length; i++)
786: results.add(values[i]);
787: } else
788: results.add(values2.toString());
789:
790: String values[] = new String[results.size()];
791: return ((String[]) results.toArray(values));
792:
793: }
794:
795: // ------------------------------------------------------ Private Methods
796:
797: /**
798: * Merge the parameters from the saved query parameter string (if any), and
799: * the parameters already present on this request (if any), such that the
800: * parameter values from the query string show up first if there are
801: * duplicate parameter names.
802: */
803: private void mergeParameters() {
804:
805: if ((queryParamString == null)
806: || (queryParamString.length() < 1))
807: return;
808:
809: HashMap queryParameters = new HashMap();
810: String encoding = getCharacterEncoding();
811: if (encoding == null)
812: encoding = "ISO-8859-1";
813: try {
814: RequestUtil.parseParameters(queryParameters,
815: queryParamString, encoding);
816: } catch (Exception e) {
817: ;
818: }
819: Iterator keys = parameters.keySet().iterator();
820: while (keys.hasNext()) {
821: String key = (String) keys.next();
822: Object value = queryParameters.get(key);
823: if (value == null) {
824: queryParameters.put(key, parameters.get(key));
825: continue;
826: }
827: queryParameters.put(key, mergeValues(value, parameters
828: .get(key)));
829: }
830: parameters = queryParameters;
831:
832: }
833:
834: // ----------------------------------- AttributeNamesEnumerator Inner Class
835:
836: /**
837: * Utility class used to expose the special attributes as being available
838: * as request attributes.
839: */
840: protected class AttributeNamesEnumerator implements Enumeration {
841:
842: protected int pos = -1;
843: protected int last = -1;
844: protected Enumeration parentEnumeration = null;
845: protected String next = null;
846:
847: public AttributeNamesEnumerator() {
848: parentEnumeration = getRequest().getAttributeNames();
849: for (int i = 0; i < specialAttributes.length; i++) {
850: if (getAttribute(specials[i]) != null) {
851: last = i;
852: }
853: }
854: }
855:
856: public boolean hasMoreElements() {
857: return ((pos != last) || (next != null) || ((next = findNext()) != null));
858: }
859:
860: public Object nextElement() {
861: if (pos != last) {
862: for (int i = pos + 1; i <= last; i++) {
863: if (getAttribute(specials[i]) != null) {
864: pos = i;
865: return (specials[i]);
866: }
867: }
868: }
869: String result = next;
870: if (next != null) {
871: next = findNext();
872: } else {
873: throw new NoSuchElementException();
874: }
875: return result;
876: }
877:
878: protected String findNext() {
879: String result = null;
880: while ((result == null)
881: && (parentEnumeration.hasMoreElements())) {
882: String current = (String) parentEnumeration
883: .nextElement();
884: if (!isSpecial(current)) {
885: result = current;
886: }
887: }
888: return result;
889: }
890:
891: }
892:
893: }
|