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 
18 package socket.io;
19 
20 import java.io.DataOutputStream;
21 import java.io.FilterOutputStream;
22 import java.io.IOException;
23 import java.io.OutputStream;
24 
25 /**
26  * This output stream works in conjunction with the WrappedInputStream
27  * to introduce a protocol for sending arbitrary length data in a
28  * uniform way. This output stream allows variable length data to be
29  * inserted into an existing output stream so that it can be read by
30  * an input stream without reading too many bytes (in case of buffering
31  * by the input stream).
32  * <p>
33  * This output stream is used like any normal output stream. The protocol
34  * is introduced by the WrappedOutputStream and does not need to be known
35  * by the user of this class. However, for those that are interested, the
36  * method is described below.
37  * <p>
38  * The output stream writes the requested bytes as packets of binary
39  * information. The packet consists of a header and payload. The header
40  * is two bytes of a single unsigned short (written in network order)
41  * that specifies the length of bytes in the payload. A header value of
42  * 0 indicates that the stream is "closed".
43  * <p>
44  * <strong>Note:</strong> For this wrapped output stream to be used,
45  * the application <strong>must</strong> call <code>close()</code>
46  * to end the output.
47  *
48  * @see WrappedInputStream
49  *
50  * @author Andy Clark, IBM
51  *
52  * @version $Id: WrappedOutputStream.java 447688 2006-09-19 02:39:49Z mrglavas $
53  */
54 public class WrappedOutputStream
55     extends FilterOutputStream {
56 
57     //
58     // Constants
59     //
60 
61     /** Default buffer size (1024). */
62     public static final int DEFAULT_BUFFER_SIZE = 1024;
63 
64     //
65     // Data
66     //
67 
68     /** Buffer. */
69     protected byte[] fBuffer;
70 
71     /** Buffer position. */
72     protected int fPosition;
73 
74     /**
75      * Data output stream. This stream is used to output the block sizes
76      * into the data stream that are read by the WrappedInputStream.
77      * <p>
78      * <strong>Note:</strong> The data output stream is only used for
79      * writing the byte count for performance reasons. We avoid the
80      * method indirection for writing the byte data.
81      */
82     protected DataOutputStream fDataOutputStream;
83 
84     //
85     // Constructors
86     //
87 
88     /** Constructs a wrapper for the given output stream. */
WrappedOutputStream(OutputStream stream)89     public WrappedOutputStream(OutputStream stream) {
90         this(stream, DEFAULT_BUFFER_SIZE);
91     } // <init>(OutputStream)
92 
93     /**
94      * Constructs a wrapper for the given output stream with the
95      * given buffer size.
96      */
WrappedOutputStream(OutputStream stream, int bufferSize)97     public WrappedOutputStream(OutputStream stream, int bufferSize) {
98         super(stream);
99         fBuffer = new byte[bufferSize];
100         fDataOutputStream = new DataOutputStream(stream);
101     } // <init>(OutputStream)
102 
103     //
104     // OutputStream methods
105     //
106 
107     /**
108      * Writes a single byte to the output.
109      * <p>
110      * <strong>Note:</strong> Single bytes written to the output stream
111      * will be buffered
112      */
write(int b)113     public void write(int b) throws IOException {
114         fBuffer[fPosition++] = (byte)b;
115         if (fPosition == fBuffer.length) {
116             fPosition = 0;
117             fDataOutputStream.writeInt(fBuffer.length);
118             super.out.write(fBuffer, 0, fBuffer.length);
119         }
120     } // write(int)
121 
122     /** Writes an array of bytes to the output. */
write(byte[] b, int offset, int length)123     public void write(byte[] b, int offset, int length)
124         throws IOException {
125 
126         // flush existing buffer
127         if (fPosition > 0) {
128             flush0();
129         }
130 
131         // write header followed by actual bytes
132         fDataOutputStream.writeInt(length);
133         super.out.write(b, offset, length);
134 
135     } // write(byte[])
136 
137     /**
138      * Flushes the output buffer, writing all bytes currently in
139      * the buffer to the output.
140      */
flush()141     public void flush() throws IOException {
142         flush0();
143         super.out.flush();
144     } // flush()
145 
146     /**
147      * Closes the output stream. This method <strong>must</strong> be
148      * called when done writing all data to the output stream.
149      * <p>
150      * <strong>Note:</strong> This method does <em>not</em> close the
151      * actual output stream, only makes the input stream see the stream
152      * closed. Do not write bytes after closing the output stream.
153      */
close()154     public void close() throws IOException {
155         flush0();
156         fDataOutputStream.writeInt(0);
157         super.out.flush();
158     } // close()
159 
160     //
161     // Protected methods
162     //
163 
164     /**
165      * Flushes the output buffer, writing all bytes currently in
166      * the buffer to the output. This method does not call the
167      * flush() method of the output stream; it merely writes the
168      * remaining bytes in the buffer.
169      */
flush0()170     public void flush0() throws IOException {
171         int length = fPosition;
172         fPosition = 0;
173         if (length > 0) {
174             fDataOutputStream.writeInt(length);
175             super.out.write(fBuffer, 0, length);
176         }
177     } // flush0()
178 
179 } // class WrappedOutputStream
180