001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.mock2;
014
015import java.io.*;
016import java.util.*;
017
018import javax.servlet.*;
019import javax.servlet.http.*;
020
021import org.apache.juneau.internal.*;
022import org.apache.juneau.rest.util.*;
023
024/**
025 * An implementation of {@link HttpServletResponse} for mocking purposes.
026 *
027 * <ul class='seealso'>
028 *    <li class='link'>{@doc juneau-rest-mock}
029 * </ul>
030*/
031public class MockServletResponse implements HttpServletResponse {
032
033   private String characterEncoding = "UTF-8";
034   private ByteArrayOutputStream baos = new ByteArrayOutputStream();
035   private int bufferSize = 0;
036   private Locale locale;
037   private int sc;
038   private String msg;
039   private Map<String,String[]> headerMap = new LinkedHashMap<>();
040
041   /**
042    * Creates a new servlet response.
043    *
044    * @return A new response.
045    */
046   public static MockServletResponse create() {
047      return new MockServletResponse();
048   }
049
050   /**
051    * Returns the response message.
052    *
053    * @return The response message.
054    */
055   public String getMessage() {
056      return msg;
057   }
058
059   @Override /* HttpServletResponse */
060   public String getCharacterEncoding() {
061      return characterEncoding ;
062   }
063
064   @Override /* HttpServletResponse */
065   public String getContentType() {
066      return getHeader("Content-Type");
067   }
068
069   @Override /* HttpServletResponse */
070   public ServletOutputStream getOutputStream() throws IOException {
071      return new FinishableServletOutputStream(baos);
072   }
073
074   @Override /* HttpServletResponse */
075   public PrintWriter getWriter() throws IOException {
076      return new PrintWriter(new OutputStreamWriter(getOutputStream(), characterEncoding));
077   }
078
079   @Override /* HttpServletResponse */
080   public void setCharacterEncoding(String charset) {
081      this.characterEncoding = charset;
082      updateContentTypeHeader();
083   }
084
085   private void updateContentTypeHeader() {
086      String contentType = getContentType();
087      String charset = characterEncoding;
088      if (contentType != null && charset != null) {
089         if (contentType.indexOf("charset=") != -1)
090            contentType = contentType.replaceAll("\\;\\s*charset=.*", "");
091         if (! "UTF-8".equalsIgnoreCase(charset))
092            contentType = contentType + ";charset=" + charset;
093         header("Content-Type", contentType);
094      }
095   }
096
097   @Override /* HttpServletResponse */
098   public void setContentLength(int len) {
099      header("Content-Length", String.valueOf(len));
100   }
101
102   @Override /* HttpServletResponse */
103   public void setContentLengthLong(long len) {
104      header("Content-Length", String.valueOf(len));
105   }
106
107   @Override /* HttpServletResponse */
108   public void setContentType(String type) {
109      setHeader("Content-Type", type);
110      updateContentTypeHeader();
111   }
112
113   @Override /* HttpServletResponse */
114   public void setBufferSize(int size) {
115      this.bufferSize = size;
116   }
117
118   @Override /* HttpServletResponse */
119   public int getBufferSize() {
120      return bufferSize;
121   }
122
123   @Override /* HttpServletResponse */
124   public void flushBuffer() throws IOException {
125   }
126
127   @Override /* HttpServletResponse */
128   public void resetBuffer() {
129   }
130
131   @Override /* HttpServletResponse */
132   public boolean isCommitted() {
133      return false;
134   }
135
136   @Override /* HttpServletResponse */
137   public void reset() {
138   }
139
140   @Override /* HttpServletResponse */
141   public void setLocale(Locale loc) {
142      this.locale = loc;
143   }
144
145   @Override /* HttpServletResponse */
146   public Locale getLocale() {
147      return locale;
148   }
149
150   @Override /* HttpServletResponse */
151   public void addCookie(Cookie cookie) {
152   }
153
154   @Override /* HttpServletResponse */
155   public boolean containsHeader(String name) {
156      return getHeader(name) != null;
157   }
158
159   @Override /* HttpServletResponse */
160   public String encodeURL(String url) {
161      return null;
162   }
163
164   @Override /* HttpServletResponse */
165   public String encodeRedirectURL(String url) {
166      return null;
167   }
168
169   @Override /* HttpServletResponse */
170   public String encodeUrl(String url) {
171      return null;
172   }
173
174   @Override /* HttpServletResponse */
175   public String encodeRedirectUrl(String url) {
176      return null;
177   }
178
179   @Override /* HttpServletResponse */
180   public void sendError(int sc, String msg) throws IOException {
181      this.sc = sc;
182      this.msg = msg;
183   }
184
185   @Override /* HttpServletResponse */
186   public void sendError(int sc) throws IOException {
187      this.sc = sc;
188   }
189
190   @Override /* HttpServletResponse */
191   public void sendRedirect(String location) throws IOException {
192      this.sc = 302;
193      headerMap.put("Location", new String[] {location});
194   }
195
196   @Override /* HttpServletResponse */
197   public void setDateHeader(String name, long date) {
198      headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)});
199   }
200
201   @Override /* HttpServletResponse */
202   public void addDateHeader(String name, long date) {
203      headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)});
204   }
205
206   @Override /* HttpServletResponse */
207   public void setHeader(String name, String value) {
208      headerMap.put(name, new String[] {value});
209   }
210
211   @Override /* HttpServletResponse */
212   public void addHeader(String name, String value) {
213      headerMap.put(name, new String[] {value});
214   }
215
216   /**
217    * Fluent setter for {@link #setHeader(String,String)}.
218    *
219    * @param name The header name.
220    * @param value The new header value.
221    * @return This object (for method chaining).
222    */
223   public MockServletResponse header(String name, String value) {
224      setHeader(name, value);
225      return this;
226   }
227
228   @Override /* HttpServletResponse */
229   public void setIntHeader(String name, int value) {
230      headerMap.put(name, new String[] {String.valueOf(value)});
231   }
232
233   @Override /* HttpServletResponse */
234   public void addIntHeader(String name, int value) {
235      headerMap.put(name, new String[] {String.valueOf(value)});
236   }
237
238   @Override /* HttpServletResponse */
239   public void setStatus(int sc) {
240      this.sc = sc;
241   }
242
243   /**
244    * Fluent setter for {@link #setStatus(int)}.
245    *
246    * @param value The new property value.
247    * @return This object (for method chaining).
248    */
249   public MockServletResponse status(int value) {
250      setStatus(value);
251      return this;
252   }
253
254   @Override /* HttpServletResponse */
255   public void setStatus(int sc, String sm) {
256      this.sc = sc;
257      this.msg = sm;
258   }
259
260   @Override /* HttpServletResponse */
261   public int getStatus() {
262      return sc;
263   }
264
265   @Override /* HttpServletResponse */
266   public String getHeader(String name) {
267      String[] s = headerMap.get(name);
268      return s == null || s.length == 0 ? null : s[0];
269   }
270
271   @Override /* HttpServletResponse */
272   public Collection<String> getHeaders(String name) {
273      String[] s = headerMap.get(name);
274      return s == null ? Collections.emptyList() : Arrays.asList(s);
275   }
276
277   @Override /* HttpServletResponse */
278   public Collection<String> getHeaderNames() {
279      return headerMap.keySet();
280   }
281
282   byte[] getBody() {
283      return baos.toByteArray();
284   }
285
286   Map<String,String[]> getHeaders() {
287      return headerMap;
288   }
289}