| // *************************************************************************************************************************** |
| // * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * |
| // * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * |
| // * to you 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 org.apache.juneau.rest.mock2; |
| |
| import static org.apache.juneau.internal.StringUtils.*; |
| |
| import java.io.*; |
| import java.text.*; |
| import java.util.*; |
| import java.util.regex.*; |
| |
| import javax.servlet.*; |
| import javax.servlet.http.*; |
| |
| import org.apache.juneau.internal.*; |
| import org.apache.juneau.rest.util.*; |
| |
| /** |
| * An implementation of {@link HttpServletResponse} for mocking purposes. |
| * |
| * <ul class='seealso'> |
| * <li class='link'>{@doc juneau-rest-mock.MockRest} |
| * </ul> |
| */ |
| public class MockServletResponse implements HttpServletResponse, MockHttpResponse { |
| |
| private String characterEncoding = "UTF-8"; |
| private ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| private long contentLength = 0; |
| private int bufferSize = 0; |
| private Locale locale; |
| private int sc; |
| private String msg; |
| private Map<String,String[]> headerMap = new LinkedHashMap<>(); |
| |
| /** |
| * Creates a new servlet response. |
| * |
| * @return A new response. |
| */ |
| public static MockServletResponse create() { |
| return new MockServletResponse(); |
| } |
| |
| /** |
| * Returns the content length. |
| * |
| * @return The content length. |
| */ |
| public long getContentLength() { |
| return contentLength; |
| } |
| |
| /** |
| * Returns the response message. |
| * |
| * @return The response message. |
| */ |
| @Override /* MockHttpResponse */ |
| public String getMessage() { |
| return msg; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String getCharacterEncoding() { |
| return characterEncoding ; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String getContentType() { |
| return getHeader("Content-Type"); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public ServletOutputStream getOutputStream() throws IOException { |
| return new FinishableServletOutputStream(baos); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public PrintWriter getWriter() throws IOException { |
| return new PrintWriter(new OutputStreamWriter(getOutputStream(), characterEncoding)); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setCharacterEncoding(String charset) { |
| this.characterEncoding = charset; |
| } |
| |
| /** |
| * Fluent setter for {@link #setCharacterEncoding(String)}. |
| * |
| * @param value The new property value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse characterEncoding(String value) { |
| setCharacterEncoding(value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setContentLength(int len) { |
| this.contentLength = len; |
| } |
| |
| /** |
| * Fluent setter for {@link #setContentLength(int)}. |
| * |
| * @param value The new property value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse contentLength(int value) { |
| setContentLength(value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setContentLengthLong(long len) { |
| this.contentLength = len; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setContentType(String type) { |
| setHeader("Content-Type", type); |
| } |
| |
| /** |
| * Fluent setter for {@link #setContentType(String)}. |
| * |
| * @param value The new property value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse contentType(String value) { |
| setContentType(value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setBufferSize(int size) { |
| this.bufferSize = size; |
| } |
| |
| /** |
| * Fluent setter for {@link #bufferSize(int)}. |
| * |
| * @param value The new property value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse bufferSize(int value) { |
| setBufferSize(value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public int getBufferSize() { |
| return bufferSize; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void flushBuffer() throws IOException { |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void resetBuffer() { |
| } |
| |
| @Override /* HttpServletResponse */ |
| public boolean isCommitted() { |
| return false; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void reset() { |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setLocale(Locale loc) { |
| this.locale = loc; |
| } |
| |
| /** |
| * Fluent setter for {@link #setLocale(Locale)}. |
| * |
| * @param value The new property value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse locale(Locale value) { |
| setLocale(value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public Locale getLocale() { |
| return locale; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void addCookie(Cookie cookie) { |
| } |
| |
| @Override /* HttpServletResponse */ |
| public boolean containsHeader(String name) { |
| return getHeader(name) != null; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String encodeURL(String url) { |
| return null; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String encodeRedirectURL(String url) { |
| return null; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String encodeUrl(String url) { |
| return null; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String encodeRedirectUrl(String url) { |
| return null; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void sendError(int sc, String msg) throws IOException { |
| this.sc = sc; |
| this.msg = msg; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void sendError(int sc) throws IOException { |
| this.sc = sc; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void sendRedirect(String location) throws IOException { |
| this.sc = 302; |
| headerMap.put("Location", new String[] {location}); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setDateHeader(String name, long date) { |
| headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)}); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void addDateHeader(String name, long date) { |
| headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)}); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setHeader(String name, String value) { |
| headerMap.put(name, new String[] {value}); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void addHeader(String name, String value) { |
| headerMap.put(name, new String[] {value}); |
| } |
| |
| /** |
| * Fluent setter for {@link #setHeader(String,String)}. |
| * |
| * @param name The header name. |
| * @param value The new header value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse header(String name, String value) { |
| setHeader(name, value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setIntHeader(String name, int value) { |
| headerMap.put(name, new String[] {String.valueOf(value)}); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void addIntHeader(String name, int value) { |
| headerMap.put(name, new String[] {String.valueOf(value)}); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setStatus(int sc) { |
| this.sc = sc; |
| } |
| |
| /** |
| * Fluent setter for {@link #setStatus(int)}. |
| * |
| * @param value The new property value. |
| * @return This object (for method chaining). |
| */ |
| public MockServletResponse status(int value) { |
| setStatus(value); |
| return this; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public void setStatus(int sc, String sm) { |
| this.sc = sc; |
| this.msg = sm; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public int getStatus() { |
| return sc; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public String getHeader(String name) { |
| String[] s = headerMap.get(name); |
| return s == null || s.length == 0 ? null : s[0]; |
| } |
| |
| @Override /* HttpServletResponse */ |
| public Collection<String> getHeaders(String name) { |
| String[] s = headerMap.get(name); |
| return s == null ? Collections.emptyList() : Arrays.asList(s); |
| } |
| |
| @Override /* HttpServletResponse */ |
| public Collection<String> getHeaderNames() { |
| return headerMap.keySet(); |
| } |
| |
| /** |
| * Returns the body of the request as a string. |
| * |
| * @return The body of the request as a string. |
| */ |
| public String getBodyAsString() { |
| try { |
| return baos.toString("UTF-8"); |
| } catch (UnsupportedEncodingException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response status does not match the expected status. |
| * |
| * @param status The expected status. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if status does not match. |
| */ |
| public MockServletResponse assertStatus(int status) throws AssertionError { |
| if (getStatus() != status) |
| throw new MockAssertionError("Response did not have the expected status.\n\tExpected=[{0}]\n\tActual=[{1}]", status, getStatus()); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response body does not contain the expected text. |
| * |
| * @param text The expected text of the body. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the body does not contain the expected text. |
| */ |
| public MockServletResponse assertBody(String text) throws AssertionError { |
| if (! StringUtils.isEquals(text, getBodyAsString())) |
| throw new MockAssertionError("Response did not have the expected text.\n\tExpected=[{0}]\n\tActual=[{1}]", text, getBodyAsString()); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response body does not contain all of the expected substrings. |
| * |
| * @param substrings The expected substrings. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the body does not contain one or more of the expected substrings. |
| */ |
| public MockServletResponse assertBodyContains(String...substrings) throws AssertionError { |
| String text = getBodyAsString(); |
| for (String substring : substrings) |
| if (! contains(text, substring)) |
| throw new MockAssertionError("Response did not have the expected substring.\n\tExpected=[{0}]\n\tBody=[{1}]", substring, text); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response body does not match the specified pattern. |
| * |
| * <p> |
| * A pattern is a simple string containing <js>"*"</js> to represent zero or more arbitrary characters. |
| * |
| * @param pattern The pattern to match against. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the body does not match the specified pattern. |
| */ |
| public MockServletResponse assertBodyMatches(String pattern) throws AssertionError { |
| String text = getBodyAsString(); |
| if (! getMatchPattern(pattern).matcher(text).matches()) |
| throw new MockAssertionError("Response did not match expected pattern.\n\tPattern=[{0}]\n\tBody=[{1}]", pattern, text); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response body does not match the specified regular expression. |
| * |
| * <p> |
| * A pattern is a simple string containing <js>"*"</js> to represent zero or more arbitrary characters. |
| * |
| * @param regExp The regular expression to match against. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the body does not match the specified regular expression. |
| */ |
| public MockServletResponse assertBodyMatchesRE(String regExp) throws AssertionError { |
| String text = getBodyAsString(); |
| if (! Pattern.compile(regExp).matcher(text).matches()) |
| throw new MockAssertionError("Response did not match expected regular expression.\n\tRegExp=[{0}]\n\tBody=[{1}]", regExp, text); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response does not contain the expected character encoding. |
| * |
| * @param value The expected character encoding. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the response does not contain the expected character encoding. |
| */ |
| public MockServletResponse assertCharset(String value) { |
| if (! StringUtils.isEquals(value, getCharacterEncoding())) |
| throw new MockAssertionError("Response did not have the expected character encoding.\n\tExpected=[{0}]\n\tActual=[{1}]", value, getBodyAsString()); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response does not contain the expected header value. |
| * |
| * @param name The header name. |
| * @param value The expected header value. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the response does not contain the expected header value. |
| */ |
| public MockServletResponse assertHeader(String name, String value) { |
| if (! StringUtils.isEquals(value, getHeader(name))) |
| throw new MockAssertionError("Response did not have the expected value for header {0}.\n\tExpected=[{1}]\n\tActual=[{2}]", name, value, getHeader(name)); |
| return this; |
| } |
| |
| /** |
| * Throws an {@link AssertionError} if the response header does not contain all of the expected substrings. |
| * |
| * @param name The header name. |
| * @param substrings The expected substrings. |
| * @return This object (for method chaining). |
| * @throws AssertionError Thrown if the header does not contain one or more of the expected substrings. |
| */ |
| public MockServletResponse assertHeaderContains(String name, String...substrings) { |
| String text = getHeader(name); |
| for (String substring : substrings) |
| if (! contains(text, substring)) |
| throw new MockAssertionError("Response did not have the expected substring in header {0}.\n\tExpected=[{1}]\n\tHeader=[{2}]", name, substring, text); |
| return this; |
| } |
| |
| /** |
| * Returns the body of the request. |
| * |
| * @return The body of the request. |
| */ |
| @Override /* MockHttpResponse */ |
| public byte[] getBody() { |
| return baos.toByteArray(); |
| } |
| |
| @Override /* MockHttpResponse */ |
| public Map<String,String[]> getHeaders() { |
| return headerMap; |
| } |
| |
| private static class MockAssertionError extends AssertionError { |
| private static final long serialVersionUID = 1L; |
| |
| MockAssertionError(String msg, Object...args) { |
| super(MessageFormat.format(msg, args)); |
| System.err.println(getMessage()); // NOT DEBUG |
| } |
| } |
| } |