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