View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2015 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.io.http.impl.servletclient;
21  
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.PrintWriter;
25  import java.text.SimpleDateFormat;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.Date;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.TimeZone;
32  
33  import javax.servlet.ServletOutputStream;
34  import javax.servlet.http.Cookie;
35  import javax.servlet.http.HttpServletResponse;
36  
37  import com.google.common.base.Charsets;
38  import com.google.common.collect.HashMultimap;
39  import com.google.common.collect.Lists;
40  import com.google.common.collect.Multimap;
41  
42  import io.wcm.caravan.io.http.response.CaravanHttpResponse;
43  import io.wcm.caravan.io.http.response.CaravanHttpResponseBuilder;
44  
45  /**
46   * Mapper from {@link CaravanHttpResponse} to {@link HttpServletResponse}.
47   */
48  public class HttpServletResponseMapper implements HttpServletResponse {
49  
50    private String characterEncoding = Charsets.UTF_8.toString();
51    private String contentType;
52    private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
53    private int bufferSize = 4096;
54    private Locale locale;
55    private final List<Cookie> cookies = Lists.newArrayList();
56    private final Multimap<String, String> headers = HashMultimap.create();
57    private int status = HttpServletResponse.SC_OK;
58    private String reason = "OK";
59  
60    @Override
61    public String getCharacterEncoding() {
62      return characterEncoding;
63    }
64  
65    @Override
66    public String getContentType() {
67      return contentType;
68    }
69  
70    @Override
71    public ServletOutputStream getOutputStream() throws IOException {
72  
73      return new ServletOutputStream() {
74  
75        @Override
76        public void write(int b) throws IOException {
77          outputStream.write(b);
78        }
79      };
80    }
81  
82    @Override
83    public PrintWriter getWriter() throws IOException {
84      return new PrintWriter(outputStream);
85    }
86  
87    @Override
88    public void setCharacterEncoding(String charset) {
89      characterEncoding = charset;
90    }
91  
92    @Override
93    public void setContentLength(int len) {
94      setIntHeader("Content-Length", len);
95  
96      // if the content length is known in advance then resize the ByteArrayOutputSteram accordingly
97      // (but only if nothing has yet been written to the output stream)
98      if (len > 0 && outputStream.size() == 0) {
99        outputStream = new ByteArrayOutputStream(len);
100     }
101   }
102 
103   @Override
104   public void setContentType(String type) {
105     contentType = type;
106   }
107 
108   @Override
109   public void setBufferSize(int size) {
110     bufferSize = size;
111   }
112 
113   @Override
114   public int getBufferSize() {
115     return bufferSize;
116   }
117 
118   @Override
119   public void flushBuffer() throws IOException {
120     outputStream.flush();
121   }
122 
123   @Override
124   public void resetBuffer() {
125     outputStream.reset();
126   }
127 
128   @Override
129   public boolean isCommitted() {
130     return false;
131   }
132 
133   @Override
134   public void reset() {
135     resetBuffer();
136     this.characterEncoding = null;
137     this.contentType = null;
138     this.locale = null;
139     this.cookies.clear();
140     this.headers.clear();
141     this.status = HttpServletResponse.SC_OK;
142     this.reason = null;
143   }
144 
145   @Override
146   public void setLocale(Locale loc) {
147     locale = loc;
148   }
149 
150   @Override
151   public Locale getLocale() {
152     return locale;
153   }
154 
155   @Override
156   public void addCookie(Cookie cookie) {
157     cookies.add(cookie);
158   }
159 
160   @Override
161   public boolean containsHeader(String name) {
162     return headers.containsKey(name);
163   }
164 
165   @Override
166   public String encodeURL(String url) {
167     return url;
168   }
169 
170   @Override
171   public String encodeRedirectURL(String url) {
172     return encodeURL(url);
173   }
174 
175   @Override
176   public String encodeUrl(String url) {
177     return encodeURL(url);
178   }
179 
180   @Override
181   public String encodeRedirectUrl(String url) {
182     return encodeRedirectURL(url);
183   }
184 
185   @Override
186   public void sendError(int sc, String msg) throws IOException {
187     status = sc;
188     reason = msg;
189   }
190 
191   @Override
192   public void sendError(int sc) throws IOException {
193     status = sc;
194   }
195 
196   @Override
197   public void sendRedirect(String location) throws IOException {
198     setHeader("Location", location);
199     setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
200   }
201 
202   @Override
203   public void setDateHeader(String name, long date) {
204     String value = formatDate(date);
205     setHeader(name, value);
206   }
207 
208   private String formatDate(long date) {
209     SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
210     dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
211     return dateFormat.format(new Date(date));
212   }
213 
214   @Override
215   public void addDateHeader(String name, long date) {
216     String value = formatDate(date);
217     addHeader(name, value);
218   }
219 
220   @Override
221   public void setHeader(String name, String value) {
222     headers.replaceValues(name, Collections.singletonList(value));
223   }
224 
225   @Override
226   public void addHeader(String name, String value) {
227     headers.put(name, value);
228   }
229 
230   @Override
231   public void setIntHeader(String name, int value) {
232     setHeader(name, String.valueOf(value));
233   }
234 
235   @Override
236   public void addIntHeader(String name, int value) {
237     addHeader(name, String.valueOf(value));
238   }
239 
240   @Override
241   public void setStatus(int sc) {
242     status = sc;
243   }
244 
245   @Override
246   public void setStatus(int sc, String sm) {
247     setStatus(sc);
248     reason = sm;
249   }
250 
251   @Override
252   public int getStatus() {
253     return status;
254   }
255 
256   @Override
257   public String getHeader(String name) {
258     Collection<String> values = headers.get(name);
259     return values.isEmpty() ? null : values.iterator().next();
260   }
261 
262   @Override
263   public Collection<String> getHeaders(String name) {
264     return headers.get(name);
265   }
266 
267   @Override
268   public Collection<String> getHeaderNames() {
269     return headers.keySet();
270   }
271 
272   public CaravanHttpResponse getResponse() {
273     return new CaravanHttpResponseBuilder()
274     .body(outputStream.toByteArray())
275     .headers(headers)
276     .reason(reason)
277     .status(status)
278     .build();
279   }
280 
281 }