1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.common.message;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.nio.ByteBuffer;
24
25 import org.eclipse.jetty.io.ByteBufferPool;
26 import org.eclipse.jetty.util.BufferUtil;
27 import org.eclipse.jetty.util.log.Log;
28 import org.eclipse.jetty.util.log.Logger;
29 import org.eclipse.jetty.websocket.api.WriteCallback;
30 import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
31 import org.eclipse.jetty.websocket.common.BlockingWriteCallback;
32 import org.eclipse.jetty.websocket.common.WebSocketSession;
33 import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
34
35
36
37
38 public class MessageOutputStream extends OutputStream
39 {
40 private static final Logger LOG = Log.getLogger(MessageOutputStream.class);
41 private final OutgoingFrames outgoing;
42 private final ByteBufferPool bufferPool;
43 private final BlockingWriteCallback blocker;
44 private long frameCount = 0;
45 private BinaryFrame frame;
46 private ByteBuffer buffer;
47 private WriteCallback callback;
48 private boolean closed = false;
49
50 public MessageOutputStream(OutgoingFrames outgoing, int bufferSize, ByteBufferPool bufferPool)
51 {
52 this.outgoing = outgoing;
53 this.bufferPool = bufferPool;
54 this.blocker = new BlockingWriteCallback();
55 this.buffer = bufferPool.acquire(bufferSize,true);
56 BufferUtil.flipToFill(buffer);
57 this.frame = new BinaryFrame();
58 }
59
60 public MessageOutputStream(WebSocketSession session)
61 {
62 this(session.getOutgoingHandler(),session.getPolicy().getMaxBinaryMessageBufferSize(),session.getBufferPool());
63 }
64
65 private void assertNotClosed() throws IOException
66 {
67 if (closed)
68 {
69 IOException e = new IOException("Stream is closed");
70 notifyFailure(e);
71 throw e;
72 }
73 }
74
75 @Override
76 public synchronized void close() throws IOException
77 {
78 assertNotClosed();
79 LOG.debug("close()");
80
81
82 flush(true);
83
84
85 LOG.debug("Sent Frame Count: {}",frameCount);
86 closed = true;
87 try
88 {
89 if (callback != null)
90 {
91 callback.writeSuccess();
92 }
93 super.close();
94 bufferPool.release(buffer);
95 LOG.debug("closed");
96 }
97 catch (IOException e)
98 {
99 notifyFailure(e);
100 throw e;
101 }
102 }
103
104 @Override
105 public synchronized void flush() throws IOException
106 {
107 LOG.debug("flush()");
108 assertNotClosed();
109
110
111 flush(false);
112 try
113 {
114 super.flush();
115 LOG.debug("flushed");
116 }
117 catch (IOException e)
118 {
119 notifyFailure(e);
120 throw e;
121 }
122 }
123
124
125
126
127
128
129
130
131 private synchronized void flush(boolean fin) throws IOException
132 {
133 BufferUtil.flipToFlush(buffer,0);
134 LOG.debug("flush({}): {}",fin,BufferUtil.toDetailString(buffer));
135 frame.setPayload(buffer);
136 frame.setFin(fin);
137
138 try
139 {
140 outgoing.outgoingFrame(frame,blocker);
141
142 blocker.block();
143
144 frameCount++;
145 frame.setIsContinuation();
146 BufferUtil.flipToFill(buffer);
147 }
148 catch (IOException e)
149 {
150 notifyFailure(e);
151 throw e;
152 }
153 }
154
155 private void notifyFailure(IOException e)
156 {
157 if (callback != null)
158 {
159 callback.writeFailed(e);
160 }
161 }
162
163 public void setCallback(WriteCallback callback)
164 {
165 this.callback = callback;
166 }
167
168 @Override
169 public synchronized void write(byte[] b) throws IOException
170 {
171 try
172 {
173 this.write(b,0,b.length);
174 }
175 catch (IOException e)
176 {
177 notifyFailure(e);
178 throw e;
179 }
180 }
181
182 @Override
183 public synchronized void write(byte[] b, int off, int len) throws IOException
184 {
185 LOG.debug("write(byte[{}], {}, {})",b.length,off,len);
186 int left = len;
187 int offset = off;
188 while (left > 0)
189 {
190 if (LOG.isDebugEnabled())
191 {
192 LOG.debug("buffer: {}",BufferUtil.toDetailString(buffer));
193 }
194 int space = buffer.remaining();
195 assert (space > 0);
196 int size = Math.min(space,left);
197 buffer.put(b,offset,size);
198 assert (size > 0);
199 left -= size;
200 if (left > 0)
201 {
202 flush(false);
203 }
204 offset += size;
205 }
206 }
207
208 @Override
209 public synchronized void write(int b) throws IOException
210 {
211 assertNotClosed();
212
213
214 buffer.put((byte)b);
215 if (buffer.remaining() <= 0)
216 {
217 flush(false);
218 }
219 }
220 }