sonixlabs-eventmachine-java 1.0.0.rc.7-java → 1.0.3.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -12
  2. data/.travis.yml +12 -0
  3. data/CHANGELOG.md +33 -0
  4. data/Gemfile +0 -1
  5. data/README.md +2 -2
  6. data/README_JP.md +10 -2
  7. data/eventmachine.gemspec +10 -7
  8. data/ext/binder.cpp +1 -1
  9. data/ext/cmain.cpp +11 -0
  10. data/ext/ed.cpp +22 -7
  11. data/ext/ed.h +5 -5
  12. data/ext/em.cpp +64 -66
  13. data/ext/em.h +10 -5
  14. data/ext/eventmachine.h +1 -0
  15. data/ext/extconf.rb +4 -3
  16. data/ext/fastfilereader/extconf.rb +1 -1
  17. data/ext/rubymain.cpp +22 -1
  18. data/ext/ssl.cpp +1 -1
  19. data/java/.classpath +1 -3
  20. data/java/.gitignore +1 -0
  21. data/java/src/com/rubyeventmachine/DatagramPacket.java +13 -0
  22. data/java/src/com/rubyeventmachine/EmReactor.java +502 -561
  23. data/java/src/com/rubyeventmachine/EventCallback.java +7 -0
  24. data/java/src/com/rubyeventmachine/EventCode.java +26 -0
  25. data/java/src/com/rubyeventmachine/EventableChannel.java +102 -42
  26. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +146 -161
  27. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +371 -334
  28. data/java/src/com/rubyeventmachine/SslBox.java +310 -0
  29. data/lib/em/iterator.rb +5 -44
  30. data/lib/em/processes.rb +1 -1
  31. data/lib/em/protocols/httpclient.rb +2 -2
  32. data/lib/em/protocols/line_protocol.rb +2 -2
  33. data/lib/em/protocols/smtpclient.rb +1 -1
  34. data/lib/em/protocols/smtpserver.rb +10 -7
  35. data/lib/em/protocols/stomp.rb +5 -2
  36. data/lib/em/protocols.rb +1 -0
  37. data/lib/em/version.rb +1 -1
  38. data/lib/eventmachine.rb +35 -14
  39. data/lib/jeventmachine.rb +65 -18
  40. data/rakelib/package.rake +15 -18
  41. data/tests/server.crt +36 -0
  42. data/tests/server.key +51 -0
  43. data/tests/test_attach.rb +24 -0
  44. data/tests/test_connection_count.rb +21 -1
  45. data/tests/test_epoll.rb +15 -0
  46. data/tests/test_httpclient2.rb +5 -0
  47. data/tests/test_idle_connection.rb +6 -4
  48. data/tests/test_iterator.rb +97 -0
  49. data/tests/test_line_protocol.rb +33 -0
  50. data/tests/test_pause.rb +24 -0
  51. data/tests/test_set_sock_opt.rb +1 -1
  52. data/tests/test_ssl_echo_data.rb +60 -0
  53. data/tests/test_ssl_methods.rb +15 -7
  54. data/tests/test_ssl_verify.rb +4 -4
  55. data/tests/test_stomp.rb +37 -0
  56. data/tests/test_system.rb +42 -0
  57. metadata +45 -42
  58. data/lib/rubyeventmachine.jar +0 -0
@@ -1,588 +1,529 @@
1
1
  /**
2
2
  * $Id$
3
- *
3
+ *
4
4
  * Author:: Francis Cianfrocca (gmail: blackhedd)
5
5
  * Homepage:: http://rubyeventmachine.com
6
6
  * Date:: 15 Jul 2007
7
- *
7
+ *
8
8
  * See EventMachine and EventMachine::Connection for documentation and
9
9
  * usage examples.
10
- *
10
+ *
11
11
  *
12
12
  *----------------------------------------------------------------------------
13
13
  *
14
14
  * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
15
15
  * Gmail: blackhedd
16
- *
16
+ *
17
17
  * This program is free software; you can redistribute it and/or modify
18
18
  * it under the terms of either: 1) the GNU General Public License
19
19
  * as published by the Free Software Foundation; either version 2 of the
20
20
  * License, or (at your option) any later version; or 2) Ruby's License.
21
- *
21
+ *
22
22
  * See the file COPYING for complete licensing information.
23
23
  *
24
24
  *---------------------------------------------------------------------------
25
25
  *
26
- *
26
+ *
27
27
  */
28
28
 
29
29
  package com.rubyeventmachine;
30
30
 
31
- import java.io.*;
32
- import java.nio.channels.*;
33
- import java.util.*;
34
- import java.nio.*;
35
- import java.net.*;
36
- import java.util.concurrent.atomic.*;
37
- import java.security.*;
38
- import java.lang.reflect.UndeclaredThrowableException;
31
+ import java.io.IOException;
32
+ import java.net.InetSocketAddress;
33
+ import java.net.SocketAddress;
34
+ import java.nio.ByteBuffer;
35
+ import java.nio.channels.ClosedChannelException;
36
+ import java.nio.channels.DatagramChannel;
37
+ import java.nio.channels.SelectionKey;
38
+ import java.nio.channels.Selector;
39
+ import java.nio.channels.ServerSocketChannel;
40
+ import java.nio.channels.SocketChannel;
41
+ import java.security.KeyManagementException;
42
+ import java.security.KeyStore;
43
+ import java.security.NoSuchAlgorithmException;
44
+ import java.util.ArrayList;
45
+ import java.util.Date;
46
+ import java.util.HashMap;
47
+ import java.util.Iterator;
48
+ import java.util.TreeMap;
49
+ import java.util.concurrent.atomic.AtomicBoolean;
39
50
 
