001 /*
002 * Copyright 1999,2004 The Apache Software Foundation.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017
018 package org.apache.commons.messagelet.impl;
019
020
021 import java.io.IOException;
022 import java.io.OutputStream;
023 import java.io.OutputStreamWriter;
024 import java.io.PrintWriter;
025 import java.util.Locale;
026
027 import javax.servlet.ServletOutputStream;
028 import javax.servlet.ServletResponse;
029
030
031 /**
032 * Based on the ResponseBase code from Catalina.
033 *
034 * @author Craig R. McClanahan
035 * @author James Strachan
036 * @version $Revision: 155459 $ $Date: 2005-02-26 13:24:44 +0000 (Sat, 26 Feb 2005) $
037 */
038
039 public class ServletResponseImpl implements ServletResponse {
040
041
042 // ----------------------------------------------------- Instance Variables
043
044
045 /**
046 * The buffer through which all of our output bytes are passed.
047 */
048 protected byte[] buffer = new byte[1024];
049
050
051 /**
052 * The number of data bytes currently in the buffer.
053 */
054 protected int bufferCount = 0;
055
056
057 /**
058 * Has this response been committed yet?
059 */
060 protected boolean committed = false;
061
062
063 /**
064 * The actual number of bytes written to this Response.
065 */
066 protected int contentCount = 0;
067
068
069 /**
070 * The content length associated with this Response.
071 */
072 protected int contentLength = -1;
073
074
075 /**
076 * The content type associated with this Response.
077 */
078 protected String contentType = null;
079
080
081 /**
082 * The character encoding associated with this Response.
083 */
084 protected String encoding = null;
085
086
087 /**
088 * Are we currently processing inside a RequestDispatcher.include()?
089 */
090 protected boolean included = false;
091
092
093 /**
094 * The Locale associated with this Response.
095 */
096 protected Locale locale = Locale.getDefault();
097
098
099 /**
100 * The output stream associated with this Response.
101 */
102 protected OutputStream output = null;
103
104
105 /**
106 * The ServletOutputStream that has been returned by
107 * <code>getOutputStream()</code>, if any.
108 */
109 protected ServletOutputStream stream = null;
110
111
112 /**
113 * The PrintWriter that has been returned by
114 * <code>getWriter()</code>, if any.
115 */
116 protected PrintWriter writer = null;
117
118
119 /**
120 * Error flag. True if the response is an error report.
121 */
122 protected boolean error = false;
123
124
125 // ------------------------------------------------------------- Properties
126
127
128
129 /**
130 * Return the number of bytes actually written to the output stream.
131 */
132 public int getContentCount() {
133
134 return (this.contentCount);
135
136 }
137
138
139
140
141 /**
142 * Return the output stream associated with this Response.
143 */
144 public OutputStream getStream() {
145
146 return (this.output);
147
148 }
149
150
151 /**
152 * Set the output stream associated with this Response.
153 *
154 * @param stream The new output stream
155 */
156 public void setStream(OutputStream stream) {
157
158 this.output = stream;
159
160 }
161
162
163
164 // --------------------------------------------------------- Public Methods
165
166
167 /**
168 * Create and return a ServletOutputStream to write the content
169 * associated with this Response.
170 *
171 * @exception IOException if an input/output error occurs
172 */
173 public ServletOutputStream createOutputStream() throws IOException {
174
175 //return (new ResponseStream(this));
176 return new BufferedServletOutputStream();
177 }
178
179
180 /**
181 * Perform whatever actions are required to flush and close the output
182 * stream or writer, in a single operation.
183 *
184 * @exception IOException if an input/output error occurs
185 */
186 public void finishResponse() throws IOException {
187
188 // If no stream has been requested yet, get one so we can
189 // flush the necessary headers
190 if (this.stream == null) {
191 ServletOutputStream sos = getOutputStream();
192 sos.flush();
193 sos.close();
194 return;
195 }
196
197 // If our stream is closed, no action is necessary
198 /*
199 if ( ((ResponseStream) stream).closed() )
200 return;
201 */
202
203 // Flush and close the appropriate output mechanism
204 if (writer != null) {
205 writer.flush();
206 writer.close();
207 } else {
208 stream.flush();
209 stream.close();
210 }
211
212 // The underlying output stream (perhaps from a socket)
213 // is not our responsibility
214
215 }
216
217
218 /**
219 * Return the content length that was set or calculated for this Response.
220 */
221 public int getContentLength() {
222
223 return (this.contentLength);
224
225 }
226
227
228 /**
229 * Return the content type that was set or calculated for this response,
230 * or <code>null</code> if no content type was set.
231 */
232 public String getContentType() {
233
234 return (this.contentType);
235
236 }
237
238
239
240 // -------------------------------------------------------- Package Methods
241
242
243
244 // ------------------------------------------------ ServletResponse Methods
245
246
247 /**
248 * Flush the buffer and commit this response.
249 *
250 * @exception IOException if an input/output error occurs
251 */
252 public void flushBuffer() throws IOException {
253
254 committed = true;
255 if (bufferCount > 0) {
256 try {
257 output.write(buffer, 0, bufferCount);
258 } finally {
259 bufferCount = 0;
260 }
261 }
262
263 }
264
265
266 /**
267 * Return the actual buffer size used for this Response.
268 */
269 public int getBufferSize() {
270
271 return (buffer.length);
272
273 }
274
275
276 /**
277 * Return the character encoding used for this Response.
278 */
279 public String getCharacterEncoding() {
280
281 if (encoding == null)
282 return ("ISO-8859-1");
283 else
284 return (encoding);
285
286 }
287
288
289 /**
290 * Return the servlet output stream associated with this Response.
291 *
292 * @exception IllegalStateException if <code>getWriter</code> has
293 * already been called for this response
294 * @exception IOException if an input/output error occurs
295 */
296 public ServletOutputStream getOutputStream() throws IOException {
297
298 if (writer != null) {
299 throw new IllegalStateException( "getWriter() has already been called" );
300 }
301
302 if (stream == null)
303 stream = createOutputStream();
304 /*
305 ((ResponseStream) stream).setCommit(true);
306 */
307 return (stream);
308
309 }
310
311
312 /**
313 * Return the Locale assigned to this response.
314 */
315 public Locale getLocale() {
316
317 return (locale);
318
319 }
320
321
322 /**
323 * Return the writer associated with this Response.
324 *
325 * @exception IllegalStateException if <code>getOutputStream</code> has
326 * already been called for this response
327 * @exception IOException if an input/output error occurs
328 */
329 public PrintWriter getWriter() throws IOException {
330
331 if (writer != null)
332 return (writer);
333
334 if (stream != null) {
335 throw new IllegalStateException( "getOutputStream() has already been called" );
336 }
337
338 stream = createOutputStream();
339
340 // a slight hack which slightly breaks the Servlet contract...
341 // see commented out section below for what it should be...
342 writer = new PrintWriter( new OutputStreamWriter(stream, getCharacterEncoding()) );
343 return writer;
344
345 /*
346 ((ResponseStream) stream).setCommit(false);
347 OutputStreamWriter osr =
348 new OutputStreamWriter(stream, getCharacterEncoding());
349 writer = new ResponseWriter(osr, (ResponseStream) stream);
350 return (writer);
351 */
352 }
353
354
355 /**
356 * Has the output of this response already been committed?
357 */
358 public boolean isCommitted() {
359
360 return (committed);
361
362 }
363
364
365 /**
366 * Clear any content written to the buffer.
367 *
368 * @exception IllegalStateException if this response has already
369 * been committed
370 */
371 public void reset() {
372
373 if (committed) {
374 throw new IllegalStateException( "response has already been committed" );
375 }
376
377 if (included)
378 return; // Ignore any call from an included servlet
379
380 /*
381 if (stream != null)
382 ((ResponseStream) stream).reset();
383 */
384 bufferCount = 0;
385 contentLength = -1;
386 contentType = null;
387
388 }
389
390
391 /**
392 * Reset the data buffer but not any status or header information.
393 *
394 * @exception IllegalStateException if the response has already
395 * been committed
396 */
397 public void resetBuffer() {
398
399 if (committed) {
400 throw new IllegalStateException( "response has already been committed" );
401 }
402
403 bufferCount = 0;
404
405 }
406
407
408 /**
409 * Set the buffer size to be used for this Response.
410 *
411 * @param size The new buffer size
412 *
413 * @exception IllegalStateException if this method is called after
414 * output has been committed for this response
415 */
416 public void setBufferSize(int size) {
417
418 if (committed || (bufferCount > 0)) {
419 throw new IllegalStateException( "Output has already been committed" );
420 }
421
422 if (buffer.length >= size)
423 return;
424 buffer = new byte[size];
425
426 }
427
428
429 /**
430 * Set the content length (in bytes) for this Response.
431 *
432 * @param length The new content length
433 */
434 public void setContentLength(int length) {
435
436 if (isCommitted())
437 return;
438
439 if (included)
440 return; // Ignore any call from an included servlet
441
442 this.contentLength = length;
443
444 }
445
446
447 /**
448 * Set the content type for this Response.
449 *
450 * @param type The new content type
451 */
452 public void setContentType(String type) {
453
454 if (isCommitted())
455 return;
456
457 if (included)
458 return; // Ignore any call from an included servlet
459
460 this.contentType = type;
461 /*
462 if (type.indexOf(';') >= 0) {
463 encoding = RequestUtil.parseCharacterEncoding(type);
464 if (encoding == null)
465 encoding = "ISO-8859-1";
466 }
467 */
468 }
469
470
471 /**
472 * Set the Locale that is appropriate for this response, including
473 * setting the appropriate character encoding.
474 *
475 * @param locale The new locale
476 */
477 public void setLocale(Locale locale) {
478
479 if (isCommitted())
480 return;
481
482 if (included)
483 return; // Ignore any call from an included servlet
484
485 this.locale = locale;
486 /*
487 if ((this.encoding == null) && (this.context != null)) {
488 CharsetMapper mapper = context.getCharsetMapper();
489 this.encoding = mapper.getCharset(locale);
490 }
491 */
492 }
493
494
495 }