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