1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.transport.nio;
21
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.SelectableChannel;
25 import java.nio.channels.SocketChannel;
26 import java.util.Queue;
27 import java.util.concurrent.atomic.AtomicBoolean;
28
29 import org.apache.mina.api.IoFuture;
30 import org.apache.mina.api.IoService;
31 import org.apache.mina.api.IoSession;
32 import org.apache.mina.service.idlechecker.IdleChecker;
33 import org.apache.mina.session.AbstractIoSession;
34 import org.apache.mina.session.DefaultWriteFuture;
35 import org.apache.mina.session.DefaultWriteQueue;
36 import org.apache.mina.session.DefaultWriteRequest;
37 import org.apache.mina.session.WriteRequest;
38 import org.apache.mina.util.AbstractIoFuture;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43
44
45
46
47 public abstract class AbstractNioSession extends AbstractIoSession {
48
49 private static final Logger LOG = LoggerFactory.getLogger(AbstractNioSession.class);
50
51
52 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
53
54
55 protected final SelectableChannel channel;
56
57
58 private final AtomicBoolean registeredForWrite = new AtomicBoolean();
59
60
61 private final Queue<WriteRequest> writeQueue = new DefaultWriteQueue();
62
63 public AbstractNioSession(IoService service, SelectableChannel channel, IdleChecker idleChecker) {
64 super(service, idleChecker);
65 this.channel = channel;
66 }
67
68
69
70
71
72
73
74 protected abstract int writeDirect(Object message);
75
76
77
78
79
80
81
82
83 protected abstract ByteBuffer convertToDirectBuffer(WriteRequest writeRequest, boolean createNew);
84
85
86
87
88
89
90 private final IoFuture<Void> closeFuture = new AbstractIoFuture<Void>() {
91
92
93
94
95 @Override
96 protected boolean cancelOwner(final boolean mayInterruptIfRunning) {
97
98 return false;
99 }
100 };
101
102
103
104
105 @Override
106 public IoFuture<Void> close(final boolean immediately) {
107 switch (state) {
108 case CREATED:
109 LOG.error("Session {} not opened", this);
110 throw new IllegalStateException("cannot close an not opened session");
111 case CONNECTED:
112 state = SessionState.CLOSING;
113 if (immediately) {
114 channelClose();
115 processSessionClosed();
116 } else {
117
118 flushWriteQueue();
119 }
120 break;
121 case CLOSING:
122
123 LOG.warn("Already closing session {}", this);
124 break;
125 case CLOSED:
126 LOG.warn("Already closed session {}", this);
127 break;
128 default:
129 throw new IllegalStateException("not implemented session state : " + state);
130 }
131
132 return closeFuture;
133 }
134
135
136
137
138 protected abstract void channelClose();
139
140
141
142
143 @Override
144 public WriteRequest enqueueWriteRequest(WriteRequest writeRequest) {
145 if (IS_DEBUG) {
146 LOG.debug("enqueueWriteRequest {}", writeRequest);
147 }
148
149 if (isConnectedSecured()) {
150
151 SslHelper sslHelper = getAttribute(SSL_HELPER, null);
152
153 if (sslHelper == null) {
154 throw new IllegalStateException();
155 }
156
157 writeRequest = sslHelper.processWrite(this, writeRequest.getMessage(), writeQueue);
158 }
159
160
161 ByteBuffer message = (ByteBuffer) writeRequest.getMessage();
162
163 if (writeQueue.isEmpty()) {
164
165 message = convertToDirectBuffer(writeRequest, false);
166
167
168
169 int written = writeDirect(writeRequest.getMessage());
170
171 if (IS_DEBUG) {
172 LOG.debug("wrote {} bytes to {}", written, this);
173 }
174
175 if (written > 0) {
176 incrementWrittenBytes(written);
177 }
178
179
180 idleChecker.sessionWritten(this, System.currentTimeMillis());
181 int remaining = message.remaining();
182
183 if ((written < 0) || (remaining > 0)) {
184
185 convertToDirectBuffer(writeRequest, true);
186
187
188 writeQueue.add(writeRequest);
189
190
191
192 if (!registeredForWrite.getAndSet(true)) {
193 flushWriteQueue();
194 }
195 } else {
196
197
198
199 final DefaultWriteFuture future = (DefaultWriteFuture) writeRequest.getFuture();
200
201 if (future != null) {
202 future.complete();
203 }
204
205 final Object highLevel = ((DefaultWriteRequest) writeRequest).getOriginalMessage();
206
207 if (highLevel != null) {
208 processMessageSent(highLevel);
209 }
210 }
211 } else {
212
213 message = convertToDirectBuffer(writeRequest, true);
214
215
216 writeQueue.add(writeRequest);
217 }
218 }
219
220 return writeRequest;
221 }
222
223 public abstract void flushWriteQueue();
224
225 public void setNotRegisteredForWrite() {
226 registeredForWrite.set(false);
227 }
228
229 protected boolean isRegisteredForWrite() {
230 return registeredForWrite.get();
231 }
232
233
234
235
236
237
238 public Queue<WriteRequest> getWriteQueue() {
239 return writeQueue;
240 }
241
242
243
244
245
246 public void processWrite(SelectorLoop selectorLoop) {
247 try {
248 if (IS_DEBUG) {
249 LOG.debug("ready for write");
250 LOG.debug("writable session : {}", this);
251 }
252
253 do {
254
255
256
257 final WriteRequest writeRequest = writeQueue.peek();
258
259 if (writeRequest == null) {
260
261 break;
262 }
263
264
265 ByteBuffer buf = (ByteBuffer) writeRequest.getMessage();
266
267
268
269
270
271
272 int written = ((SocketChannel) channel).write(buf);
273
274 if (IS_DEBUG) {
275 LOG.debug("wrote {} bytes to {}", written, this);
276 }
277
278 if (written > 0) {
279 incrementWrittenBytes(written);
280 }
281
282
283 idleChecker.sessionWritten(this, System.currentTimeMillis());
284
285
286 if (buf.remaining() == 0) {
287
288
289
290 writeQueue.poll();
291
292
293 final DefaultWriteFuture future = (DefaultWriteFuture) writeRequest.getFuture();
294
295 if (future != null) {
296 future.complete();
297 }
298
299
300 final Object highLevel = ((DefaultWriteRequest) writeRequest).getOriginalMessage();
301
302 if (highLevel != null) {
303 processMessageSent(highLevel);
304 }
305 } else {
306
307
308
309 break;
310 }
311 } while (!writeQueue.isEmpty());
312
313
314
315
316
317
318
319
320 synchronized (writeQueue) {
321 if (writeQueue.isEmpty()) {
322 if (isClosing()) {
323 if (IS_DEBUG) {
324 LOG.debug("closing session {} have empty write queue, so we close it", this);
325 }
326
327
328 channelClose();
329 } else {
330
331 selectorLoop.modifyRegistration(false, !isReadSuspended(), false, (SelectorListener) this,
332 channel, false);
333
334
335 setNotRegisteredForWrite();
336 }
337 }
338
339
340 }
341 } catch (final IOException e) {
342 LOG.error("Exception while writing : ", e);
343 processException(e);
344 }
345 }
346 }