1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
113 try
114 {
115 response.getClass().getMethod("setCharacterEncoding", new Class[]{String.class}).invoke(response, new Object[]{characterEncoding});
116 }
117 catch (NoSuchMethodException nsme)
118 {
119
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
242
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
257
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
269
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
281
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
293
294
295 public boolean containsHeader(String name)
296 {
297 return getHeaderList(name, false) != null;
298 }
299
300
301
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
314
315
316 public void sendError(int errorCode) throws IOException
317 {
318 sendError(errorCode, null);
319 }
320
321
322
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
333
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
346
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
359
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
372
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
380
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
393
394
395 public void flushBuffer() throws IOException
396 {
397 if (!closed)
398 {
399 committed = true;
400 }
401 }
402
403
404
405
406 public int getBufferSize()
407 {
408 return Integer.MAX_VALUE;
409 }
410
411
412
413
414 public String getCharacterEncoding()
415 {
416 return characterEncoding != null ? characterEncoding : "ISO-8859-1";
417 }
418
419
420
421
422 public Locale getLocale()
423 {
424 return locale != null ? locale : super.getLocale();
425 }
426
427
428
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 public void write(int b) throws IOException
442 {
443 if (!closed)
444 {
445 byteOutputBuffer.write(b);
446 if (contentLength>-1 && byteOutputBuffer.size()>=contentLength)
447 {
448 committed = true;
449 closed = true;
450 }
451 }
452 }
453 };
454 }
455 return outputStream;
456 }
457
458
459
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
476
477
478 public boolean isCommitted()
479 {
480 return committed;
481 }
482
483
484
485
486 public void reset()
487 {
488 resetBuffer();
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
502
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
520
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
533
534
535 public void setCharacterEncoding(String charset)
536 {
537 if (charset != null && !committed && printWriter == null)
538 {
539 characterEncoding = charset;
540 }
541 }
542
543
544
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
564
565
566 public void setContentType(String type)
567 {
568 if (!committed)
569 {
570 contentType = type;
571 if (printWriter == null)
572 {
573
574 }
575 }
576 }
577
578
579
580
581 public void setLocale(Locale locale)
582 {
583 if (!committed)
584 {
585 this.locale = locale;
586
587
588
589 }
590 }
591 }