%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.jetspeed.resource.BufferedHttpServletResponse$1 |
|
|
1 | /* |
|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more |
|
3 | * contributor license agreements. See the NOTICE file distributed with |
|
4 | * this work for additional information regarding copyright ownership. |
|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
6 | * (the "License"); you may not use this file except in compliance with |
|
7 | * the License. You may obtain a copy of the License at |
|
8 | * |
|
9 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 | * |
|
11 | * Unless required by applicable law or agreed to in writing, software |
|
12 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 | * See the License for the specific language governing permissions and |
|
15 | * limitations under the License. |
|
16 | */ |
|
17 | package org.apache.jetspeed.resource; |
|
18 | ||
19 | import java.io.ByteArrayOutputStream; |
|
20 | import java.io.CharArrayWriter; |
|
21 | import java.io.IOException; |
|
22 | import java.io.PrintWriter; |
|
23 | import java.util.ArrayList; |
|
24 | import java.util.HashMap; |
|
25 | import java.util.Iterator; |
|
26 | import java.util.Locale; |
|
27 | import java.util.Map.Entry; |
|
28 | ||
29 | import javax.servlet.ServletOutputStream; |
|
30 | import javax.servlet.http.Cookie; |
|
31 | import javax.servlet.http.HttpServletResponse; |
|
32 | import javax.servlet.http.HttpServletResponseWrapper; |
|
33 | ||
34 | /** |
|
35 | * <p> |
|
36 | * BufferedHttpServletResponse fully captures all HttpServletResponse interactions to be flushed out later. |
|
37 | * This wrapper is specifically written to allow included servlets to set headers, cookies, encoding etc. which isn't allowed by |
|
38 | * the servlet specification on included responses. |
|
39 | * </p> |
|
40 | * <p> |
|
41 | * Call flush(HttpServletResponse) after the include has returned to flush out the buffered data, headers and state. |
|
42 | * </p> |
|
43 | * <p> |
|
44 | * Note: the only method not fully supported by this buffered version is getCharacterEncoding(). Setting characterEncoding through |
|
45 | * setContentType or setLocale on this class won't be reflected in the return value from getCharacterEncoding(), and calling getWriter() |
|
46 | * won't set it either although calling setLocale, setContentType or setCharacterEncoding (servlet api 2.4+) after that will be ignored. |
|
47 | * But, when this object is flused to a (real) response, the contentType, locale and/or characterEncoding recorded will be set on the |
|
48 | * target response then. |
|
49 | * </p> |
|
50 | * |
|
51 | * @author <a href="mailto:ate@douma.nu">Ate Douma</a> |
|
52 | * @version $Id: BufferedHttpServletResponse.java 544024 2007-06-04 00:59:09Z ate $ |
|
53 | */ |
|
54 | public class BufferedHttpServletResponse extends HttpServletResponseWrapper |
|
55 | { |
|
56 | private static class CharArrayWriterBuffer extends CharArrayWriter |
|
57 | { |
|
58 | public char[] getBuffer() |
|
59 | { |
|
60 | return buf; |
|
61 | } |
|
62 | ||
63 | public int getCount() |
|
64 | { |
|
65 | return count; |
|
66 | } |
|
67 | } |
|
68 | ||
69 | private ByteArrayOutputStream byteOutputBuffer; |
|
70 | private CharArrayWriterBuffer charOutputBuffer; |
|
71 | private ServletOutputStream outputStream; |
|
72 | private PrintWriter printWriter; |
|
73 | private HashMap headers; |
|
74 | private ArrayList cookies; |
|
75 | private int errorCode; |
|
76 | private int statusCode; |
|
77 | private String errorMessage; |
|
78 | private String redirectLocation; |
|
79 | private boolean committed; |
|
80 | private boolean hasStatus; |
|
81 | private boolean hasError; |
|
82 | private Locale locale; |
|
83 | private boolean closed; |
|
84 | private String characterEncoding; |
|
85 | private int contentLength = -1; |
|
86 | private String contentType; |
|
87 | private boolean flushed; |
|
88 | ||
89 | public BufferedHttpServletResponse(HttpServletResponse response) |
|
90 | { |
|
91 | super(response); |
|
92 | } |
|
93 | ||
94 | public void flush(HttpServletResponse response) throws IOException |
|
95 | { |
|
96 | if (flushed) |
|
97 | { |
|
98 | throw new IllegalStateException("Already flushed"); |
|
99 | } |
|
100 | flushed = true; |
|
101 | ||
102 | if (locale != null) |
|
103 | { |
|
104 | response.setLocale(locale); |
|
105 | } |
|
106 | if (contentType != null) |
|
107 | { |
|
108 | response.setContentType(contentType); |
|
109 | } |
|
110 | if (characterEncoding != null) |
|
111 | { |
|
112 | // setCharacterEncoding only available on Servlet Spec 2.4+ |
|
113 | try |
|
114 | { |
|
115 | response.getClass().getMethod("setCharacterEncoding", new Class[]{String.class}).invoke(response, class="keyword">new Object[]{characterEncoding}); |
|
116 | } |
|
117 | catch (NoSuchMethodException nsme) |
|
118 | { |
|
119 | // servlet spec 2.3 |
|
120 | } |
|
121 | catch (Exception e) |
|
122 | { |
|
123 | throw new RuntimeException(e); |
|
124 | } |
|
125 | } |
|
126 | if (cookies != null) |
|
127 | { |
|
128 | for (int i=0,size=cookies.size(); i<size; i++) |
|
129 | { |
|
130 | response.addCookie((Cookie)cookies.get(i)); |
|
131 | } |
|
132 | cookies = null; |
|
133 | } |
|
134 | if (headers != null) |
|
135 | { |
|
136 | Iterator iter = headers.entrySet().iterator(); |
|
137 | while (iter.hasNext()) |
|
138 | { |
|
139 | Entry e = (Entry)iter.next(); |
|
140 | String name = (String)e.getKey(); |
|
141 | ArrayList values = (ArrayList)e.getValue(); |
|
142 | for (int i=0, size=values.size(); i < size; i++ ) |
|
143 | { |
|
144 | Object value = values.get(i); |
|
145 | if (value instanceof Integer) |
|
146 | { |
|
147 | response.addIntHeader(name, ((Integer)value).intValue()); |
|
148 | } |
|
149 | else if (value instanceof Long) |
|
150 | { |
|
151 | response.addDateHeader(name, ((Long)value).longValue()); |
|
152 | } |
|
153 | else |
|
154 | { |
|
155 | response.addHeader(name, (String)value); |
|
156 | } |
|
157 | } |
|
158 | } |
|
159 | headers = null; |
|
160 | } |
|
161 | if (contentLength > -1) |
|
162 | { |
|
163 | response.setContentLength(contentLength); |
|
164 | } |
|
165 | if (hasStatus) |
|
166 | { |
|
167 | response.setStatus(statusCode); |
|
168 | } |
|
169 | if (hasError) |
|
170 | { |
|
171 | response.sendError(errorCode, errorMessage); |
|
172 | } |
|
173 | else if (redirectLocation != null) |
|
174 | { |
|
175 | response.sendRedirect(redirectLocation); |
|
176 | } |
|
177 | else |
|
178 | { |
|
179 | if (outputStream != null) |
|
180 | { |
|
181 | if (!closed) |
|
182 | { |
|
183 | outputStream.flush(); |
|
184 | } |
|
185 | ServletOutputStream realOutputStream = response.getOutputStream(); |
|
186 | int len = byteOutputBuffer.size(); |
|
187 | if (contentLength > -1 && contentLength < len) |
|
188 | { |
|
189 | len = contentLength; |
|
190 | } |
|
191 | if (len > 0) |
|
192 | { |
|
193 | realOutputStream.write(byteOutputBuffer.toByteArray(), 0, len); |
|
194 | } |
|
195 | outputStream.close(); |
|
196 | outputStream = null; |
|
197 | byteOutputBuffer = null; |
|
198 | } |
|
199 | else if (printWriter != null) |
|
200 | { |
|
201 | if (!closed) |
|
202 | { |
|
203 | printWriter.flush(); |
|
204 | if ( charOutputBuffer.getCount() > 0) |
|
205 | { |
|
206 | response.getWriter().write(charOutputBuffer.getBuffer(), 0, charOutputBuffer.getCount()); |
|
207 | } |
|
208 | printWriter.close(); |
|
209 | ||
210 | printWriter = null; |
|
211 | charOutputBuffer = null; |
|
212 | } |
|
213 | } |
|
214 | ||
215 | } |
|
216 | } |
|
217 | ||
218 | private ArrayList getHeaderList(String name, boolean create) |
|
219 | { |
|
220 | if ( headers == null ) |
|
221 | { |
|
222 | headers = new HashMap(); |
|
223 | } |
|
224 | ArrayList headerList = (ArrayList)headers.get(name); |
|
225 | if ( headerList == null && create ) |
|
226 | { |
|
227 | headerList = new ArrayList(); |
|
228 | headers.put(name,headerList); |
|
229 | } |
|
230 | return headerList; |
|
231 | } |
|
232 | ||
233 | private void failIfCommitted() |
|
234 | { |
|
235 | if (committed) |
|
236 | { |
|
237 | throw new IllegalStateException("Response is already committed"); |
|
238 | } |
|
239 | } |
|
240 | ||
241 | /* (non-Javadoc) |
|
242 | * @see javax.servlet.http.HttpServletResponseWrapper#addCookie(javax.servlet.http.Cookie) |
|
243 | */ |
|
244 | public void addCookie(Cookie cookie) |
|
245 | { |
|
246 | if ( !committed ) |
|
247 | { |
|
248 | if ( cookies == null ) |
|
249 | { |
|
250 | cookies = new ArrayList(); |
|
251 | } |
|
252 | cookies.add(cookie); |
|
253 | } |
|
254 | } |
|
255 | ||
256 | /* (non-Javadoc) |
|
257 | * @see javax.servlet.http.HttpServletResponseWrapper#addDateHeader(java.lang.String, long) |
|
258 | */ |
|
259 | public void addDateHeader(String name, long date) |
|
260 | { |
|
261 | if (!committed) |
|
262 | { |
|
263 | ArrayList headerList = getHeaderList(name, true); |
|
264 | headerList.add(new Long(date)); |
|
265 | } |
|
266 | } |
|
267 | ||
268 | /* (non-Javadoc) |
|
269 | * @see javax.servlet.http.HttpServletResponseWrapper#addHeader(java.lang.String, java.lang.String) |
|
270 | */ |
|
271 | public void addHeader(String name, String value) |
|
272 | { |
|
273 | if (!committed) |
|
274 | { |
|
275 | ArrayList headerList = getHeaderList(name, true); |
|
276 | headerList.add(value); |
|
277 | } |
|
278 | } |
|
279 | ||
280 | /* (non-Javadoc) |
|
281 | * @see javax.servlet.http.HttpServletResponseWrapper#addIntHeader(java.lang.String, int) |
|
282 | */ |
|
283 | public void addIntHeader(String name, int value) |
|
284 | { |
|
285 | if (!committed) |
|
286 | { |
|
287 | ArrayList headerList = getHeaderList(name, true); |
|
288 | headerList.add(new Integer(value)); |
|
289 | } |
|
290 | } |
|
291 | ||
292 | /* (non-Javadoc) |
|
293 | * @see javax.servlet.http.HttpServletResponseWrapper#containsHeader(java.lang.String) |
|
294 | */ |
|
295 | public boolean containsHeader(String name) |
|
296 | { |
|
297 | return getHeaderList(name, false) != null; |
|
298 | } |
|
299 | ||
300 | /* (non-Javadoc) |
|
301 | * @see javax.servlet.http.HttpServletResponseWrapper#sendError(int, java.lang.String) |
|
302 | */ |
|
303 | public void sendError(int errorCode, String errorMessage) throws IOException |
|
304 | { |
|
305 | failIfCommitted(); |
|
306 | committed = true; |
|
307 | closed = true; |
|
308 | hasError = true; |
|
309 | this.errorCode = errorCode; |
|
310 | this.errorMessage = errorMessage; |
|
311 | } |
|
312 | ||
313 | /* (non-Javadoc) |
|
314 | * @see javax.servlet.http.HttpServletResponseWrapper#sendError(int) |
|
315 | */ |
|
316 | public void sendError(int errorCode) throws IOException |
|
317 | { |
|
318 | sendError(errorCode, null); |
|
319 | } |
|
320 | ||
321 | /* (non-Javadoc) |
|
322 | * @see javax.servlet.http.HttpServletResponseWrapper#sendRedirect(java.lang.String) |
|
323 | */ |
|
324 | public void sendRedirect(String redirectLocation) throws IOException |
|
325 | { |
|
326 | failIfCommitted(); |
|
327 | closed = true; |
|
328 | committed = true; |
|
329 | this.redirectLocation = redirectLocation; |
|
330 | } |
|
331 | ||
332 | /* (non-Javadoc) |
|
333 | * @see javax.servlet.http.HttpServletResponseWrapper#setDateHeader(java.lang.String, long) |
|
334 | */ |
|
335 | public void setDateHeader(String name, long date) |
|
336 | { |
|
337 | if (!committed) |
|
338 | { |
|
339 | ArrayList headerList = getHeaderList(name, true); |
|
340 | headerList.clear(); |
|
341 | headerList.add(new Long(date)); |
|
342 | } |
|
343 | } |
|
344 | ||
345 | /* (non-Javadoc) |
|
346 | * @see javax.servlet.http.HttpServletResponseWrapper#setHeader(java.lang.String, java.lang.String) |
|
347 | */ |
|
348 | public void setHeader(String name, String value) |
|
349 | { |
|
350 | if (!committed) |
|
351 | { |
|
352 | ArrayList headerList = getHeaderList(name, true); |
|
353 | headerList.clear(); |
|
354 | headerList.add(value); |
|
355 | } |
|
356 | } |
|
357 | ||
358 | /* (non-Javadoc) |
|
359 | * @see javax.servlet.http.HttpServletResponseWrapper#setIntHeader(java.lang.String, int) |
|
360 | */ |
|
361 | public void setIntHeader(String name, int value) |
|
362 | { |
|
363 | if (!committed) |
|
364 | { |
|
365 | ArrayList headerList = getHeaderList(name, true); |
|
366 | headerList.clear(); |
|
367 | headerList.add(new Integer(value)); |
|
368 | } |
|
369 | } |
|
370 | ||
371 | /* (non-Javadoc) |
|
372 | * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int, java.lang.String) |
|
373 | */ |
|
374 | public void setStatus(int statusCode, String message) |
|
375 | { |
|
376 | throw new UnsupportedOperationException("This method is deprecated and no longer available"); |
|
377 | } |
|
378 | ||
379 | /* (non-Javadoc) |
|
380 | * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int) |
|
381 | */ |
|
382 | public void setStatus(int statusCode) |
|
383 | { |
|
384 | if (!committed) |
|
385 | { |
|
386 | this.statusCode = statusCode; |
|
387 | this.hasStatus = true; |
|
388 | resetBuffer(); |
|
389 | } |
|
390 | } |
|
391 | ||
392 | /* (non-Javadoc) |
|
393 | * @see javax.servlet.ServletResponseWrapper#flushBuffer() |
|
394 | */ |
|
395 | public void flushBuffer() throws IOException |
|
396 | { |
|
397 | if (!closed) |
|
398 | { |
|
399 | committed = true; |
|
400 | } |
|
401 | } |
|
402 | ||
403 | /* (non-Javadoc) |
|
404 | * @see javax.servlet.ServletResponseWrapper#getBufferSize() |
|
405 | */ |
|
406 | public int getBufferSize() |
|
407 | { |
|
408 | return Integer.MAX_VALUE; |
|
409 | } |
|
410 | ||
411 | /* (non-Javadoc) |
|
412 | * @see javax.servlet.ServletResponseWrapper#getCharacterEncoding() |
|
413 | */ |
|
414 | public String getCharacterEncoding() |
|
415 | { |
|
416 | return characterEncoding != null ? characterEncoding : "ISO-8859-1"; |
|
417 | } |
|
418 | ||
419 | /* (non-Javadoc) |
|
420 | * @see javax.servlet.ServletResponseWrapper#getLocale() |
|
421 | */ |
|
422 | public Locale getLocale() |
|
423 | { |
|
424 | return locale != null ? locale : super.getLocale(); |
|
425 | } |
|
426 | ||
427 | /* (non-Javadoc) |
|
428 | * @see javax.servlet.ServletResponseWrapper#getOutputStream() |
|
429 | */ |
|
430 | public ServletOutputStream getOutputStream() throws IOException |
|
431 | { |
|
432 | if (outputStream == null) |
|
433 | { |
|
434 | if (printWriter != null) |
|
435 | { |
|
436 | throw new IllegalStateException("getWriter() has already been called on this response"); |
|
437 | } |
|
438 | byteOutputBuffer = new ByteArrayOutputStream(); |
|
439 | outputStream = new ServletOutputStream() |
|
440 | { |
|
441 | 0 | public void write(int b) throws IOException |
442 | { |
|
443 | 0 | if (!closed) |
444 | { |
|
445 | 0 | byteOutputBuffer.write(b); |
446 | 0 | if (contentLength>-1 && byteOutputBuffer.size()>=contentLength) |
447 | { |
|
448 | 0 | committed = true; |
449 | 0 | closed = true; |
450 | } |
|
451 | } |
|
452 | 0 | } |
453 | }; |
|
454 | } |
|
455 | return outputStream; |
|
456 | } |
|
457 | ||
458 | /* (non-Javadoc) |
|
459 | * @see javax.servlet.ServletResponseWrapper#getWriter() |
|
460 | */ |
|
461 | public PrintWriter getWriter() throws IOException |
|
462 | { |
|
463 | if (printWriter == null) |
|
464 | { |
|
465 | if (outputStream != null) |
|
466 | { |
|
467 | throw new IllegalStateException("getOutputStream() has already been called on this response"); |
|
468 | } |
|
469 | charOutputBuffer = new CharArrayWriterBuffer(); |
|
470 | printWriter = new PrintWriter(charOutputBuffer); |
|
471 | } |
|
472 | return printWriter; |
|
473 | } |
|
474 | ||
475 | /* (non-Javadoc) |
|
476 | * @see javax.servlet.ServletResponseWrapper#isCommitted() |
|
477 | */ |
|
478 | public boolean isCommitted() |
|
479 | { |
|
480 | return committed; |
|
481 | } |
|
482 | ||
483 | /* (non-Javadoc) |
|
484 | * @see javax.servlet.ServletResponseWrapper#reset() |
|
485 | */ |
|
486 | public void reset() |
|
487 | { |
|
488 | resetBuffer(); // fails if committed |
|
489 | headers = null; |
|
490 | cookies = null; |
|
491 | hasStatus = false; |
|
492 | contentLength = -1; |
|
493 | if (printWriter == null) |
|
494 | { |
|
495 | contentType = null; |
|
496 | characterEncoding = null; |
|
497 | locale = null; |
|
498 | } |
|
499 | } |
|
500 | ||
501 | /* (non-Javadoc) |
|
502 | * @see javax.servlet.ServletResponseWrapper#resetBuffer() |
|
503 | */ |
|
504 | public void resetBuffer() |
|
505 | { |
|
506 | failIfCommitted(); |
|
507 | if (outputStream != null) |
|
508 | { |
|
509 | try { outputStream.flush(); } catch (Exception e){} |
|
510 | byteOutputBuffer.reset(); |
|
511 | } |
|
512 | else if (printWriter != null) |
|
513 | { |
|
514 | printWriter.flush(); |
|
515 | charOutputBuffer.reset(); |
|
516 | } |
|
517 | } |
|
518 | ||
519 | /* (non-Javadoc) |
|
520 | * @see javax.servlet.ServletResponseWrapper#setBufferSize(int) |
|
521 | */ |
|
522 | public void setBufferSize(int size) |
|
523 | { |
|
524 | failIfCommitted(); |
|
525 | if ( (charOutputBuffer != null && charOutputBuffer.size() > 0) |
|
526 | || (byteOutputBuffer != null && byteOutputBuffer.size() > 0) ) |
|
527 | { |
|
528 | throw new IllegalStateException("Content has already been written"); |
|
529 | } |
|
530 | } |
|
531 | ||
532 | /* (non-Javadoc) |
|
533 | * @see javax.servlet.ServletResponseWrapper#setCharacterEncoding(java.lang.String) |
|
534 | */ |
|
535 | public void setCharacterEncoding(String charset) |
|
536 | { |
|
537 | if (charset != null && !committed && printWriter == class="keyword">null) |
|
538 | { |
|
539 | characterEncoding = charset; |
|
540 | } |
|
541 | } |
|
542 | ||
543 | /* (non-Javadoc) |
|
544 | * @see javax.servlet.ServletResponseWrapper#setContentLength(int) |
|
545 | */ |
|
546 | public void setContentLength(int len) |
|
547 | { |
|
548 | if (!committed && printWriter == null && len > 0) |
|
549 | { |
|
550 | contentLength = len; |
|
551 | if (outputStream != null) |
|
552 | { |
|
553 | try { outputStream.flush(); } catch (Exception e){} |
|
554 | } |
|
555 | if ( !closed && byteOutputBuffer != null && byteOutputBuffer.size() >= len ) |
|
556 | { |
|
557 | committed = true; |
|
558 | closed = true; |
|
559 | } |
|
560 | } |
|
561 | } |
|
562 | ||
563 | /* (non-Javadoc) |
|
564 | * @see javax.servlet.ServletResponseWrapper#setContentType(java.lang.String) |
|
565 | */ |
|
566 | public void setContentType(String type) |
|
567 | { |
|
568 | if (!committed) |
|
569 | { |
|
570 | contentType = type; |
|
571 | if (printWriter == null) |
|
572 | { |
|
573 | // TODO: parse possible encoding for better return value from getCharacterEncoding() |
|
574 | } |
|
575 | } |
|
576 | } |
|
577 | ||
578 | /* (non-Javadoc) |
|
579 | * @see javax.servlet.ServletResponseWrapper#setLocale(java.util.Locale) |
|
580 | */ |
|
581 | public void setLocale(Locale locale) |
|
582 | { |
|
583 | if (!committed) |
|
584 | { |
|
585 | this.locale = locale; |
|
586 | /* NON-FIXABLE ISSUE: defaulting the characterEncoding from the Locale |
|
587 | This feature cannot be implemented/wrapped as it might depend on web.xml locale settings |
|
588 | */ |
|
589 | } |
|
590 | } |
|
591 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |