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