/* Copyright 2005-2006 Tim Fennell
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sourceforge.stripes.mock;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* <p>Mock implementation of an HttpServletRequest object. Allows for setting most values that
* are likely to be of interest (and can always be subclassed to affect others). Of key interest
* and perhaps not completely obvious, the way to get request parameters into an instance of
* MockHttpServletRequest is to fetch the parameter map using getParameterMap() and use the
* put() and putAll() methods on it. Values must be String arrays. Examples follow:</p>
*
* <pre>
* MockHttpServletRequest req = new MockHttpServletRequest("/foo", "/bar.action");
* req.getParameterMap().put("param1", new String[] {"value"});
* req.getParameterMap().put("param2", new String[] {"value1", "value2"});
* </pre>
*
* <p>It should also be noted that unless you generate an instance of MockHttpSession (or
* another implementation of HttpSession) and set it on the request, then your request will
* <i>never</i> have a session associated with it.</p>
*
* @author Tim Fennell
* @since Stripes 1.1.1
*/
public class MockHttpServletRequest implements HttpServletRequest {
private String authType;
private Cookie[] cookies;
private Map<String,Object> headers = new HashMap<String,Object>();
private Map<String,Object> attributes = new HashMap<String,Object>();
private Map<String,String[]> parameters = new HashMap<String,String[]>();
private String method = "POST";
private HttpSession session;
private String characterEncoding = "UTF-8";
private List<Locale> locales = new ArrayList<Locale>();
private Principal userPrincipal;
private Set<String> roles = new HashSet<String>();
private String forwardUrl;
private List<String> includedUrls = new ArrayList<String>();
private byte[] requestBody = new byte[0];
// All the bits of the URL
private String protocol = "https";
private String serverName = "localhost";
private int serverPort = 8080;
private String contextPath = "";
private String servletPath = "";
private String pathInfo = "";
private String queryString = "";
private MockAsyncContext asyncContext = null;
/**
* Minimal constructor that makes sense. Requires a context path (should be the same as
* the name of the servlet context, prepended with a '/') and a servlet path. E.g.
* new MockHttpServletRequest("/myapp", "/actionType/foo.action").
* @param contextPath
* @param servletPath
*/
public MockHttpServletRequest(
String contextPath,
String servletPath) {
this.contextPath = contextPath;
this.servletPath = servletPath;
}
/** Sets the auth type that will be reported by this request. */
public void setAuthType(String authType) { this.authType = authType; }
/** Gets the auth type being used by this request. */
public String getAuthType() { return this.authType; }
/** Sets the array of cookies that will be available from the request. */
public void setCookies(Cookie[] cookies) { this.cookies = cookies; }
/** Returns any cookies that are set on the request. */
public Cookie[] getCookies() { return this.cookies; }
/**
* Allows headers to be set on the request. These will be returned by the various getXxHeader()
* methods. If the header is a date header it should be set with a Long. If the header is an
* Int header it should be set with an Integer.
*/
public void addHeader(String name, Object value) {
this.headers.put(name.toLowerCase(), value);
}
/** Gets the named header as a long. Must have been set as a long with addHeader(). */
public long getDateHeader(String name) { return (Long) this.headers.get(name); }
/** Returns any header as a String if it exists. */
public String getHeader(String name) {
final Object header = this.headers.get(name == null ? null : name.toLowerCase());
return header == null ? null : header.toString();
}
/** Returns an enumeration with single value of the named header, or an empty enum if no value. */
public Enumeration<String> getHeaders(String name) {
String header = getHeader(name);
Collection<String> values = new ArrayList<String>();
if (header != null) {
values.add(header);
}
return Collections.enumeration(values);
}
/** Returns an enumeration containing all the names of headers supplied. */
public Enumeration<String> getHeaderNames() { return Collections.enumeration(headers.keySet()); }
/** Gets the named header as an int. Must have been set as an Integer with addHeader(). */
public int getIntHeader(String name) {
String headerValue = getHeader( name );
if( headerValue == null ) return -1;
return Integer.parseInt( headerValue );
}
/** Sets the method used by the request. Defaults to POST. */
public void setMethod(String method) { this.method = method; }
/** Gets the method used by the request. Defaults to POST. */
public String getMethod() { return this.method; }
/** Sets the path info. Defaults to the empty string. */
public void setPathInfo(String pathInfo) { this.pathInfo = pathInfo; }
/** Returns the path info. Defaults to the empty string. */
public String getPathInfo() { return this.pathInfo; }
/** Always returns the same as getPathInfo(). */
public String getPathTranslated() { return getPathInfo(); }
/** Sets the context path. Defaults to the empty string. */
public void setContextPath(String contextPath) { this.contextPath = contextPath; }
/** Returns the context path. Defaults to the empty string. */
public String getContextPath() { return this.contextPath; }
/** Sets the query string set on the request; this value is not parsed for anything. */
public void setQueryString(String queryString) { this.queryString = queryString; }
/** Returns the query string set on the request. */
public String getQueryString() { return this.queryString; }
/** Returns the name from the user principal if one exists, otherwise null. */
public String getRemoteUser() {
Principal p = getUserPrincipal();
return p == null ? null : p.getName();
}
/** Sets the set of roles that the user is deemed to be in for the request. */
public void setRoles(Set<String> roles) { this.roles = roles; }
/** Returns true if the set of roles contains the role specified, false otherwise. */
public boolean isUserInRole(String role) {
return this.roles.contains(role);
}
/** Sets the Principal for the current request. */
public void setUserPrincipal(Principal userPrincipal) { this.userPrincipal = userPrincipal; }
/** Returns the Principal if one is set on the request. */
public Principal getUserPrincipal() { return this.userPrincipal; }
/** Returns the ID of the session if one is attached to this request. Otherwise null. */
public String getRequestedSessionId() {
if (this.session == null) {
return null;
}
return this.session.getId();
}
/** Returns the request URI as defined by the servlet spec. */
public String getRequestURI() { return this.contextPath + this.servletPath + this.pathInfo; }
/** Returns (an attempt at) a reconstructed URL based on its constituent parts. */
public StringBuffer getRequestURL() {
return new StringBuffer().append(this.protocol)
.append("://")
.append(this.serverName)
.append(":")
.append(this.serverPort)
.append(this.contextPath)
.append(this.servletPath)
.append(this.pathInfo);
}
/** Gets the part of the path which matched the servlet. */
public String getServletPath() { return this.servletPath; }
/** Gets the session object attached to this request. */
public HttpSession getSession(boolean b) { return this.session; }
/** Gets the session object attached to this request. */
public HttpSession getSession() { return this.session; }
/** Allows a session to be associated with the request. */
public void setSession(HttpSession session) { this.session = session; }
/** Always returns true. */
public boolean isRequestedSessionIdValid() { return true; }
/** Always returns true. */
public boolean isRequestedSessionIdFromCookie() { return true; }
/** Always returns false. */
public boolean isRequestedSessionIdFromURL() { return false; }
/** Always returns false. */
public boolean isRequestedSessionIdFromUrl() { return false; }
/** Gets the named request attribute from an internal Map. */
public Object getAttribute(String key) { return this.attributes.get(key); }
/** Gets an enumeration of all request attribute names. */
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(this.attributes.keySet());
}
/** Gets the character encoding, defaults to UTF-8. */
public String getCharacterEncoding() { return this.characterEncoding; }
/** Sets the character encoding that will be returned by getCharacterEncoding(). */
public void setCharacterEncoding(String encoding) { this.characterEncoding = encoding; }
/** Always returns -1 (unknown). */
public int getContentLength() { return requestBody.length; }
/** Always returns null. */
public String getContentType() {
return getHeader("content-type");
}
/** Always returns null. */
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
ByteArrayInputStream wrappedStream = new ByteArrayInputStream( requestBody );
public final InputStream getWrappedInputStream() {
return wrappedStream;
}
@Override
public int read() throws IOException {
return wrappedStream.read();
}
@Override
public void close() throws IOException {
wrappedStream.close();
}
@Override
public boolean isFinished() {
return wrappedStream.available()==0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
/** Gets the first value of the named parameter or null if a value does not exist. */
public String getParameter(String name) {
String[] values = getParameterValues(name);
if (values != null && values.length > 0) {
return values[0];
}
return null;
}
/** Gets an enumeration containing all the parameter names present. */
public Enumeration<String> getParameterNames() {
return Collections.enumeration(this.parameters.keySet());
}
/** Returns an array of all values for a parameter, or null if the parameter does not exist. */
public String[] getParameterValues(String name) {
return this.parameters.get(name);
}
/**
* Provides access to the parameter map. Note that this returns a reference to the live,
* modifiable parameter map. As a result it can be used to insert parameters when constructing
* the request.
*/
public Map<String,String[]> getParameterMap() {
return this.parameters;
}
/** Sets the protocol for the request. Defaults to "https". */
public void setProtocol(String protocol) { this.protocol = protocol; }
/** Gets the protocol for the request. Defaults to "https". */
public String getProtocol() { return this.protocol; }
/** Always returns the same as getProtocol. */
public String getScheme() { return getProtocol(); }
/** Sets the server name. Defaults to "localhost". */
public void setServerName(String serverName) { this.serverName = serverName; }
/** Gets the server name. Defaults to "localhost". */
public String getServerName() { return this.serverName; }
/** Sets the server port. Defaults to 8080. */
public void setServerPort(int serverPort) { this.serverPort = serverPort; }
/** Returns the server port. Defaults to 8080. */
public int getServerPort() { return this.serverPort; }
/** Always returns null. */
public BufferedReader getReader() throws IOException {
return new BufferedReader( new InputStreamReader( getInputStream() ) );
}
/** Aways returns "127.0.0.1". */
public String getRemoteAddr() { return "127.0.0.1"; }
/** Always returns "localhost". */
public String getRemoteHost() { return "localhost"; }
/** Sets the supplied value for the named request attribute. */
public void setAttribute(String name, Object value) {
this.attributes.put(name, value);
}
/** Sets the body of the request */
public void setRequestBody( String requestBody ) {
if ( requestBody != null ) {
this.requestBody = requestBody.getBytes();
}
}
/** Removes any value for the named request attribute. */
public void removeAttribute(String name) { this.attributes.remove(name); }
/** Adds a Locale to the set of requested locales. */
public void addLocale(Locale locale) { this.locales.add(locale); }
/** Returns the preferred locale. Defaults to the system locale. */
public Locale getLocale() { return getLocales().nextElement(); }
/** Returns an enumeration of requested locales. Defaults to the system locale. */
public Enumeration<Locale> getLocales() {
if (this.locales.size() == 0) {
this.locales.add( Locale.getDefault() );
}
return Collections.enumeration(this.locales);
}
/** Returns true if the protocol is set to https (default), false otherwise. */
public boolean isSecure() {
return this.protocol.equalsIgnoreCase("https");
}
/**
* Returns an instance of MockRequestDispatcher that just records what URLs are forwarded
* to or included. The results can be examined later by calling getForwardUrl() and
* getIncludedUrls().
*/
public MockRequestDispatcher getRequestDispatcher(String url) {
return new MockRequestDispatcher(url);
}
/** Always returns the path passed in without any alteration. */
public String getRealPath(String path) { return path; }
/** Always returns 1088 (and yes, that was picked arbitrarily). */
public int getRemotePort() { return 1088; }
/** Always returns the same value as getServerName(). */
public String getLocalName() { return getServerName(); }
/** Always returns 127.0.0.1). */
public String getLocalAddr() { return "127.0.0.1"; }
/** Always returns the same value as getServerPort(). */
public int getLocalPort() { return getServerPort(); }
/** Used by the request dispatcher to set the forward URL when a forward is invoked. */
void setForwardUrl(String url) { this.forwardUrl = url; }
/** Gets the URL that was forwarded to, if a forward was processed. Null otherwise. */
public String getForwardUrl() { return this.forwardUrl; }
/** Used by the request dispatcher to record that a URL was included. */
void addIncludedUrl(String url) { this.includedUrls.add(url); }
/** Gets the list (potentially empty) or URLs that were included during the request. */
public List<String> getIncludedUrls() { return this.includedUrls; }
public String changeSessionId() {
return null;
}
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
return false;
}
public void login(String username, String password) throws ServletException {
}
public void logout() throws ServletException {
}
public Collection<Part> getParts() throws IOException, ServletException {
return null;
}
public Part getPart(String name) throws IOException, ServletException {
return null;
}
public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
return null;
}
public long getContentLengthLong() {
return 0;
}
public ServletContext getServletContext() {
return null;
}
public AsyncContext startAsync() throws IllegalStateException {
throw new UnsupportedOperationException("use request,response variant");
}
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
if (asyncContext == null) {
asyncContext = new MockAsyncContext(servletRequest, servletResponse);
} else if (asyncContext.isCompleted()) {
throw new IllegalStateException("Async Context already completed");
}
return asyncContext;
}
public boolean isAsyncStarted() {
return asyncContext != null;
}
public boolean isAsyncSupported() {
return true;
}
public MockAsyncContext getAsyncContext() {
return asyncContext;
}
public DispatcherType getDispatcherType() {
return null;
}
}