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.mock;
014
015import static org.apache.juneau.internal.CollectionUtils.*;
016
017import java.io.*;
018import java.util.*;
019
020import jakarta.servlet.*;
021import jakarta.servlet.http.*;
022
023import org.apache.juneau.internal.*;
024import org.apache.juneau.rest.util.*;
025
026/**
027 * An implementation of {@link HttpServletResponse} for mocking purposes.
028 *
029 * <h5 class='section'>See Also:</h5><ul>
030 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-mock">juneau-rest-mock</a>
031 * </ul>
032*/
033public class MockServletResponse implements HttpServletResponse {
034
035   private String characterEncoding = "UTF-8";
036   private ByteArrayOutputStream baos = new ByteArrayOutputStream();
037   private int bufferSize = 0;
038   private Locale locale;
039   private int sc;
040   private String msg;
041   private Map<String,String[]> headerMap = map();
042
043   /**
044    * Creates a new servlet response.
045    *
046    * @return A new response.
047    */
048   public static MockServletResponse create() {
049      return new MockServletResponse();
050   }
051
052   /**
053    * Returns the response message.
054    *
055    * @return The response message.
056    */
057   public String getMessage() {
058      return msg;
059   }
060
061   @Override /* HttpServletResponse */
062   public String getCharacterEncoding() {
063      return characterEncoding ;
064   }
065
066   @Override /* HttpServletResponse */
067   public String getContentType() {
068      return getHeader("Content-Type");
069   }
070
071   @Override /* HttpServletResponse */
072   public ServletOutputStream getOutputStream() throws IOException {
073      return new FinishableServletOutputStream(baos);
074   }
075
076   @Override /* HttpServletResponse */
077   public PrintWriter getWriter() throws IOException {
078      return new PrintWriter(new OutputStreamWriter(getOutputStream(), characterEncoding));
079   }
080
081   @Override /* HttpServletResponse */
082   public void setCharacterEncoding(String charset) {
083      this.characterEncoding = charset;
084      updateContentTypeHeader();
085   }
086
087   private void updateContentTypeHeader() {
088      String contentType = getContentType();
089      String charset = characterEncoding;
090      if (contentType != null && charset != null) {
091         if (contentType.indexOf("charset=") != -1)
092            contentType = contentType.replaceAll("\\;\\s*charset=.*", "");
093         if (! "UTF-8".equalsIgnoreCase(charset))
094            contentType = contentType + ";charset=" + charset;
095         header("Content-Type", contentType);
096      }
097   }
098
099   @Override /* HttpServletResponse */
100   public void setContentLength(int len) {
101      header("Content-Length", String.valueOf(len));
102   }
103
104   @Override /* HttpServletResponse */
105   public void setContentLengthLong(long len) {
106      header("Content-Length", String.valueOf(len));
107   }
108
109   @Override /* HttpServletResponse */
110   public void setContentType(String type) {
111      setHeader("Content-Type", type);
112      updateContentTypeHeader();
113   }
114
115   @Override /* HttpServletResponse */
116   public void setBufferSize(int size) {
117      this.bufferSize = size;
118   }
119
120   @Override /* HttpServletResponse */
121   public int getBufferSize() {
122      return bufferSize;
123   }
124
125   @Override /* HttpServletResponse */
126   public void flushBuffer() throws IOException {
127   }
128
129   @Override /* HttpServletResponse */
130   public void resetBuffer() {
131   }
132
133   @Override /* HttpServletResponse */
134   public boolean isCommitted() {
135      return false;
136   }
137
138   @Override /* HttpServletResponse */
139   public void reset() {
140   }
141
142   @Override /* HttpServletResponse */
143   public void setLocale(Locale loc) {
144      this.locale = loc;
145   }
146
147   @Override /* HttpServletResponse */
148   public Locale getLocale() {
149      return locale;
150   }
151
152   @Override /* HttpServletResponse */
153   public void addCookie(Cookie cookie) {
154   }
155
156   @Override /* HttpServletResponse */
157   public boolean containsHeader(String name) {
158      return getHeader(name) != null;
159   }
160
161   @Override /* HttpServletResponse */
162   public String encodeURL(String url) {
163      return null;
164   }
165
166   @Override /* HttpServletResponse */
167   public String encodeRedirectURL(String url) {
168      return null;
169   }
170
171   @Override /* HttpServletResponse */
172   public void sendError(int sc, String msg) throws IOException {
173      this.sc = sc;
174      this.msg = msg;
175   }
176
177   @Override /* HttpServletResponse */
178   public void sendError(int sc) throws IOException {
179      this.sc = sc;
180   }
181
182   @Override /* HttpServletResponse */
183   public void sendRedirect(String location) throws IOException {
184      this.sc = 302;
185      headerMap.put("Location", new String[] {location});
186   }
187
188   @Override /* HttpServletResponse */
189   public void setDateHeader(String name, long date) {
190      headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)});
191   }
192
193   @Override /* HttpServletResponse */
194   public void addDateHeader(String name, long date) {
195      headerMap.put(name, new String[] {DateUtils.formatDate(new Date(date), DateUtils.PATTERN_RFC1123)});
196   }
197
198   @Override /* HttpServletResponse */
199   public void setHeader(String name, String value) {
200      headerMap.put(name, new String[] {value});
201   }
202
203   @Override /* HttpServletResponse */
204   public void addHeader(String name, String value) {
205      headerMap.put(name, new String[] {value});
206   }
207
208   /**
209    * Fluent setter for {@link #setHeader(String,String)}.
210    *
211    * @param name The header name.
212    * @param value The new header value.
213    * @return This object.
214    */
215   public MockServletResponse header(String name, String value) {
216      setHeader(name, value);
217      return this;
218   }
219
220   @Override /* HttpServletResponse */
221   public void setIntHeader(String name, int value) {
222      headerMap.put(name, new String[] {String.valueOf(value)});
223   }
224
225   @Override /* HttpServletResponse */
226   public void addIntHeader(String name, int value) {
227      headerMap.put(name, new String[] {String.valueOf(value)});
228   }
229
230   @Override /* HttpServletResponse */
231   public void setStatus(int sc) {
232      this.sc = sc;
233   }
234
235   /**
236    * Fluent setter for {@link #setStatus(int)}.
237    *
238    * @param value The new property value.
239    * @return This object.
240    */
241   public MockServletResponse status(int value) {
242      setStatus(value);
243      return this;
244   }
245
246   @Override /* HttpServletResponse */
247   public int getStatus() {
248      return sc;
249   }
250
251   @Override /* HttpServletResponse */
252   public String getHeader(String name) {
253      String[] s = headerMap.get(name);
254      return s == null || s.length == 0 ? null : s[0];
255   }
256
257   @Override /* HttpServletResponse */
258   public Collection<String> getHeaders(String name) {
259      String[] s = headerMap.get(name);
260      return s == null ? emptyList() : ulist(s);
261   }
262
263   @Override /* HttpServletResponse */
264   public Collection<String> getHeaderNames() {
265      return headerMap.keySet();
266   }
267
268   byte[] getContent() throws IOException {
269      baos.flush();
270      return baos.toByteArray();
271   }
272
273   Map<String,String[]> getHeaders() {
274      return headerMap;
275   }
276}