AbstractSessionOutputBuffer.java revision 417f3b92ba4549b2f22340e3107d869d2b9c5bb8
1/*
2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionOutputBuffer.java $
3 * $Revision: 652091 $
4 * $Date: 2008-04-29 13:41:07 -0700 (Tue, 29 Apr 2008) $
5 *
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements.  See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership.  The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License.  You may obtain a copy of the License at
14 *
15 *   http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied.  See the License for the
21 * specific language governing permissions and limitations
22 * under the License.
23 * ====================================================================
24 *
25 * This software consists of voluntary contributions made by many
26 * individuals on behalf of the Apache Software Foundation.  For more
27 * information on the Apache Software Foundation, please see
28 * <http://www.apache.org/>.
29 *
30 */
31
32package org.apache.http.impl.io;
33
34import java.io.IOException;
35import java.io.OutputStream;
36
37import org.apache.http.io.SessionOutputBuffer;
38import org.apache.http.io.HttpTransportMetrics;
39import org.apache.http.params.HttpParams;
40import org.apache.http.params.HttpProtocolParams;
41import org.apache.http.protocol.HTTP;
42import org.apache.http.util.ByteArrayBuffer;
43import org.apache.http.util.CharArrayBuffer;
44
45/**
46 * Abstract base class for session output buffers that stream data
47 * to an {@link OutputStream}.
48 *
49 * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
50 *
51 */
52public abstract class AbstractSessionOutputBuffer implements SessionOutputBuffer {
53
54    private static final byte[] CRLF = new byte[] {HTTP.CR, HTTP.LF};
55
56    private static final int MAX_CHUNK = 256;
57
58    private OutputStream outstream;
59    private ByteArrayBuffer buffer;
60
61    private String charset = HTTP.US_ASCII;
62    private boolean ascii = true;
63
64    private HttpTransportMetricsImpl metrics;
65
66    protected void init(final OutputStream outstream, int buffersize, final HttpParams params) {
67        if (outstream == null) {
68            throw new IllegalArgumentException("Input stream may not be null");
69        }
70        if (buffersize <= 0) {
71            throw new IllegalArgumentException("Buffer size may not be negative or zero");
72        }
73        if (params == null) {
74            throw new IllegalArgumentException("HTTP parameters may not be null");
75        }
76        this.outstream = outstream;
77        this.buffer = new ByteArrayBuffer(buffersize);
78        this.charset = HttpProtocolParams.getHttpElementCharset(params);
79        this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
80                     || this.charset.equalsIgnoreCase(HTTP.ASCII);
81        this.metrics = new HttpTransportMetricsImpl();
82    }
83
84    protected void flushBuffer() throws IOException {
85        int len = this.buffer.length();
86        if (len > 0) {
87            this.outstream.write(this.buffer.buffer(), 0, len);
88            this.buffer.clear();
89            this.metrics.incrementBytesTransferred(len);
90        }
91    }
92
93    public void flush() throws IOException {
94        flushBuffer();
95        this.outstream.flush();
96    }
97
98    public void write(final byte[] b, int off, int len) throws IOException {
99        if (b == null) {
100            return;
101        }
102        // Do not want to buffer largish chunks
103        // if the byte array is larger then MAX_CHUNK
104        // write it directly to the output stream
105        if (len > MAX_CHUNK || len > this.buffer.capacity()) {
106            // flush the buffer
107            flushBuffer();
108            // write directly to the out stream
109            this.outstream.write(b, off, len);
110            this.metrics.incrementBytesTransferred(len);
111        } else {
112            // Do not let the buffer grow unnecessarily
113            int freecapacity = this.buffer.capacity() - this.buffer.length();
114            if (len > freecapacity) {
115                // flush the buffer
116                flushBuffer();
117            }
118            // buffer
119            this.buffer.append(b, off, len);
120        }
121    }
122
123    public void write(final byte[] b) throws IOException {
124        if (b == null) {
125            return;
126        }
127        write(b, 0, b.length);
128    }
129
130    public void write(int b) throws IOException {
131        if (this.buffer.isFull()) {
132            flushBuffer();
133        }
134        this.buffer.append(b);
135    }
136
137    public void writeLine(final String s) throws IOException {
138        if (s == null) {
139            return;
140        }
141        if (s.length() > 0) {
142            write(s.getBytes(this.charset));
143        }
144        write(CRLF);
145    }
146
147    public void writeLine(final CharArrayBuffer s) throws IOException {
148        if (s == null) {
149            return;
150        }
151        if (this.ascii) {
152            int off = 0;
153            int remaining = s.length();
154            while (remaining > 0) {
155                int chunk = this.buffer.capacity() - this.buffer.length();
156                chunk = Math.min(chunk, remaining);
157                if (chunk > 0) {
158                    this.buffer.append(s, off, chunk);
159                }
160                if (this.buffer.isFull()) {
161                    flushBuffer();
162                }
163                off += chunk;
164                remaining -= chunk;
165            }
166        } else {
167            // This is VERY memory inefficient, BUT since non-ASCII charsets are
168            // NOT meant to be used anyway, there's no point optimizing it
169            byte[] tmp = s.toString().getBytes(this.charset);
170            write(tmp);
171        }
172        write(CRLF);
173    }
174
175    public HttpTransportMetrics getMetrics() {
176        return this.metrics;
177    }
178
179}
180