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.ClosedChannelException;
25 import java.nio.channels.SelectableChannel;
26 import java.nio.channels.SelectionKey;
27 import java.nio.channels.Selector;
28 import java.util.Iterator;
29 import java.util.Queue;
30 import java.util.concurrent.ConcurrentLinkedQueue;
31
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35
36
37
38
39
40
41
42 public class NioSelectorLoop implements SelectorLoop {
43
44 private static final Logger LOG = LoggerFactory.getLogger(NioSelectorLoop.class);
45
46 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
47
48
49 private Selector selector;
50
51
52 private final ByteBuffer readBuffer = ByteBuffer.allocateDirect(64 * 1024);
53
54
55 private final Queue<Registration> registrationQueue = new ConcurrentLinkedQueue<Registration>();
56
57
58
59
60
61
62
63 public NioSelectorLoop(final String prefix) {
64 this(prefix, -1);
65 }
66
67
68
69
70
71
72
73 public NioSelectorLoop(final String prefix, final int index) {
74 String workerName = "SelectorWorker " + prefix;
75
76 if (index >= 0) {
77 workerName += "-" + index;
78 }
79
80 SelectorWorker worker = new SelectorWorker(workerName);
81
82 try {
83 if (IS_DEBUG) {
84 LOG.debug("open a selector");
85 }
86
87 selector = Selector.open();
88 } catch (final IOException ioe) {
89 LOG.error("Impossible to open a new NIO selector, O/S is out of file descriptor ?");
90 throw new IllegalStateException("Impossible to open a new NIO selector, O/S is out of file descriptor ?",
91 ioe);
92 }
93
94 if (IS_DEBUG) {
95 LOG.debug("starting worker thread");
96 }
97
98 worker.start();
99
100 }
101
102
103
104
105 @Override
106 public void register(boolean accept, boolean connect, boolean read, boolean write, SelectorListener listener,
107 SelectableChannel channel, RegistrationCallback callback) {
108 if (IS_DEBUG) {
109 LOG.debug("registering : {} for accept : {}, connect: {}, read : {}, write : {}, channel : {}",
110 new Object[] { listener, accept, connect, read, write, channel });
111 }
112
113 int ops = 0;
114
115 if (accept) {
116 ops |= SelectionKey.OP_ACCEPT;
117 }
118
119 if (connect) {
120 ops |= SelectionKey.OP_CONNECT;
121 }
122
123 if (read) {
124 ops |= SelectionKey.OP_READ;
125 }
126
127 if (write) {
128 ops |= SelectionKey.OP_WRITE;
129 }
130
131
132 registrationQueue.add(new Registration(ops, channel, listener, callback));
133
134
135 wakeup();
136 }
137
138
139
140
141 @Override
142 public void modifyRegistration(boolean accept, boolean read, boolean write, final SelectorListener listener,
143 SelectableChannel channel, boolean wakeup) {
144 if (IS_DEBUG) {
145 LOG.debug("modifying registration : {} for accept : {}, read : {}, write : {}, channel : {}", new Object[] {
146 listener, accept, read, write, channel });
147 }
148
149 final SelectionKey key = channel.keyFor(selector);
150
151 if (key == null) {
152 LOG.error("Trying to modify the registration of a not registered channel");
153 return;
154 }
155
156 int ops = 0;
157
158 if (accept) {
159 ops |= SelectionKey.OP_ACCEPT;
160 }
161
162 if (read) {
163 ops |= SelectionKey.OP_READ;
164 }
165
166 if (write) {
167 ops |= SelectionKey.OP_WRITE;
168 }
169
170 key.interestOps(ops);
171
172
173 if (wakeup) {
174 wakeup();
175 }
176 }
177
178
179
180
181 @Override
182 public void unregister(final SelectorListener listener, final SelectableChannel channel) {
183 if (IS_DEBUG) {
184 LOG.debug("unregistering : {}", listener);
185 }
186
187 final SelectionKey key = channel.keyFor(selector);
188
189 if (key == null) {
190 LOG.error("Trying to modify the registration of a not registered channel");
191 return;
192 }
193 key.cancel();
194 key.attach(null);
195
196 if (IS_DEBUG) {
197 LOG.debug("unregistering : {} done !", listener);
198 }
199 }
200
201
202
203
204
205 private class SelectorWorker extends Thread {
206
207 public SelectorWorker(String name) {
208 super(name);
209 setDaemon(true);
210 }
211
212 @Override
213 public void run() {
214
215 for (;;) {
216 try {
217 if (IS_DEBUG) {
218 LOG.debug("selecting...");
219 }
220
221 final int readyCount = selector.select();
222
223 if (IS_DEBUG) {
224 LOG.debug("... done selecting : {} events", readyCount);
225 }
226
227 if (readyCount > 0) {
228 final Iterator<SelectionKey> it = selector.selectedKeys().iterator();
229
230 while (it.hasNext()) {
231 final SelectionKey key = it.next();
232 final SelectorListener listener = (SelectorListener) key.attachment();
233 int ops = key.readyOps();
234 boolean isAcceptable = (ops & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
235 boolean isConnectable = (ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
236 boolean isReadable = (ops & SelectionKey.OP_READ) == SelectionKey.OP_READ;
237 boolean isWritable = (ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;
238 listener.ready(isAcceptable, isConnectable, isReadable, isReadable ? readBuffer : null,
239 isWritable);
240
241
242 if (IS_DEBUG) {
243 LOG.debug("remove");
244 }
245
246 it.remove();
247 }
248 }
249
250
251 while (!registrationQueue.isEmpty()) {
252 final Registration reg = registrationQueue.poll();
253
254 try {
255 SelectionKey selectionKey = reg.channel.register(selector, reg.ops, reg.listener);
256
257 if (reg.getCallback() != null) {
258 reg.getCallback().done(selectionKey);
259 }
260 } catch (final ClosedChannelException ex) {
261
262 LOG.error("socket is already dead", ex);
263 }
264 }
265 } catch (final Exception e) {
266 LOG.error("Unexpected exception : ", e);
267 }
268 }
269 }
270 }
271
272 @Override
273 public void wakeup() {
274 selector.wakeup();
275 }
276
277 private static class Registration {
278
279 public Registration(int ops, SelectableChannel channel, SelectorListener listener, RegistrationCallback callback) {
280 this.ops = ops;
281 this.channel = channel;
282 this.listener = listener;
283 this.callback = callback;
284 }
285
286 private final int ops;
287
288 private final SelectableChannel channel;
289
290 private final SelectorListener listener;
291
292 private final RegistrationCallback callback;
293
294 public RegistrationCallback getCallback() {
295 return callback;
296 }
297
298 @Override
299 public String toString() {
300 StringBuilder sb = new StringBuilder();
301
302 sb.append("Registration : [");
303
304 boolean hasOp = false;
305
306 if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
307 sb.append("OP_READ");
308 hasOp = true;
309 }
310
311 if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
312 if (hasOp) {
313 sb.append("|");
314 }
315
316 sb.append("OP_WRITE");
317 hasOp = true;
318 }
319
320 if ((ops & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
321 if (hasOp) {
322 sb.append("|");
323 }
324
325 sb.append("OP_ACCEPT");
326 hasOp = true;
327 }
328
329 if ((ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) {
330 if (hasOp) {
331 sb.append("|");
332 }
333
334 sb.append("OP_CONNECT");
335 hasOp = true;
336 }
337
338 if (channel != null) {
339 sb.append(", ").append(channel);
340 }
341
342 return sb.toString();
343 }
344 }
345 }