001: /*
002: * Copyright 2002-2007 the original author or authors.
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.springframework.mock.web;
018:
019: import java.io.BufferedReader;
020: import java.io.ByteArrayInputStream;
021: import java.io.InputStream;
022: import java.io.InputStreamReader;
023: import java.io.Reader;
024: import java.io.UnsupportedEncodingException;
025: import java.security.Principal;
026: import java.util.Collection;
027: import java.util.Collections;
028: import java.util.Date;
029: import java.util.Enumeration;
030: import java.util.HashSet;
031: import java.util.Hashtable;
032: import java.util.Locale;
033: import java.util.Map;
034: import java.util.Set;
035: import java.util.Vector;
036:
037: import javax.servlet.RequestDispatcher;
038: import javax.servlet.ServletContext;
039: import javax.servlet.ServletInputStream;
040: import javax.servlet.http.Cookie;
041: import javax.servlet.http.HttpServletRequest;
042: import javax.servlet.http.HttpSession;
043:
044: import org.springframework.core.CollectionFactory;
045: import org.springframework.util.Assert;
046:
047: /**
048: * Mock implementation of the {@link javax.servlet.http.HttpServletRequest}
049: * interface. Supports the Servlet 2.4 API level.
050: *
051: * <p>Used for testing the web framework; also useful for testing
052: * application controllers.
053: *
054: * @author Juergen Hoeller
055: * @author Rod Johnson
056: * @author Rick Evans
057: * @since 1.0.2
058: */
059: public class MockHttpServletRequest implements HttpServletRequest {
060:
061: /**
062: * The default protocol: 'http'.
063: */
064: public static final String DEFAULT_PROTOCOL = "http";
065:
066: /**
067: * The default server address: '127.0.0.1'.
068: */
069: public static final String DEFAULT_SERVER_ADDR = "127.0.0.1";
070:
071: /**
072: * The default server name: 'localhost'.
073: */
074: public static final String DEFAULT_SERVER_NAME = "localhost";
075:
076: /**
077: * The default server port: '80'.
078: */
079: public static final int DEFAULT_SERVER_PORT = 80;
080:
081: /**
082: * The default remote address: '127.0.0.1'.
083: */
084: public static final String DEFAULT_REMOTE_ADDR = "127.0.0.1";
085:
086: /**
087: * The default remote host: 'localhost'.
088: */
089: public static final String DEFAULT_REMOTE_HOST = "localhost";
090:
091: private boolean active = true;
092:
093: //---------------------------------------------------------------------
094: // ServletRequest properties
095: //---------------------------------------------------------------------
096:
097: private final Hashtable attributes = new Hashtable();
098:
099: private String characterEncoding;
100:
101: private byte[] content;
102:
103: private String contentType;
104:
105: private final Map parameters = CollectionFactory
106: .createLinkedMapIfPossible(16);
107:
108: private String protocol = DEFAULT_PROTOCOL;
109:
110: private String scheme = DEFAULT_PROTOCOL;
111:
112: private String serverName = DEFAULT_SERVER_NAME;
113:
114: private int serverPort = DEFAULT_SERVER_PORT;
115:
116: private String remoteAddr = DEFAULT_REMOTE_ADDR;
117:
118: private String remoteHost = DEFAULT_REMOTE_HOST;
119:
120: /** List of locales in descending order */
121: private final Vector locales = new Vector();
122:
123: private boolean secure = false;
124:
125: private final ServletContext servletContext;
126:
127: private int remotePort = DEFAULT_SERVER_PORT;
128:
129: private String localName = DEFAULT_SERVER_NAME;
130:
131: private String localAddr = DEFAULT_SERVER_ADDR;
132:
133: private int localPort = DEFAULT_SERVER_PORT;
134:
135: //---------------------------------------------------------------------
136: // HttpServletRequest properties
137: //---------------------------------------------------------------------
138:
139: private String authType;
140:
141: private Cookie[] cookies;
142:
143: /**
144: * The key is the lowercase header name; the value is a {@link HeaderValueHolder} object.
145: */
146: private final Hashtable headers = new Hashtable();
147:
148: private String method;
149:
150: private String pathInfo;
151:
152: private String contextPath = "";
153:
154: private String queryString;
155:
156: private String remoteUser;
157:
158: private final Set userRoles = new HashSet();
159:
160: private Principal userPrincipal;
161:
162: private String requestURI;
163:
164: private String servletPath = "";
165:
166: private HttpSession session;
167:
168: private boolean requestedSessionIdValid = true;
169:
170: private boolean requestedSessionIdFromCookie = true;
171:
172: private boolean requestedSessionIdFromURL = false;
173:
174: //---------------------------------------------------------------------
175: // Constructors
176: //---------------------------------------------------------------------
177:
178: /**
179: * Create a new MockHttpServletRequest with a default
180: * {@link MockServletContext}.
181: * @see MockServletContext
182: */
183: public MockHttpServletRequest() {
184: this (null, "", "");
185: }
186:
187: /**
188: * Create a new MockHttpServletRequest with a default
189: * {@link MockServletContext}.
190: * @param method the request method (may be <code>null</code>)
191: * @param requestURI the request URI (may be <code>null</code>)
192: * @see #setMethod
193: * @see #setRequestURI
194: * @see MockServletContext
195: */
196: public MockHttpServletRequest(String method, String requestURI) {
197: this (null, method, requestURI);
198: }
199:
200: /**
201: * Create a new MockHttpServletRequest.
202: * @param servletContext the ServletContext that the request runs in
203: * (may be <code>null</code> to use a default MockServletContext)
204: * @see MockServletContext
205: */
206: public MockHttpServletRequest(ServletContext servletContext) {
207: this (servletContext, "", "");
208: }
209:
210: /**
211: * Create a new MockHttpServletRequest.
212: * @param servletContext the ServletContext that the request runs in
213: * (may be <code>null</code> to use a default MockServletContext)
214: * @param method the request method (may be <code>null</code>)
215: * @param requestURI the request URI (may be <code>null</code>)
216: * @see #setMethod
217: * @see #setRequestURI
218: * @see MockServletContext
219: */
220: public MockHttpServletRequest(ServletContext servletContext,
221: String method, String requestURI) {
222: this .servletContext = (servletContext != null ? servletContext
223: : new MockServletContext());
224: this .method = method;
225: this .requestURI = requestURI;
226: this .locales.add(Locale.ENGLISH);
227: }
228:
229: //---------------------------------------------------------------------
230: // Lifecycle methods
231: //---------------------------------------------------------------------
232:
233: /**
234: * Return whether this request is still active (that is, not completed yet).
235: */
236: public boolean isActive() {
237: return this .active;
238: }
239:
240: /**
241: * Mark this request as completed, keeping its state.
242: */
243: public void close() {
244: this .active = false;
245: }
246:
247: /**
248: * Invalidate this request, clearing its state.
249: */
250: public void invalidate() {
251: close();
252: clearAttributes();
253: }
254:
255: /**
256: * Check whether this request is still active (that is, not completed yet),
257: * throwing an IllegalStateException if not active anymore.
258: */
259: protected void checkActive() throws IllegalStateException {
260: if (!this .active) {
261: throw new IllegalStateException(
262: "Request is not active anymore");
263: }
264: }
265:
266: //---------------------------------------------------------------------
267: // ServletRequest interface
268: //---------------------------------------------------------------------
269:
270: public Object getAttribute(String name) {
271: checkActive();
272: return this .attributes.get(name);
273: }
274:
275: public Enumeration getAttributeNames() {
276: checkActive();
277: return this .attributes.keys();
278: }
279:
280: public String getCharacterEncoding() {
281: return this .characterEncoding;
282: }
283:
284: public void setCharacterEncoding(String characterEncoding) {
285: this .characterEncoding = characterEncoding;
286: }
287:
288: public void setContent(byte[] content) {
289: this .content = content;
290: }
291:
292: public int getContentLength() {
293: return (this .content != null ? this .content.length : -1);
294: }
295:
296: public void setContentType(String contentType) {
297: this .contentType = contentType;
298: }
299:
300: public String getContentType() {
301: return this .contentType;
302: }
303:
304: public ServletInputStream getInputStream() {
305: if (this .content != null) {
306: return new DelegatingServletInputStream(
307: new ByteArrayInputStream(this .content));
308: } else {
309: return null;
310: }
311: }
312:
313: /**
314: * Set a single value for the specified HTTP parameter.
315: * <p>If there are already one or more values registered for the given
316: * parameter name, they will be replaced.
317: */
318: public void setParameter(String name, String value) {
319: setParameter(name, new String[] { value });
320: }
321:
322: /**
323: * Set an array of values for the specified HTTP parameter.
324: * <p>If there are already one or more values registered for the given
325: * parameter name, they will be replaced.
326: */
327: public void setParameter(String name, String[] values) {
328: Assert.notNull(name, "Parameter name must not be null");
329: this .parameters.put(name, values);
330: }
331:
332: /**
333: * Add a single value for the specified HTTP parameter.
334: * <p>If there are already one or more values registered for the given
335: * parameter name, the given value will be added to the end of the list.
336: */
337: public void addParameter(String name, String value) {
338: addParameter(name, new String[] { value });
339: }
340:
341: /**
342: * Add an array of values for the specified HTTP parameter.
343: * <p>If there are already one or more values registered for the given
344: * parameter name, the given values will be added to the end of the list.
345: */
346: public void addParameter(String name, String[] values) {
347: Assert.notNull(name, "Parameter name must not be null");
348: String[] oldArr = (String[]) this .parameters.get(name);
349: if (oldArr != null) {
350: String[] newArr = new String[oldArr.length + values.length];
351: System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
352: System.arraycopy(values, 0, newArr, oldArr.length,
353: values.length);
354: this .parameters.put(name, newArr);
355: } else {
356: this .parameters.put(name, values);
357: }
358: }
359:
360: /**
361: * Remove already registered values for the specified HTTP parameter, if any.
362: */
363: public void removeParameter(String name) {
364: Assert.notNull(name, "Parameter name must not be null");
365: this .parameters.remove(name);
366: }
367:
368: public String getParameter(String name) {
369: Assert.notNull(name, "Parameter name must not be null");
370: String[] arr = (String[]) this .parameters.get(name);
371: return (arr != null && arr.length > 0 ? arr[0] : null);
372: }
373:
374: public Enumeration getParameterNames() {
375: return Collections.enumeration(this .parameters.keySet());
376: }
377:
378: public String[] getParameterValues(String name) {
379: Assert.notNull(name, "Parameter name must not be null");
380: return (String[]) this .parameters.get(name);
381: }
382:
383: public Map getParameterMap() {
384: return Collections.unmodifiableMap(this .parameters);
385: }
386:
387: public void setProtocol(String protocol) {
388: this .protocol = protocol;
389: }
390:
391: public String getProtocol() {
392: return this .protocol;
393: }
394:
395: public void setScheme(String scheme) {
396: this .scheme = scheme;
397: }
398:
399: public String getScheme() {
400: return this .scheme;
401: }
402:
403: public void setServerName(String serverName) {
404: this .serverName = serverName;
405: }
406:
407: public String getServerName() {
408: return this .serverName;
409: }
410:
411: public void setServerPort(int serverPort) {
412: this .serverPort = serverPort;
413: }
414:
415: public int getServerPort() {
416: return this .serverPort;
417: }
418:
419: public BufferedReader getReader()
420: throws UnsupportedEncodingException {
421: if (this .content != null) {
422: InputStream sourceStream = new ByteArrayInputStream(
423: this .content);
424: Reader sourceReader = (this .characterEncoding != null) ? new InputStreamReader(
425: sourceStream, this .characterEncoding)
426: : new InputStreamReader(sourceStream);
427: return new BufferedReader(sourceReader);
428: } else {
429: return null;
430: }
431: }
432:
433: public void setRemoteAddr(String remoteAddr) {
434: this .remoteAddr = remoteAddr;
435: }
436:
437: public String getRemoteAddr() {
438: return this .remoteAddr;
439: }
440:
441: public void setRemoteHost(String remoteHost) {
442: this .remoteHost = remoteHost;
443: }
444:
445: public String getRemoteHost() {
446: return this .remoteHost;
447: }
448:
449: public void setAttribute(String name, Object value) {
450: checkActive();
451: Assert.notNull(name, "Attribute name must not be null");
452: if (value != null) {
453: this .attributes.put(name, value);
454: } else {
455: this .attributes.remove(name);
456: }
457: }
458:
459: public void removeAttribute(String name) {
460: checkActive();
461: Assert.notNull(name, "Attribute name must not be null");
462: this .attributes.remove(name);
463: }
464:
465: /**
466: * Clear all of this request's attributes.
467: */
468: public void clearAttributes() {
469: this .attributes.clear();
470: }
471:
472: /**
473: * Add a new preferred locale, before any existing locales.
474: */
475: public void addPreferredLocale(Locale locale) {
476: Assert.notNull(locale, "Locale must not be null");
477: this .locales.add(0, locale);
478: }
479:
480: public Locale getLocale() {
481: return (Locale) this .locales.get(0);
482: }
483:
484: public Enumeration getLocales() {
485: return this .locales.elements();
486: }
487:
488: public void setSecure(boolean secure) {
489: this .secure = secure;
490: }
491:
492: public boolean isSecure() {
493: return this .secure;
494: }
495:
496: public RequestDispatcher getRequestDispatcher(String path) {
497: return new MockRequestDispatcher(path);
498: }
499:
500: public String getRealPath(String path) {
501: return this .servletContext.getRealPath(path);
502: }
503:
504: public void setRemotePort(int remotePort) {
505: this .remotePort = remotePort;
506: }
507:
508: public int getRemotePort() {
509: return this .remotePort;
510: }
511:
512: public void setLocalName(String localName) {
513: this .localName = localName;
514: }
515:
516: public String getLocalName() {
517: return this .localName;
518: }
519:
520: public void setLocalAddr(String localAddr) {
521: this .localAddr = localAddr;
522: }
523:
524: public String getLocalAddr() {
525: return this .localAddr;
526: }
527:
528: public void setLocalPort(int localPort) {
529: this .localPort = localPort;
530: }
531:
532: public int getLocalPort() {
533: return this .localPort;
534: }
535:
536: //---------------------------------------------------------------------
537: // HttpServletRequest interface
538: //---------------------------------------------------------------------
539:
540: public void setAuthType(String authType) {
541: this .authType = authType;
542: }
543:
544: public String getAuthType() {
545: return this .authType;
546: }
547:
548: public void setCookies(Cookie[] cookies) {
549: this .cookies = cookies;
550: }
551:
552: public Cookie[] getCookies() {
553: return this .cookies;
554: }
555:
556: /**
557: * Add a header entry for the given name.
558: * <p>If there was no entry for that header name before,
559: * the value will be used as-is. In case of an existing entry,
560: * a String array will be created, adding the given value (more
561: * specifically, its toString representation) as further element.
562: * <p>Multiple values can only be stored as list of Strings,
563: * following the Servlet spec (see <code>getHeaders</code> accessor).
564: * As alternative to repeated <code>addHeader</code> calls for
565: * individual elements, you can use a single call with an entire
566: * array or Collection of values as parameter.
567: * @see #getHeaderNames
568: * @see #getHeader
569: * @see #getHeaders
570: * @see #getDateHeader
571: * @see #getIntHeader
572: */
573: public void addHeader(String name, Object value) {
574: HeaderValueHolder header = HeaderValueHolder.getByName(
575: this .headers, name);
576: Assert.notNull(value, "Header value must not be null");
577: if (header == null) {
578: header = new HeaderValueHolder();
579: this .headers.put(name, header);
580: }
581: if (value instanceof Collection) {
582: header.addValues((Collection) value);
583: } else if (value.getClass().isArray()) {
584: header.addValueArray(value);
585: } else {
586: header.addValue(value);
587: }
588: }
589:
590: public long getDateHeader(String name) {
591: HeaderValueHolder header = HeaderValueHolder.getByName(
592: this .headers, name);
593: Object value = (header != null ? header.getValue() : null);
594: if (value instanceof Date) {
595: return ((Date) value).getTime();
596: } else if (value instanceof Number) {
597: return ((Number) value).longValue();
598: } else if (value != null) {
599: throw new IllegalArgumentException("Value for header '"
600: + name + "' is neither a Date nor a Number: "
601: + value);
602: } else {
603: return -1L;
604: }
605: }
606:
607: public String getHeader(String name) {
608: HeaderValueHolder header = HeaderValueHolder.getByName(
609: this .headers, name);
610: return (header != null ? header.getValue().toString() : null);
611: }
612:
613: public Enumeration getHeaders(String name) {
614: HeaderValueHolder header = HeaderValueHolder.getByName(
615: this .headers, name);
616: return Collections.enumeration(header != null ? header
617: .getValues() : Collections.EMPTY_LIST);
618: }
619:
620: public Enumeration getHeaderNames() {
621: return this .headers.keys();
622: }
623:
624: public int getIntHeader(String name) {
625: HeaderValueHolder header = HeaderValueHolder.getByName(
626: this .headers, name);
627: Object value = (header != null ? header.getValue() : null);
628: if (value instanceof Number) {
629: return ((Number) value).intValue();
630: } else if (value instanceof String) {
631: return Integer.parseInt((String) value);
632: } else if (value != null) {
633: throw new NumberFormatException("Value for header '" + name
634: + "' is not a Number: " + value);
635: } else {
636: return -1;
637: }
638: }
639:
640: public void setMethod(String method) {
641: this .method = method;
642: }
643:
644: public String getMethod() {
645: return this .method;
646: }
647:
648: public void setPathInfo(String pathInfo) {
649: this .pathInfo = pathInfo;
650: }
651:
652: public String getPathInfo() {
653: return this .pathInfo;
654: }
655:
656: public String getPathTranslated() {
657: return (this .pathInfo != null ? getRealPath(this .pathInfo)
658: : null);
659: }
660:
661: public void setContextPath(String contextPath) {
662: this .contextPath = contextPath;
663: }
664:
665: public String getContextPath() {
666: return this .contextPath;
667: }
668:
669: public void setQueryString(String queryString) {
670: this .queryString = queryString;
671: }
672:
673: public String getQueryString() {
674: return this .queryString;
675: }
676:
677: public void setRemoteUser(String remoteUser) {
678: this .remoteUser = remoteUser;
679: }
680:
681: public String getRemoteUser() {
682: return this .remoteUser;
683: }
684:
685: /**
686: * @deprecated in favor of addUserRole
687: * @see #addUserRole
688: */
689: public void addRole(String role) {
690: addUserRole(role);
691: }
692:
693: public void addUserRole(String role) {
694: this .userRoles.add(role);
695: }
696:
697: public boolean isUserInRole(String role) {
698: return this .userRoles.contains(role);
699: }
700:
701: public void setUserPrincipal(Principal userPrincipal) {
702: this .userPrincipal = userPrincipal;
703: }
704:
705: public Principal getUserPrincipal() {
706: return this .userPrincipal;
707: }
708:
709: public String getRequestedSessionId() {
710: HttpSession session = getSession();
711: return (session != null ? session.getId() : null);
712: }
713:
714: public void setRequestURI(String requestURI) {
715: this .requestURI = requestURI;
716: }
717:
718: public String getRequestURI() {
719: return this .requestURI;
720: }
721:
722: public StringBuffer getRequestURL() {
723: StringBuffer url = new StringBuffer(this .scheme);
724: url.append("://").append(this .serverName).append(':').append(
725: this .serverPort);
726: url.append(getRequestURI());
727: return url;
728: }
729:
730: public void setServletPath(String servletPath) {
731: this .servletPath = servletPath;
732: }
733:
734: public String getServletPath() {
735: return this .servletPath;
736: }
737:
738: public void setSession(HttpSession session) {
739: this .session = session;
740: if (session instanceof MockHttpSession) {
741: MockHttpSession mockSession = ((MockHttpSession) session);
742: mockSession.access();
743: }
744: }
745:
746: public HttpSession getSession(boolean create) {
747: checkActive();
748: // Reset session if invalidated.
749: if (this .session instanceof MockHttpSession
750: && ((MockHttpSession) this .session).isInvalid()) {
751: this .session = null;
752: }
753: // Create new session if necessary.
754: if (this .session == null && create) {
755: this .session = new MockHttpSession(this .servletContext);
756: }
757: return this .session;
758: }
759:
760: public HttpSession getSession() {
761: return getSession(true);
762: }
763:
764: public void setRequestedSessionIdValid(
765: boolean requestedSessionIdValid) {
766: this .requestedSessionIdValid = requestedSessionIdValid;
767: }
768:
769: public boolean isRequestedSessionIdValid() {
770: return this .requestedSessionIdValid;
771: }
772:
773: public void setRequestedSessionIdFromCookie(
774: boolean requestedSessionIdFromCookie) {
775: this .requestedSessionIdFromCookie = requestedSessionIdFromCookie;
776: }
777:
778: public boolean isRequestedSessionIdFromCookie() {
779: return this .requestedSessionIdFromCookie;
780: }
781:
782: public void setRequestedSessionIdFromURL(
783: boolean requestedSessionIdFromURL) {
784: this .requestedSessionIdFromURL = requestedSessionIdFromURL;
785: }
786:
787: public boolean isRequestedSessionIdFromURL() {
788: return this .requestedSessionIdFromURL;
789: }
790:
791: public boolean isRequestedSessionIdFromUrl() {
792: return isRequestedSessionIdFromURL();
793: }
794:
795: }
|