40
51
  public class EmReactor {
41
- public final int EM_TIMER_FIRED = 100;
42
- public final int EM_CONNECTION_READ = 101;
43
- public final int EM_CONNECTION_UNBOUND = 102;
44
- public final int EM_CONNECTION_ACCEPTED = 103;
45
- public final int EM_CONNECTION_COMPLETED = 104;
46
- public final int EM_LOOPBREAK_SIGNAL = 105;
47
- public final int EM_CONNECTION_NOTIFY_READABLE = 106;
48
- public final int EM_CONNECTION_NOTIFY_WRITABLE = 107;
49
- public final int EM_SSL_HANDSHAKE_COMPLETED = 108;
50
- public final int EM_SSL_VERIFY = 109;
51
- public final int EM_PROXY_TARGET_UNBOUND = 110;
52
- public final int EM_PROXY_COMPLETED = 111;
53
-
54
- private Selector mySelector;
55
- private TreeMap<Long, ArrayList<Long>> Timers;
56
- private HashMap<Long, EventableChannel> Connections;
57
- private HashMap<Long, ServerSocketChannel> Acceptors;
58
- private ArrayList<Long> NewConnections;
59
- private ArrayList<Long> UnboundConnections;
60
- private ArrayList<EventableSocketChannel> DetachedConnections;
61
-
62
- private boolean bRunReactor;
63
- private long BindingIndex;
64
- private AtomicBoolean loopBreaker;
65
- private ByteBuffer myReadBuffer;
66
- private int timerQuantum;
67
-
68
- public EmReactor() {
69
- Timers = new TreeMap<Long, ArrayList<Long>>();
70
- Connections = new HashMap<Long, EventableChannel>();
71
- Acceptors = new HashMap<Long, ServerSocketChannel>();
72
- NewConnections = new ArrayList<Long>();
73
- UnboundConnections = new ArrayList<Long>();
74
- DetachedConnections = new ArrayList<EventableSocketChannel>();
75
-
76
- BindingIndex = 0;
77
- loopBreaker = new AtomicBoolean();
78
- loopBreaker.set(false);
79
- myReadBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
80
- timerQuantum = 98;
81
- }
82
-
83
- /**
84
- * This is a no-op stub, intended to be overridden in user code.
85
- */
86
- public void eventCallback (long sig, int eventType, ByteBuffer data, long data2) {
87
- System.out.println ("Default callback: "+sig+" "+eventType+" "+data+" "+data2);
88
- }
89
- public void eventCallback (long sig, int eventType, ByteBuffer data) {
90
- eventCallback (sig, eventType, data, 0);
91
- }
92
-
93
-
94
- public void run() {
95
- try {
96
- mySelector = Selector.open();
97
- bRunReactor = true;
98
- } catch (IOException e) {
99
- throw new RuntimeException ("Could not open selector", e);
100
- }
101
-
102
- while (bRunReactor) {
103
- runLoopbreaks();
104
- if (!bRunReactor) break;
105
-
106
- runTimers();
107
- if (!bRunReactor) break;
108
-
109
- removeUnboundConnections();
110
- checkIO();
111
- addNewConnections();
112
- processIO();
113
- }
114
-
115
- close();
116
- }
117
-
118
- void addNewConnections() {
119
- ListIterator<EventableSocketChannel> iter = DetachedConnections.listIterator(0);
120
- while (iter.hasNext()) {
121
- EventableSocketChannel ec = iter.next();
122
- ec.cleanup();
123
- }
124
- DetachedConnections.clear();
125
-
126
- ListIterator<Long> iter2 = NewConnections.listIterator(0);
127
- while (iter2.hasNext()) {
128
- long b = iter2.next();
129
-
130
- EventableChannel ec = Connections.get(b);
131
- if (ec != null) {
132
- try {
133
- ec.register();
134
- } catch (ClosedChannelException e) {
135
- UnboundConnections.add (ec.getBinding());
136
- }
137
- }
138
- }
139
- NewConnections.clear();
140
- }
141
-
142
- void removeUnboundConnections() {
143
- ListIterator<Long> iter = UnboundConnections.listIterator(0);
144
- while (iter.hasNext()) {
145
- long b = iter.next();
146
-
147
- EventableChannel ec = Connections.remove(b);
148
- if (ec != null) {
149
- eventCallback (b, EM_CONNECTION_UNBOUND, null);
150
- ec.close();
151
-
152
- EventableSocketChannel sc = (EventableSocketChannel) ec;
153
- if (sc != null && sc.isAttached())
154
- DetachedConnections.add (sc);
155
- }
156
- }
157
- UnboundConnections.clear();
158
- }
159
-
160
- void checkIO() {
161
- long timeout;
162
-
163
- if (NewConnections.size() > 0) {
164
- timeout = -1;
165
- } else if (!Timers.isEmpty()) {
166
- long now = new Date().getTime();
167
- long k = Timers.firstKey();
168
- long diff = k-now;
169
-
170
- if (diff <= 0)
171
- timeout = -1; // don't wait, just poll once
172
- else
173
- timeout = diff;
174
- } else {
175
- timeout = 0; // wait indefinitely
176
- }
177
-
178
- try {
179
- if (timeout == -1)
180
- mySelector.selectNow();
181
- else
182
- mySelector.select(timeout);
183
- } catch (IOException e) {
184
- e.printStackTrace();
185
- }
186
- }
187
-
188
- void processIO() {
189
- Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
190
- while (it.hasNext()) {
191
- SelectionKey k = it.next();
192
- it.remove();
193
-
194
- if (k.isConnectable())
195
- isConnectable(k);
196
-
197
- else if (k.isAcceptable())
198
- isAcceptable(k);
199
-
200
- else {
201
- if (k.isWritable())
202
- isWritable(k);
203
-
204
- if (k.isReadable())
205
- isReadable(k);
206
- }
207
- }
208
- }
209
-
210
- void isAcceptable (SelectionKey k) {
211
- ServerSocketChannel ss = (ServerSocketChannel) k.channel();
212
- SocketChannel sn;
213
- long b;
214
-
215
- for (int n = 0; n < 10; n++) {
216
- try {
217
- sn = ss.accept();
218
- if (sn == null)
219
- break;
220
- } catch (IOException e) {
221
- e.printStackTrace();
222
- k.cancel();
223
-
224
- ServerSocketChannel server = Acceptors.remove(k.attachment());
225
- if (server != null)
226
- try{ server.close(); } catch (IOException ex) {};
227
- break;
228
- }
229
-
230
- try {
231
- sn.configureBlocking(false);
232
- } catch (IOException e) {
233
- e.printStackTrace();
234
- continue;
235
- }
236
-
237
- b = createBinding();
238
- EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
239
- Connections.put (b, ec);
240
- NewConnections.add (b);
241
-
242
- eventCallback (((Long)k.attachment()).longValue(), EM_CONNECTION_ACCEPTED, null, b);
243
- }
244
- }
245
-
246
- void isReadable (SelectionKey k) {
247
- EventableChannel ec = (EventableChannel) k.attachment();
248
- long b = ec.getBinding();
249
-
250
- if (ec.isWatchOnly()) {
251
- if (ec.isNotifyReadable())
252
- eventCallback (b, EM_CONNECTION_NOTIFY_READABLE, null);
253
- } else {
254
- myReadBuffer.clear();
255
-
256
- try {
257
- ec.readInboundData (myReadBuffer);
258
- myReadBuffer.flip();
259
- if (myReadBuffer.limit() > 0)
260
- eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
261
- } catch (IOException e) {
262
- UnboundConnections.add (b);
263
- }
264
- }
265
- }
266
-
267
- void isWritable (SelectionKey k) {
268
- EventableChannel ec = (EventableChannel) k.attachment();
269
- long b = ec.getBinding();
270
-
271
- if (ec.isWatchOnly()) {
272
- if (ec.isNotifyWritable())
273
- eventCallback (b, EM_CONNECTION_NOTIFY_WRITABLE, null);
274
- }
275
- else {
276
- try {
277
- if (!ec.writeOutboundData())
278
- UnboundConnections.add (b);
279
- } catch (IOException e) {
280
- UnboundConnections.add (b);
281
- }
282
- }
283
- }
284
-
285
- void isConnectable (SelectionKey k) {
286
- EventableSocketChannel ec = (EventableSocketChannel) k.attachment();
287
- long b = ec.getBinding();
288
-
289
- try {
290
- if (ec.finishConnecting())
291
- eventCallback (b, EM_CONNECTION_COMPLETED, null);
292
- else
293
- UnboundConnections.add (b);
294
- } catch (IOException e) {
295
- UnboundConnections.add (b);
296
- }
297
- }
298
-
299
- void close() {
300
- try {
301
- if (mySelector != null)
302
- mySelector.close();
303
- } catch (IOException e) {}
304
- mySelector = null;
305
-
306
- // run down open connections and sockets.
307
- Iterator<ServerSocketChannel> i = Acceptors.values().iterator();
308
- while (i.hasNext()) {
309
- try {
310
- i.next().close();
311
- } catch (IOException e) {}
312
- }
313
-
314
- // 29Sep09: We create an ArrayList of the existing connections, then iterate over
315
- // that to call unbind on them. This is because an unbind can trigger a reconnect,
316
- // which will add to the Connections HashMap, causing a ConcurrentModificationException.
317
- // XXX: The correct behavior here would be to latch the various reactor methods to return
318
- // immediately if the reactor is shutting down.
319
- ArrayList<EventableChannel> conns = new ArrayList<EventableChannel>();
320
- Iterator<EventableChannel> i2 = Connections.values().iterator();
321
- while (i2.hasNext()) {
322
- EventableChannel ec = i2.next();
323
- if (ec != null) {
324
- conns.add (ec);
325
- }
326
- }
327
- Connections.clear();
328
-
329
- ListIterator<EventableChannel> i3 = conns.listIterator(0);
330
- while (i3.hasNext()) {
331
- EventableChannel ec = i3.next();
332
- eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, null);
333
- ec.close();
334
-
335
- EventableSocketChannel sc = (EventableSocketChannel) ec;
336
- if (sc != null && sc.isAttached())
337
- DetachedConnections.add (sc);
338
- }
339
-
340
- ListIterator<EventableSocketChannel> i4 = DetachedConnections.listIterator(0);
341
- while (i4.hasNext()) {
342
- EventableSocketChannel ec = i4.next();
343
- ec.cleanup();
344
- }
345
- DetachedConnections.clear();
346
- }
347
-
348
- void runLoopbreaks() {
349
- if (loopBreaker.getAndSet(false)) {
350
- eventCallback (0, EM_LOOPBREAK_SIGNAL, null);
351
- }
352
- }
353
-
354
- public void stop() {
355
- bRunReactor = false;
356
- signalLoopbreak();
357
- }
358
-
359
- void runTimers() {
360
- long now = new Date().getTime();
361
- while (!Timers.isEmpty()) {
362
- long k = Timers.firstKey();
363
- if (k > now)
364
- break;
365
-
366
- ArrayList<Long> callbacks = Timers.get(k);
367
- Timers.remove(k);
368
-
369
- // Fire all timers at this timestamp
370
- ListIterator<Long> iter = callbacks.listIterator(0);
371
- try {
372
- while (iter.hasNext()) {
373
- eventCallback (0, EM_TIMER_FIRED, null, iter.next().longValue());
374
- }
375
- } catch (UndeclaredThrowableException e) {
376
- e.printStackTrace();
377
- }
378
- }
379
- }
380
-
381
- public long installOneshotTimer (int milliseconds) {
382
- long s = createBinding();
383
- long deadline = new Date().getTime() + milliseconds;
384
-
385
- if (Timers.containsKey(deadline)) {
386
- Timers.get(deadline).add(s);
387
- } else {
388
- ArrayList<Long> callbacks = new ArrayList<Long>();
389
- callbacks.add(s);
390
- Timers.put(deadline, callbacks);
391
- }
392
-
393
- return s;
394
- }
395
-
396
- public long startTcpServer (SocketAddress sa) throws EmReactorException {
397
- try {
398
- ServerSocketChannel server = ServerSocketChannel.open();
399
- server.configureBlocking(false);
400
- server.socket().bind (sa);
401
- long s = createBinding();
402
- Acceptors.put(s, server);
403
- server.register(mySelector, SelectionKey.OP_ACCEPT, s);
404
- return s;
405
- } catch (IOException e) {
406
- throw new EmReactorException ("unable to open socket acceptor: " + e.toString());
407
- }
408
- }
409
-
410
- public long startTcpServer (String address, int port) throws EmReactorException {
411
- return startTcpServer (new InetSocketAddress (address, port));
412
- }
413
-
414
- public void stopTcpServer (long signature) throws IOException {
415
- ServerSocketChannel server = Acceptors.remove(signature);
416
- if (server != null)
417
- server.close();
418
- else
419
- throw new RuntimeException ("failed to close unknown acceptor");
420
- }
421
-
422
- public long openUdpSocket (InetSocketAddress address) throws IOException {
423
- // TODO, don't throw an exception out of here.
424
- DatagramChannel dg = DatagramChannel.open();
425
- dg.configureBlocking(false);
426
- dg.socket().bind(address);
427
- long b = createBinding();
428
- EventableChannel ec = new EventableDatagramChannel (dg, b, mySelector);
429
- dg.register(mySelector, SelectionKey.OP_READ, ec);
430
- Connections.put(b, ec);
431
- return b;
432
- }
433
-
434
- public long openUdpSocket (String address, int port) throws IOException {
435
- return openUdpSocket (new InetSocketAddress (address, port));
436
- }
437
-
438
- public void sendData (long sig, ByteBuffer bb) throws IOException {
439
- EventableChannel ec = Connections.get(sig);
440
- if (ec == null) {
441
- return;
442
- }
443
- ec.scheduleOutboundData( bb );
444
- // HACK: In Ubuntu(java version 1.6.0_33), it is blocked by mSelector.select() in checkIO() and writeOutboundData() is not called.
445
- if (!ec.isWatchOnly()) {
446
- ec.writeOutboundData();
447
- }
448
- }
449
-
450
- public void sendData (long sig, byte[] data) throws IOException {
451
- sendData (sig, ByteBuffer.wrap(data));
452
- }
453
-
454
- public void setCommInactivityTimeout (long sig, long mills) {
455
- Connections.get(sig).setCommInactivityTimeout (mills);
456
- }
457
-
458
- public void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort) {
459
- sendDatagram (sig, ByteBuffer.wrap(data), recipAddress, recipPort);
460
- }
461
-
462
- public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) {
463
- (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
464
- }
465
-
466
- public long connectTcpServer (String address, int port) {
467
- return connectTcpServer(null, 0, address, port);
468
- }
469
-
470
- public long connectTcpServer (String bindAddr, int bindPort, String address, int port) {
471
- long b = createBinding();
472
-
473
- try {
474
- SocketChannel sc = SocketChannel.open();
475
- sc.configureBlocking(false);
476
- if (bindAddr != null)
477
- sc.socket().bind(new InetSocketAddress (bindAddr, bindPort));
478
-
479
- EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
480
-
481
- if (sc.connect (new InetSocketAddress (address, port))) {
482
- // Connection returned immediately. Can happen with localhost connections.
483
- // WARNING, this code is untested due to lack of available test conditions.
484
- // Ought to be be able to come here from a localhost connection, but that
485
- // doesn't happen on Linux. (Maybe on FreeBSD?)
486
- // The reason for not handling this until we can test it is that we
487
- // really need to return from this function WITHOUT triggering any EM events.
488
- // That's because until the user code has seen the signature we generated here,
489
- // it won't be able to properly dispatch them. The C++ EM deals with this
490
- // by setting pending mode as a flag in ALL eventable descriptors and making
491
- // the descriptor select for writable. Then, it can send UNBOUND and
492
- // CONNECTION_COMPLETED on the next pass through the loop, because writable will
493
- // fire.
494
- throw new RuntimeException ("immediate-connect unimplemented");
495
- }
496
- else {
497
- ec.setConnectPending();
498
- Connections.put (b, ec);
499
- NewConnections.add (b);
500
- }
501
- } catch (IOException e) {
502
- // Can theoretically come here if a connect failure can be determined immediately.
503
- // I don't know how to make that happen for testing purposes.
504
- throw new RuntimeException ("immediate-connect unimplemented: " + e.toString());
505
- }
506
- return b;
507
- }
508
-
509
- public void closeConnection (long sig, boolean afterWriting) {
510
- EventableChannel ec = Connections.get(sig);
511
- if (ec != null)
512
- if (ec.scheduleClose (afterWriting))
513
- UnboundConnections.add (sig);
514
- }
515
-
516
- long createBinding() {
517
- return ++BindingIndex;
518
- }
519
-
520
- public void signalLoopbreak() {
521
- loopBreaker.set(true);
522
- if (mySelector != null)
523
- mySelector.wakeup();
524
- }
525
-
526
- public void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException {
527
- Connections.get(sig).startTls();
528
- }
529
-
530
- public void setTimerQuantum (int mills) {
531
- if (mills < 5 || mills > 2500)
532
- throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
533
- timerQuantum = mills;
534
- }
535
-
536
- public Object[] getPeerName (long sig) {
537
- return Connections.get(sig).getPeerName();
538
- }
539
-
540
- public Object[] getSockName (long sig) {
541
- return Connections.get(sig).getSockName();
542
- }
543
-
544
- public long attachChannel (SocketChannel sc, boolean watch_mode) {
545
- long b = createBinding();
546
-
547
- EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
548
-
549
- ec.setAttached();
550
- if (watch_mode)
551
- ec.setWatchOnly();
552
-
553
- Connections.put (b, ec);
554
- NewConnections.add (b);
555
-
556
- return b;
557
- }
558
-
559
- public SocketChannel detachChannel (long sig) {
560
- EventableSocketChannel ec = (EventableSocketChannel) Connections.get (sig);
561
- if (ec != null) {
562
- UnboundConnections.add (sig);
563
- return ec.getChannel();
564
- } else {
565
- return null;
566
- }
567
- }
568
-
569
- public void setNotifyReadable (long sig, boolean mode) {
570
- ((EventableSocketChannel) Connections.get(sig)).setNotifyReadable(mode);
571
- }
572
-
573
- public void setNotifyWritable (long sig, boolean mode) {
574
- ((EventableSocketChannel) Connections.get(sig)).setNotifyWritable(mode);
575
- }
576
-
577
- public boolean isNotifyReadable (long sig) {
578
- return Connections.get(sig).isNotifyReadable();
579
- }
580
-
581
- public boolean isNotifyWritable (long sig) {
582
- return Connections.get(sig).isNotifyWritable();
583
- }
584
-
585
- public int getConnectionCount() {
586
- return Connections.size() + Acceptors.size();
587
- }
52
+ private Selector mySelector;
53
+ private TreeMap<Long, ArrayList<Long>> Timers;
54
+ private HashMap<Long, EventableChannel<?>> Connections;
55
+ private HashMap<Long, ServerSocketChannel> Acceptors;
56
+ private ArrayList<Long> NewConnections;
57
+ private ArrayList<Long> UnboundConnections;
58
+ private ArrayList<EventableSocketChannel> DetachedConnections;
59
+
60
+ private boolean bRunReactor;
61
+ private long BindingIndex;
62
+ private AtomicBoolean loopBreaker;
63
+ private int timerQuantum;
64
+ private EventCallback callback;
65
+
66
+ public EmReactor(EventCallback callback) {
67
+ this.callback = callback;
68
+ Timers = new TreeMap<Long, ArrayList<Long>>();
69
+ Connections = new HashMap<Long, EventableChannel<?>>();
70
+ Acceptors = new HashMap<Long, ServerSocketChannel>();
71
+ NewConnections = new ArrayList<Long>();
72
+ UnboundConnections = new ArrayList<Long>();
73
+ DetachedConnections = new ArrayList<EventableSocketChannel>();
74
+
75
+ BindingIndex = 0;
76
+ loopBreaker = new AtomicBoolean();
77
+ loopBreaker.set(false);
78
+ timerQuantum = 98;
79
+ }
80
+
81
+ public void run() {
82
+ try {
83
+ mySelector = Selector.open();
84
+ bRunReactor = true;
85
+ } catch (IOException e) {
86
+ throw new RuntimeException ("Could not open selector", e);
87
+ }
88
+
89
+ while (bRunReactor) {
90
+ runLoopbreaks();
91
+ if (!bRunReactor) break;
92
+
93
+ runTimers();
94
+ if (!bRunReactor) break;
95
+
96
+ removeUnboundConnections();
97
+ checkIO();
98
+ addNewConnections();
99
+ processIO();
100
+ }
101
+
102
+ close();
103
+ }
104
+
105
+ void addNewConnections() {
106
+ for (EventableSocketChannel ec : DetachedConnections) {
107
+ ec.cleanup();
108
+ }
109
+ DetachedConnections.clear();
110
+
111
+ for (long b : NewConnections) {
112
+ EventableChannel<?> ec = Connections.get(b);
113
+ if (ec != null) {
114
+ try {
115
+ ec.register();
116
+ } catch (ClosedChannelException e) {
117
+ UnboundConnections.add (ec.getBinding());
118
+ }
119
+ }
120
+ }
121
+ NewConnections.clear();
122
+ }
123
+
124
+ void removeUnboundConnections() {
125
+ for (long b : UnboundConnections) {
126
+ EventableChannel<?> ec = Connections.remove(b);
127
+ if (ec != null) {
128
+ callback.trigger(b, EventCode.EM_CONNECTION_UNBOUND, null, (long) 0);
129
+ ec.close();
130
+
131
+ EventableSocketChannel sc = (EventableSocketChannel) ec;
132
+ if (sc != null && sc.isAttached())
133
+ DetachedConnections.add (sc);
134
+ }
135
+ }
136
+ UnboundConnections.clear();
137
+ }
138
+
139
+ void checkIO() {
140
+ long timeout;
141
+
142
+ if (NewConnections.size() > 0) {
143
+ timeout = -1;
144
+ } else if (!Timers.isEmpty()) {
145
+ long now = new Date().getTime();
146
+ long k = Timers.firstKey();
147
+ long diff = k-now;
148
+
149
+ if (diff <= 0)
150
+ timeout = -1; // don't wait, just poll once
151
+ else
152
+ timeout = diff;
153
+ } else {
154
+ timeout = 0; // wait indefinitely
155
+ }
156
+
157
+ try {
158
+ if (timeout == -1)
159
+ mySelector.selectNow();
160
+ else
161
+ mySelector.select(timeout);
162
+ } catch (IOException e) {
163
+ e.printStackTrace();
164
+ }
165
+ }
166
+
167
+ void processIO() {
168
+ Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
169
+ while (it.hasNext()) {
170
+ SelectionKey k = it.next();
171
+ it.remove();
172
+ if (k.isConnectable()) {
173
+ isConnectable(k);
174
+ }
175
+ else if (k.isAcceptable()) {
176
+ isAcceptable(k);
177
+ }
178
+ else {
179
+ if (k.isWritable())
180
+ isWritable(k);
181
+
182
+ if (k.isReadable())
183
+ isReadable(k);
184
+ }
185
+ }
186
+ }
187
+
188
+ void isAcceptable (SelectionKey k) {
189
+ ServerSocketChannel ss = (ServerSocketChannel) k.channel();
190
+ SocketChannel sn;
191
+ long b;
192
+
193
+ for (int n = 0; n < 10; n++) {
194
+ try {
195
+ sn = ss.accept();
196
+ if (sn == null)
197
+ break;
198
+ } catch (IOException e) {
199
+ e.printStackTrace();
200
+ k.cancel();
201
+
202
+ ServerSocketChannel server = Acceptors.remove(k.attachment());
203
+ if (server != null)
204
+ try{ server.close(); } catch (IOException ex) {};
205
+ break;
206
+ }
207
+
208
+ try {
209
+ sn.configureBlocking(false);
210
+ } catch (IOException e) {
211
+ e.printStackTrace();
212
+ continue;
213
+ }
214
+
215
+ b = createBinding();
216
+ EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector, callback);
217
+ ec.setServerMode();
218
+ Connections.put (b, ec);
219
+ NewConnections.add (b);
220
+
221
+ callback.trigger(((Long)k.attachment()).longValue(), EventCode.EM_CONNECTION_ACCEPTED, null, b);
222
+ }
223
+ }
224
+
225
+ void isReadable (SelectionKey k) {
226
+ EventableChannel<?> ec = (EventableChannel<?>) k.attachment();
227
+ if (!ec.read()) {
228
+ UnboundConnections.add (ec.getBinding());
229
+ }
230
+ }
231
+
232
+ void isWritable (SelectionKey k) {
233
+ EventableChannel<?> ec = (EventableChannel<?>) k.attachment();
234
+ if (!ec.write()) {
235
+ UnboundConnections.add (ec.getBinding());
236
+ }
237
+ }
238
+
239
+ void isConnectable (SelectionKey k) {
240
+ EventableSocketChannel ec = (EventableSocketChannel) k.attachment();
241
+ if (!ec.finishConnecting()) {
242
+ UnboundConnections.add (ec.getBinding());
243
+ }
244
+ }
245
+
246
+ void close() {
247
+ try {
248
+ if (mySelector != null)
249
+ mySelector.close();
250
+ } catch (IOException e) {}
251
+ mySelector = null;
252
+
253
+ // run down open connections and sockets.
254
+ for (ServerSocketChannel c : Acceptors.values()) {
255
+ try {
256
+ c.close();
257
+ } catch (IOException e) {}
258
+ }
259
+
260
+ // 29Sep09: We create an ArrayList of the existing connections, then iterate over
261
+ // that to call unbind on them. This is because an unbind can trigger a reconnect,
262
+ // which will add to the Connections HashMap, causing a ConcurrentModificationException.
263
+ // XXX: The correct behavior here would be to latch the various reactor methods to return
264
+ // immediately if the reactor is shutting down.
265
+ ArrayList<EventableChannel<?>> conns = new ArrayList<EventableChannel<?>>();
266
+ for (EventableChannel<?> ec : Connections.values()) {
267
+ if (ec != null) {
268
+ conns.add (ec);
269
+ }
270
+ }
271
+ Connections.clear();
272
+
273
+ for (EventableChannel<?> ec : conns) {
274
+ callback.trigger(ec.getBinding(), EventCode.EM_CONNECTION_UNBOUND, null, (long) 0);
275
+ ec.close();
276
+
277
+ EventableSocketChannel sc = (EventableSocketChannel) ec;
278
+ if (sc != null && sc.isAttached())
279
+ DetachedConnections.add (sc);
280
+ }
281
+
282
+ for (EventableSocketChannel ec : DetachedConnections) {
283
+ ec.cleanup();
284
+ }
285
+ DetachedConnections.clear();
286
+ }
287
+
288
+ void runLoopbreaks() {
289
+ if (loopBreaker.getAndSet(false)) {
290
+ callback.trigger((long) 0, EventCode.EM_LOOPBREAK_SIGNAL, null, (long) 0);
291
+ }
292
+ }
293
+
294
+ public void stop() {
295
+ bRunReactor = false;
296
+ signalLoopbreak();
297
+ }
298
+
299
+ void runTimers() {
300
+ long now = new Date().getTime();
301
+ while (!Timers.isEmpty()) {
302
+ long k = Timers.firstKey();
303
+ if (k > now)
304
+ break;
305
+
306
+ ArrayList<Long> callbacks = Timers.get(k);
307
+ Timers.remove(k);
308
+
309
+ // Fire all timers at this timestamp
310
+ for (long timerCallback : callbacks) {
311
+ callback.trigger((long) 0, EventCode.EM_TIMER_FIRED, null, timerCallback);
312
+ }
313
+ }
314
+ }
315
+
316
+ public long installOneshotTimer (int milliseconds) {
317
+ long s = createBinding();
318
+ long deadline = new Date().getTime() + milliseconds;
319
+
320
+ if (Timers.containsKey(deadline)) {
321
+ Timers.get(deadline).add(s);
322
+ } else {
323
+ ArrayList<Long> callbacks = new ArrayList<Long>();
324
+ callbacks.add(s);
325
+ Timers.put(deadline, callbacks);
326
+ }
327
+
328
+ return s;
329
+ }
330
+
331
+ public long startTcpServer (SocketAddress sa) throws EmReactorException {
332
+ try {
333
+ ServerSocketChannel server = ServerSocketChannel.open();
334
+ server.configureBlocking(false);
335
+ server.socket().bind (sa);
336
+ long s = createBinding();
337
+ Acceptors.put(s, server);
338
+ server.register(mySelector, SelectionKey.OP_ACCEPT, s);
339
+ return s;
340
+ } catch (IOException e) {
341
+ throw new EmReactorException ("unable to open socket acceptor: " + e.toString());
342
+ }
343
+ }
344
+
345
+ public long startTcpServer (String address, int port) throws EmReactorException {
346
+ return startTcpServer (new InetSocketAddress (address, port));
347
+ }
348
+
349
+ public void stopTcpServer (long signature) throws IOException {
350
+ ServerSocketChannel server = Acceptors.remove(signature);
351
+ if (server != null)
352
+ server.close();
353
+ else
354
+ throw new RuntimeException ("failed to close unknown acceptor");
355
+ }
356
+
357
+ public long openUdpSocket (InetSocketAddress address) throws IOException {
358
+ // TODO, don't throw an exception out of here.
359
+ DatagramChannel dg = DatagramChannel.open();
360
+ dg.configureBlocking(false);
361
+ dg.socket().bind(address);
362
+ long b = createBinding();
363
+ EventableChannel<?> ec = new EventableDatagramChannel (dg, b, mySelector, callback);
364
+ dg.register(mySelector, SelectionKey.OP_READ, ec);
365
+ Connections.put(b, ec);
366
+ return b;
367
+ }
368
+
369
+ public long openUdpSocket (String address, int port) throws IOException {
370
+ return openUdpSocket (new InetSocketAddress (address, port));
371
+ }
372
+
373
+ public void sendData (long sig, ByteBuffer bb) throws IOException {
374
+ Connections.get(sig).scheduleOutboundData( bb );
375
+ }
376
+
377
+ public void sendData (long sig, byte[] data) throws IOException {
378
+ sendData (sig, ByteBuffer.wrap(data));
379
+ }
380
+
381
+ public void setCommInactivityTimeout (long sig, long mills) {
382
+ Connections.get(sig).setCommInactivityTimeout (mills);
383
+ }
384
+
385
+ public void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort) {
386
+ sendDatagram (sig, ByteBuffer.wrap(data), recipAddress, recipPort);
387
+ }
388
+
389
+ public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) {
390
+ (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
391
+ }
392
+
393
+ public long connectTcpServer (String address, int port) {
394
+ return connectTcpServer(null, 0, address, port);
395
+ }
396
+
397
+ public long connectTcpServer (String bindAddr, int bindPort, String address, int port) {
398
+ long b = createBinding();
399
+
400
+ try {
401
+ SocketChannel sc = SocketChannel.open();
402
+ sc.configureBlocking(false);
403
+ if (bindAddr != null)
404
+ sc.socket().bind(new InetSocketAddress (bindAddr, bindPort));
405
+
406
+ EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector, callback);
407
+
408
+ if (sc.connect (new InetSocketAddress (address, port))) {
409
+ // Connection returned immediately. Can happen with localhost connections.
410
+ // WARNING, this code is untested due to lack of available test conditions.
411
+ // Ought to be be able to come here from a localhost connection, but that
412
+ // doesn't happen on Linux. (Maybe on FreeBSD?)
413
+ // The reason for not handling this until we can test it is that we
414
+ // really need to return from this function WITHOUT triggering any EM events.
415
+ // That's because until the user code has seen the signature we generated here,
416
+ // it won't be able to properly dispatch them. The C++ EM deals with this
417
+ // by setting pending mode as a flag in ALL eventable descriptors and making
418
+ // the descriptor select for writable. Then, it can send UNBOUND and
419
+ // CONNECTION_COMPLETED on the next pass through the loop, because writable will
420
+ // fire.
421
+ throw new RuntimeException ("immediate-connect unimplemented");
422
+ }
423
+ else {
424
+ ec.setConnectPending();
425
+ Connections.put (b, ec);
426
+ NewConnections.add (b);
427
+ }
428
+ } catch (IOException e) {
429
+ // Can theoretically come here if a connect failure can be determined immediately.
430
+ // I don't know how to make that happen for testing purposes.
431
+ throw new RuntimeException ("immediate-connect unimplemented: " + e.toString());
432
+ }
433
+ return b;
434
+ }
435
+
436
+ public void closeConnection (long sig, boolean afterWriting) {
437
+ EventableChannel<?> ec = Connections.get(sig);
438
+ if (ec != null)
439
+ if (ec.scheduleClose (afterWriting))
440
+ UnboundConnections.add (sig);
441
+ }
442
+
443
+ long createBinding() {
444
+ return ++BindingIndex;
445
+ }
446
+
447
+ public void signalLoopbreak() {
448
+ loopBreaker.set(true);
449
+ if (mySelector != null)
450
+ mySelector.wakeup();
451
+ }
452
+
453
+ public void setTlsParms(long sig, KeyStore keyStore, boolean verifyPeer) {
454
+ ((EventableSocketChannel) Connections.get(sig)).setTlsParms(keyStore, verifyPeer);
455
+ }
456
+
457
+ public void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException {
458
+ Connections.get(sig).startTls();
459
+ }
460
+
461
+ public void acceptSslPeer (long sig) {
462
+ EventableSocketChannel sc = (EventableSocketChannel) Connections.get(sig);
463
+ sc.acceptSslPeer();
464
+ }
465
+
466
+ public byte[] getPeerCert (long sig) {
467
+ EventableSocketChannel sc = (EventableSocketChannel) Connections.get(sig);
468
+ return sc.getPeerCert();
469
+ }
470
+
471
+ public void setTimerQuantum (int mills) {
472
+ if (mills < 5 || mills > 2500)
473
+ throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
474
+ timerQuantum = mills;
475
+ }
476
+
477
+ public Object[] getPeerName (long sig) {
478
+ return Connections.get(sig).getPeerName();
479
+ }
480
+
481
+ public Object[] getSockName (long sig) {
482
+ return Connections.get(sig).getSockName();
483
+ }
484
+
485
+ public long attachChannel (SocketChannel sc, boolean watch_mode) {
486
+ long b = createBinding();
487
+
488
+ EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector, callback);
489
+
490
+ ec.setAttached();
491
+ if (watch_mode)
492
+ ec.setWatchOnly();
493
+
494
+ Connections.put (b, ec);
495
+ NewConnections.add (b);
496
+
497
+ return b;
498
+ }
499
+
500
+ public SocketChannel detachChannel (long sig) {
501
+ EventableSocketChannel ec = (EventableSocketChannel) Connections.get (sig);
502
+ if (ec != null) {
503
+ UnboundConnections.add (sig);
504
+ return ec.getChannel();
505
+ } else {
506
+ return null;
507
+ }
508
+ }
509
+
510
+ public void setNotifyReadable (long sig, boolean mode) {
511
+ ((EventableSocketChannel) Connections.get(sig)).setNotifyReadable(mode);
512
+ }
513
+
514
+ public void setNotifyWritable (long sig, boolean mode) {
515
+ ((EventableSocketChannel) Connections.get(sig)).setNotifyWritable(mode);
516
+ }
517
+
518
+ public boolean isNotifyReadable (long sig) {
519
+ return Connections.get(sig).isNotifyReadable();
520
+ }
521
+
522
+ public boolean isNotifyWritable (long sig) {
523
+ return Connections.get(sig).isNotifyWritable();
524
+ }
525
+
526
+ public int getConnectionCount() {
527
+ return Connections.size() + Acceptors.size();
528
+ }
588
529
  }