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    }