eventmachine-eventmachine 0.12.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/Rakefile +169 -0
  2. data/docs/COPYING +60 -0
  3. data/docs/ChangeLog +183 -0
  4. data/docs/DEFERRABLES +138 -0
  5. data/docs/EPOLL +141 -0
  6. data/docs/GNU +281 -0
  7. data/docs/INSTALL +15 -0
  8. data/docs/KEYBOARD +38 -0
  9. data/docs/LEGAL +25 -0
  10. data/docs/LIGHTWEIGHT_CONCURRENCY +72 -0
  11. data/docs/PURE_RUBY +77 -0
  12. data/docs/README +74 -0
  13. data/docs/RELEASE_NOTES +96 -0
  14. data/docs/SMTP +9 -0
  15. data/docs/SPAWNED_PROCESSES +93 -0
  16. data/docs/TODO +10 -0
  17. data/ext/binder.cpp +126 -0
  18. data/ext/binder.h +48 -0
  19. data/ext/cmain.cpp +530 -0
  20. data/ext/cplusplus.cpp +172 -0
  21. data/ext/ed.cpp +1473 -0
  22. data/ext/ed.h +361 -0
  23. data/ext/em.cpp +1895 -0
  24. data/ext/em.h +170 -0
  25. data/ext/emwin.cpp +300 -0
  26. data/ext/emwin.h +94 -0
  27. data/ext/epoll.cpp +26 -0
  28. data/ext/epoll.h +25 -0
  29. data/ext/eventmachine.h +90 -0
  30. data/ext/eventmachine_cpp.h +94 -0
  31. data/ext/extconf.rb +150 -0
  32. data/ext/files.cpp +94 -0
  33. data/ext/files.h +65 -0
  34. data/ext/kb.cpp +368 -0
  35. data/ext/page.cpp +107 -0
  36. data/ext/page.h +51 -0
  37. data/ext/pipe.cpp +327 -0
  38. data/ext/project.h +119 -0
  39. data/ext/rubymain.cpp +683 -0
  40. data/ext/sigs.cpp +89 -0
  41. data/ext/sigs.h +32 -0
  42. data/ext/ssl.cpp +408 -0
  43. data/ext/ssl.h +86 -0
  44. data/java/src/com/rubyeventmachine/Application.java +196 -0
  45. data/java/src/com/rubyeventmachine/Connection.java +74 -0
  46. data/java/src/com/rubyeventmachine/ConnectionFactory.java +37 -0
  47. data/java/src/com/rubyeventmachine/DefaultConnectionFactory.java +46 -0
  48. data/java/src/com/rubyeventmachine/EmReactor.java +408 -0
  49. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  50. data/java/src/com/rubyeventmachine/EventableChannel.java +57 -0
  51. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +171 -0
  52. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +244 -0
  53. data/java/src/com/rubyeventmachine/PeriodicTimer.java +38 -0
  54. data/java/src/com/rubyeventmachine/Timer.java +54 -0
  55. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +108 -0
  56. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +124 -0
  57. data/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
  58. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
  59. data/java/src/com/rubyeventmachine/tests/TestServers.java +74 -0
  60. data/java/src/com/rubyeventmachine/tests/TestTimers.java +89 -0
  61. data/lib/em/deferrable.rb +208 -0
  62. data/lib/em/eventable.rb +39 -0
  63. data/lib/em/future.rb +62 -0
  64. data/lib/em/messages.rb +66 -0
  65. data/lib/em/processes.rb +68 -0
  66. data/lib/em/spawnable.rb +88 -0
  67. data/lib/em/streamer.rb +112 -0
  68. data/lib/eventmachine.rb +1763 -0
  69. data/lib/eventmachine_version.rb +31 -0
  70. data/lib/evma.rb +32 -0
  71. data/lib/evma/callback.rb +32 -0
  72. data/lib/evma/container.rb +75 -0
  73. data/lib/evma/factory.rb +77 -0
  74. data/lib/evma/protocol.rb +87 -0
  75. data/lib/evma/reactor.rb +48 -0
  76. data/lib/jeventmachine.rb +137 -0
  77. data/lib/pr_eventmachine.rb +1011 -0
  78. data/lib/protocols/buftok.rb +127 -0
  79. data/lib/protocols/header_and_content.rb +129 -0
  80. data/lib/protocols/httpcli2.rb +794 -0
  81. data/lib/protocols/httpclient.rb +270 -0
  82. data/lib/protocols/line_and_text.rb +122 -0
  83. data/lib/protocols/linetext2.rb +163 -0
  84. data/lib/protocols/postgres.rb +261 -0
  85. data/lib/protocols/saslauth.rb +179 -0
  86. data/lib/protocols/smtpclient.rb +308 -0
  87. data/lib/protocols/smtpserver.rb +556 -0
  88. data/lib/protocols/stomp.rb +130 -0
  89. data/lib/protocols/tcptest.rb +57 -0
  90. data/tasks/cpp.rake +77 -0
  91. data/tasks/project.rake +78 -0
  92. data/tasks/tests.rake +192 -0
  93. data/tests/test_attach.rb +66 -0
  94. data/tests/test_basic.rb +231 -0
  95. data/tests/test_defer.rb +47 -0
  96. data/tests/test_epoll.rb +161 -0
  97. data/tests/test_errors.rb +82 -0
  98. data/tests/test_eventables.rb +78 -0
  99. data/tests/test_exc.rb +58 -0
  100. data/tests/test_futures.rb +214 -0
  101. data/tests/test_hc.rb +218 -0
  102. data/tests/test_httpclient.rb +215 -0
  103. data/tests/test_httpclient2.rb +133 -0
  104. data/tests/test_kb.rb +61 -0
  105. data/tests/test_ltp.rb +192 -0
  106. data/tests/test_ltp2.rb +320 -0
  107. data/tests/test_next_tick.rb +102 -0
  108. data/tests/test_processes.rb +56 -0
  109. data/tests/test_pure.rb +129 -0
  110. data/tests/test_running.rb +47 -0
  111. data/tests/test_sasl.rb +74 -0
  112. data/tests/test_send_file.rb +245 -0
  113. data/tests/test_servers.rb +80 -0
  114. data/tests/test_smtpclient.rb +81 -0
  115. data/tests/test_smtpserver.rb +93 -0
  116. data/tests/test_spawn.rb +329 -0
  117. data/tests/test_ssl_args.rb +68 -0
  118. data/tests/test_timers.rb +146 -0
  119. data/tests/test_ud.rb +43 -0
  120. data/tests/testem.rb +31 -0
  121. metadata +197 -0
data/ext/ssl.h ADDED
@@ -0,0 +1,86 @@
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: ssl.h
6
+ Date: 30Apr06
7
+
8
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
+ Gmail: blackhedd
10
+
11
+ This program is free software; you can redistribute it and/or modify
12
+ it under the terms of either: 1) the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2 of the
14
+ License, or (at your option) any later version; or 2) Ruby's License.
15
+
16
+ See the file COPYING for complete licensing information.
17
+
18
+ *****************************************************************************/
19
+
20
+
21
+ #ifndef __SslBox__H_
22
+ #define __SslBox__H_
23
+
24
+
25
+
26
+
27
+ #ifdef WITH_SSL
28
+
29
+ /******************
30
+ class SslContext_t
31
+ ******************/
32
+
33
+ class SslContext_t
34
+ {
35
+ public:
36
+ SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile);
37
+ virtual ~SslContext_t();
38
+
39
+ private:
40
+ static bool bLibraryInitialized;
41
+
42
+ private:
43
+ bool bIsServer;
44
+ SSL_CTX *pCtx;
45
+
46
+ EVP_PKEY *PrivateKey;
47
+ X509 *Certificate;
48
+
49
+ friend class SslBox_t;
50
+ };
51
+
52
+
53
+ /**************
54
+ class SslBox_t
55
+ **************/
56
+
57
+ class SslBox_t
58
+ {
59
+ public:
60
+ SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile);
61
+ virtual ~SslBox_t();
62
+
63
+ int PutPlaintext (const char*, int);
64
+ int GetPlaintext (char*, int);
65
+
66
+ bool PutCiphertext (const char*, int);
67
+ bool CanGetCiphertext();
68
+ int GetCiphertext (char*, int);
69
+
70
+ void Shutdown();
71
+
72
+ protected:
73
+ SslContext_t *Context;
74
+
75
+ bool bIsServer;
76
+ SSL *pSSL;
77
+ BIO *pbioRead;
78
+ BIO *pbioWrite;
79
+
80
+ PageList OutboundQ;
81
+ };
82
+ #endif // WITH_SSL
83
+
84
+
85
+ #endif // __SslBox__H_
86
+
@@ -0,0 +1,196 @@
1
+ /**
2
+ * $Id$
3
+ *
4
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
5
+ * Homepage:: http://rubyeventmachine.com
6
+ * Date:: 15 Jul 2007
7
+ *
8
+ * See EventMachine and EventMachine::Connection for documentation and
9
+ * usage examples.
10
+ *
11
+ *
12
+ *----------------------------------------------------------------------------
13
+ *
14
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
15
+ * Gmail: blackhedd
16
+ *
17
+ * This program is free software; you can redistribute it and/or modify
18
+ * it under the terms of either: 1) the GNU General Public License
19
+ * as published by the Free Software Foundation; either version 2 of the
20
+ * License, or (at your option) any later version; or 2) Ruby's License.
21
+ *
22
+ * See the file COPYING for complete licensing information.
23
+ *
24
+ *---------------------------------------------------------------------------
25
+ *
26
+ *
27
+ */
28
+
29
+ /**
30
+ *
31
+ */
32
+ package com.rubyeventmachine;
33
+
34
+ import java.nio.ByteBuffer;
35
+ import java.nio.channels.*;
36
+ import java.util.*;
37
+ import java.io.*;
38
+ import java.net.*;
39
+ import java.net.SocketAddress;
40
+
41
+ /**
42
+ * @author francis
43
+ *
44
+ */
45
+ public class Application {
46
+
47
+
48
+ public class Reactor extends EmReactor {
49
+
50
+ private Application application;
51
+ private TreeMap<String, Timer> timers;
52
+ private TreeMap<String, Connection> connections;
53
+ private TreeMap<String, ConnectionFactory> acceptors;
54
+ /**
55
+ *
56
+ */
57
+ public Reactor (Application app) {
58
+ application = app;
59
+ timers = new TreeMap<String, Timer>();
60
+ connections = new TreeMap<String, Connection>();
61
+ acceptors = new TreeMap<String, ConnectionFactory>();
62
+ }
63
+
64
+
65
+ public void eventCallback (String sig, int eventType, ByteBuffer data) {
66
+ if (eventType == EM_TIMER_FIRED) {
67
+ String timersig = new String (data.array());
68
+ //System.out.println ("EVSIG "+sig + "..."+new String(data.array()));
69
+ Timer r = timers.remove(timersig);
70
+ if (r != null)
71
+ r._fire();
72
+ else
73
+ throw new RuntimeException ("unable to run unknown timer");
74
+ }
75
+ else if (eventType == EM_CONNECTION_COMPLETED) {
76
+ Connection c = connections.get(sig);
77
+ if (c != null) {
78
+ c.connectionCompleted();
79
+ }
80
+ else
81
+ throw new RuntimeException ("connection completed to unknown object");
82
+
83
+ }
84
+ else if (eventType == EM_CONNECTION_UNBOUND) {
85
+ Connection c = connections.get(sig);
86
+ if (c != null) {
87
+ c.unbind();
88
+ }
89
+ else
90
+ throw new RuntimeException ("unbind received on unknown object");
91
+ }
92
+ else if (eventType == EM_CONNECTION_ACCEPTED) {
93
+ ConnectionFactory f = acceptors.get(sig);
94
+ if (f != null) {
95
+ Connection c = f.connection();
96
+ c.signature = new String (data.array());
97
+ c.application = application;
98
+ connections.put(c.signature, c);
99
+ c.postInit();
100
+ //System.out.println (sig+"..."+new String(data.array()));
101
+ }
102
+ else
103
+ throw new RuntimeException ("received connection on unknown acceptor");
104
+ }
105
+ else if (eventType == EM_CONNECTION_READ) {
106
+ Connection c = connections.get(sig);
107
+ if (c != null) {
108
+ c.receiveData(data);
109
+ }
110
+ else throw new RuntimeException ("received data on unknown object");
111
+ }
112
+ else {
113
+ System.out.println ("unknown event type: " + eventType);
114
+ }
115
+ }
116
+ }
117
+
118
+
119
+ Reactor reactor;
120
+
121
+ public Application() {
122
+ reactor = new Reactor (this);
123
+ }
124
+ public void addTimer (double seconds, Timer t) {
125
+ t.application = this;
126
+ t.interval = seconds;
127
+ String s = reactor.installOneshotTimer ((int)(seconds * 1000));
128
+ reactor.timers.put(s, t);
129
+
130
+ }
131
+
132
+ public void connect (String host, int port, Connection c) {
133
+ try {
134
+ String s = reactor.connectTcpServer(host, port);
135
+ c.application = this;
136
+ c.signature = s;
137
+ reactor.connections.put(s, c);
138
+ c.postInit();
139
+ } catch (ClosedChannelException e) {}
140
+ }
141
+
142
+ public void startServer (SocketAddress sa, ConnectionFactory f) throws EmReactorException {
143
+ String s = reactor.startTcpServer(sa);
144
+ reactor.acceptors.put(s, f);
145
+ }
146
+
147
+ public void stop() {
148
+ reactor.stop();
149
+ }
150
+ public void run() {
151
+ try {
152
+ reactor.run();
153
+ } catch (IOException e) {}
154
+ }
155
+ public void run (final Runnable r) {
156
+ addTimer(0, new Timer() {
157
+ public void fire() {
158
+ r.run();
159
+ }
160
+ });
161
+ run();
162
+ }
163
+
164
+ public void sendData (String sig, ByteBuffer bb) {
165
+ try {
166
+ reactor.sendData(sig, bb);
167
+ } catch (IOException e) {}
168
+ }
169
+
170
+ public void sendDatagram (String sig, ByteBuffer bb, InetSocketAddress target) {
171
+ reactor.sendDatagram(sig, bb, target.getHostName(), target.getPort());
172
+ }
173
+
174
+ public void closeConnection (String sig, boolean afterWriting) {
175
+ try {
176
+ reactor.closeConnection(sig, afterWriting);
177
+ } catch (ClosedChannelException e) {}
178
+ }
179
+
180
+ public void openDatagramSocket (Connection c) {
181
+ openDatagramSocket (new InetSocketAddress ("0.0.0.0", 0), c);
182
+ }
183
+ public void openDatagramSocket (InetSocketAddress addr, Connection c) {
184
+ try {
185
+ String s = reactor.openUdpSocket(addr);
186
+ c.application = this;
187
+ c.signature = s;
188
+ reactor.connections.put(s, c);
189
+ c.postInit();
190
+ } catch (ClosedChannelException e) {
191
+ } catch (IOException e) {
192
+ System.out.println ("Bad Datagram socket "+e+" "+addr);
193
+ /* TODO, can't catch this here, because it can happen on a bad address */
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * $Id$
3
+ *
4
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
5
+ * Homepage:: http://rubyeventmachine.com
6
+ * Date:: 15 Jul 2007
7
+ *
8
+ * See EventMachine and EventMachine::Connection for documentation and
9
+ * usage examples.
10
+ *
11
+ *
12
+ *----------------------------------------------------------------------------
13
+ *
14
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
15
+ * Gmail: blackhedd
16
+ *
17
+ * This program is free software; you can redistribute it and/or modify
18
+ * it under the terms of either: 1) the GNU General Public License
19
+ * as published by the Free Software Foundation; either version 2 of the
20
+ * License, or (at your option) any later version; or 2) Ruby's License.
21
+ *
22
+ * See the file COPYING for complete licensing information.
23
+ *
24
+ *---------------------------------------------------------------------------
25
+ *
26
+ *
27
+ */
28
+
29
+
30
+
31
+ package com.rubyeventmachine;
32
+
33
+ //import java.io.*;
34
+ import java.nio.*;
35
+ import java.net.*;
36
+ //import java.nio.channels.*;
37
+
38
+ public class Connection {
39
+
40
+ public Application application;
41
+ public String signature;
42
+
43
+ public void postInit() {}
44
+ public void connectionCompleted() {}
45
+ public void unbind() {}
46
+ public void receiveData (ByteBuffer bytebuffer) {}
47
+
48
+
49
+ /**
50
+ * Called by user code.
51
+ * @param bytebuffer
52
+ */
53
+ public void sendData (ByteBuffer b) {
54
+ application.sendData(signature, b);
55
+ }
56
+
57
+ /**
58
+ * This is called by user code.
59
+ * TODO: don't expose the exception here.
60
+ */
61
+ public void close() {
62
+ application.closeConnection(signature, false);
63
+ }
64
+ /**
65
+ * This is called by user code/
66
+ */
67
+ public void closeAfterWriting() {
68
+ application.closeConnection(signature, true);
69
+ }
70
+
71
+ public void sendDatagram (ByteBuffer bb, InetSocketAddress addr) {
72
+ application.sendDatagram (signature, bb, addr);
73
+ }
74
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * $Id$
3
+ *
4
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
5
+ * Homepage:: http://rubyeventmachine.com
6
+ * Date:: 15 Jul 2007
7
+ *
8
+ * See EventMachine and EventMachine::Connection for documentation and
9
+ * usage examples.
10
+ *
11
+ *
12
+ *----------------------------------------------------------------------------
13
+ *
14
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
15
+ * Gmail: blackhedd
16
+ *
17
+ * This program is free software; you can redistribute it and/or modify
18
+ * it under the terms of either: 1) the GNU General Public License
19
+ * as published by the Free Software Foundation; either version 2 of the
20
+ * License, or (at your option) any later version; or 2) Ruby's License.
21
+ *
22
+ * See the file COPYING for complete licensing information.
23
+ *
24
+ *---------------------------------------------------------------------------
25
+ *
26
+ *
27
+ */
28
+
29
+
30
+
31
+ package com.rubyeventmachine;
32
+
33
+ //import com.rubyeventmachine.*;
34
+
35
+ public interface ConnectionFactory {
36
+ public Connection connection();
37
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * $Id$
3
+ *
4
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
5
+ * Homepage:: http://rubyeventmachine.com
6
+ * Date:: 15 Jul 2007
7
+ *
8
+ * See EventMachine and EventMachine::Connection for documentation and
9
+ * usage examples.
10
+ *
11
+ *
12
+ *----------------------------------------------------------------------------
13
+ *
14
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
15
+ * Gmail: blackhedd
16
+ *
17
+ * This program is free software; you can redistribute it and/or modify
18
+ * it under the terms of either: 1) the GNU General Public License
19
+ * as published by the Free Software Foundation; either version 2 of the
20
+ * License, or (at your option) any later version; or 2) Ruby's License.
21
+ *
22
+ * See the file COPYING for complete licensing information.
23
+ *
24
+ *---------------------------------------------------------------------------
25
+ *
26
+ *
27
+ */
28
+
29
+
30
+
31
+ package com.rubyeventmachine;
32
+
33
+ import com.rubyeventmachine.ConnectionFactory;
34
+
35
+ public class DefaultConnectionFactory implements ConnectionFactory {
36
+
37
+ /**
38
+ * Convenience class. Its connection() method returns an instance of class
39
+ * Connection, which is usually overridden. This class is probably most
40
+ * useful for unit testing.
41
+ */
42
+ public Connection connection() {
43
+ return new Connection();
44
+ }
45
+
46
+ }
@@ -0,0 +1,408 @@
1
+ /**
2
+ * $Id$
3
+ *
4
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
5
+ * Homepage:: http://rubyeventmachine.com
6
+ * Date:: 15 Jul 2007
7
+ *
8
+ * See EventMachine and EventMachine::Connection for documentation and
9
+ * usage examples.
10
+ *
11
+ *
12
+ *----------------------------------------------------------------------------
13
+ *
14
+ * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
15
+ * Gmail: blackhedd
16
+ *
17
+ * This program is free software; you can redistribute it and/or modify
18
+ * it under the terms of either: 1) the GNU General Public License
19
+ * as published by the Free Software Foundation; either version 2 of the
20
+ * License, or (at your option) any later version; or 2) Ruby's License.
21
+ *
22
+ * See the file COPYING for complete licensing information.
23
+ *
24
+ *---------------------------------------------------------------------------
25
+ *
26
+ *
27
+ */
28
+
29
+ package com.rubyeventmachine;
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
+
39
+ public class EmReactor {
40
+
41
+
42
+ public final int EM_TIMER_FIRED = 100;
43
+ public final int EM_CONNECTION_READ = 101;
44
+ public final int EM_CONNECTION_UNBOUND = 102;
45
+ public final int EM_CONNECTION_ACCEPTED = 103;
46
+ public final int EM_CONNECTION_COMPLETED = 104;
47
+ public final int EM_LOOPBREAK_SIGNAL = 105;
48
+
49
+ private Selector mySelector;
50
+ private TreeMap<Long, String> Timers;
51
+ private TreeMap<String, EventableChannel> Connections;
52
+ private TreeMap<String, ServerSocketChannel> Acceptors;
53
+
54
+ private boolean bRunReactor;
55
+ private long BindingIndex;
56
+ private ByteBuffer EmptyByteBuffer;
57
+ private AtomicBoolean loopBreaker;
58
+ private ByteBuffer myReadBuffer;
59
+ private int timerQuantum;
60
+
61
+ public EmReactor() {
62
+ Timers = new TreeMap<Long, String>();
63
+ Connections = new TreeMap<String, EventableChannel>();
64
+ Acceptors = new TreeMap<String, ServerSocketChannel>();
65
+
66
+ BindingIndex = 100000;
67
+ EmptyByteBuffer = ByteBuffer.allocate(0);
68
+ loopBreaker = new AtomicBoolean();
69
+ loopBreaker.set(false);
70
+ myReadBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
71
+ timerQuantum = 98;
72
+ }
73
+
74
+ /**
75
+ * Intended to be overridden in languages (like Ruby) that can't handle ByteBuffer. This is a stub.
76
+ * Obsolete now that I figured out how to make Ruby deal with ByteBuffers.
77
+ * @param sig
78
+ * @param eventType
79
+ * @param data
80
+ */
81
+ /*
82
+ public void stringEventCallback (String sig, int eventType, String data) {
83
+ System.out.println ("Default event callback: " + sig + " " + eventType + " " + data);
84
+ }
85
+ */
86
+
87
+ /**
88
+ * This is a no-op stub, intended to be overridden in user code.
89
+ * @param sig
90
+ * @param eventType
91
+ * @param data
92
+ */
93
+ public void eventCallback (String sig, int eventType, ByteBuffer data) {
94
+ System.out.println ("Default callback: "+sig+" "+eventType+" "+data);
95
+ //stringEventCallback (sig, eventType, new String (data.array(), data.position(), data.limit()));
96
+
97
+ }
98
+
99
+ public void run() throws IOException {
100
+ mySelector = Selector.open();
101
+ bRunReactor = true;
102
+
103
+ //int n = 0;
104
+ for (;;) {
105
+ //System.out.println ("loop "+ (n++));
106
+ if (!bRunReactor) break;
107
+ runLoopbreaks();
108
+ if (!bRunReactor) break;
109
+ runTimers();
110
+ if (!bRunReactor) break;
111
+ mySelector.select(timerQuantum);
112
+
113
+ Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
114
+ while (it.hasNext()) {
115
+ SelectionKey k = it.next();
116
+ it.remove();
117
+
118
+ try {
119
+ if (k.isAcceptable()) {
120
+ ServerSocketChannel ss = (ServerSocketChannel) k.channel();
121
+ SocketChannel sn;
122
+ while ((sn = ss.accept()) != null) {
123
+ sn.configureBlocking(false);
124
+ String b = createBinding();
125
+ EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
126
+ Connections.put(b, ec);
127
+ eventCallback ((String)k.attachment(), EM_CONNECTION_ACCEPTED, ByteBuffer.wrap(b.getBytes()));
128
+ }
129
+ }
130
+
131
+ if (k.isReadable()) {
132
+ EventableChannel ec = (EventableChannel)k.attachment();
133
+ myReadBuffer.clear();
134
+ ec.readInboundData (myReadBuffer);
135
+ myReadBuffer.flip();
136
+ String b = ec.getBinding();
137
+ if (myReadBuffer.limit() > 0) {
138
+ eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
139
+ }
140
+ else {
141
+ eventCallback (b, EM_CONNECTION_UNBOUND, EmptyByteBuffer);
142
+ Connections.remove(b);
143
+ k.channel().close();
144
+ }
145
+ /*
146
+ System.out.println ("READABLE");
147
+ SocketChannel sn = (SocketChannel) k.channel();
148
+ //ByteBuffer bb = ByteBuffer.allocate(16 * 1024);
149
+ // Obviously not thread-safe, since we're using the same buffer for every connection.
150
+ // This should minimize the production of garbage, though.
151
+ // TODO, we need somehow to make a call to the EventableChannel, so we can pass the
152
+ // inbound data through an SSLEngine. Hope that won't break the strategy of using one
153
+ // global read-buffer.
154
+ myReadBuffer.clear();
155
+ int r = sn.read(myReadBuffer);
156
+ if (r > 0) {
157
+ myReadBuffer.flip();
158
+ //bb = ((EventableChannel)k.attachment()).dispatchInboundData (bb);
159
+ eventCallback (((EventableChannel)k.attachment()).getBinding(), EM_CONNECTION_READ, myReadBuffer);
160
+ }
161
+ else {
162
+ // TODO. Figure out if a socket that selects readable can ever return 0 bytes
163
+ // without it being indicative of an error condition. If Java is like C, the answer is no.
164
+ String b = ((EventableChannel)k.attachment()).getBinding();
165
+ eventCallback (b, EM_CONNECTION_UNBOUND, EmptyByteBuffer);
166
+ Connections.remove(b);
167
+ sn.close();
168
+ }
169
+ */
170
+ }
171
+
172
+
173
+ if (k.isWritable()) {
174
+ EventableChannel ec = (EventableChannel)k.attachment();
175
+ if (!ec.writeOutboundData()) {
176
+ eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, EmptyByteBuffer);
177
+ Connections.remove (ec.getBinding());
178
+ k.channel().close();
179
+ }
180
+ }
181
+
182
+ if (k.isConnectable()) {
183
+ EventableSocketChannel ec = (EventableSocketChannel)k.attachment();
184
+ if (ec.finishConnecting()) {
185
+ eventCallback (ec.getBinding(), EM_CONNECTION_COMPLETED, EmptyByteBuffer);
186
+ }
187
+ else {
188
+ Connections.remove (ec.getBinding());
189
+ k.channel().close();
190
+ eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, EmptyByteBuffer);
191
+ }
192
+ }
193
+ }
194
+ catch (CancelledKeyException e) {
195
+ // No-op. We can come here if a read-handler closes a socket before we fall through
196
+ // to call isWritable.
197
+ }
198
+
199
+ }
200
+ }
201
+
202
+ close();
203
+ }
204
+
205
+ void close() throws IOException {
206
+ mySelector.close();
207
+ mySelector = null;
208
+
209
+ // run down open connections and sockets.
210
+ Iterator<ServerSocketChannel> i = Acceptors.values().iterator();
211
+ while (i.hasNext()) {
212
+ i.next().close();
213
+ }
214
+
215
+ Iterator<EventableChannel> i2 = Connections.values().iterator();
216
+ while (i2.hasNext())
217
+ i2.next().close();
218
+ }
219
+
220
+ void runLoopbreaks() {
221
+ if (loopBreaker.getAndSet(false)) {
222
+ eventCallback ("", EM_LOOPBREAK_SIGNAL, EmptyByteBuffer);
223
+ }
224
+ }
225
+
226
+ public void stop() {
227
+ bRunReactor = false;
228
+ signalLoopbreak();
229
+ }
230
+
231
+ void runTimers() {
232
+ long now = new Date().getTime();
233
+ while (!Timers.isEmpty()) {
234
+ long k = Timers.firstKey();
235
+ //System.out.println (k - now);
236
+ if (k > now)
237
+ break;
238
+ String s = Timers.remove(k);
239
+ eventCallback ("", EM_TIMER_FIRED, ByteBuffer.wrap(s.getBytes()));
240
+ }
241
+ }
242
+
243
+ public String installOneshotTimer (int milliseconds) {
244
+ BindingIndex++;
245
+ String s = createBinding();
246
+ Timers.put(new Date().getTime() + milliseconds, s);
247
+ return s;
248
+ }
249
+
250
+ public String startTcpServer (SocketAddress sa) throws EmReactorException {
251
+ try {
252
+ ServerSocketChannel server = ServerSocketChannel.open();
253
+ server.configureBlocking(false);
254
+ server.socket().bind (sa);
255
+ String s = createBinding();
256
+ Acceptors.put(s, server);
257
+ server.register(mySelector, SelectionKey.OP_ACCEPT, s);
258
+ return s;
259
+ } catch (IOException e) {
260
+ // TODO, should parameterize this exception better.
261
+ throw new EmReactorException ("unable to open socket acceptor");
262
+ }
263
+ }
264
+
265
+ public String startTcpServer (String address, int port) throws EmReactorException {
266
+ return startTcpServer (new InetSocketAddress (address, port));
267
+ /*
268
+ ServerSocketChannel server = ServerSocketChannel.open();
269
+ server.configureBlocking(false);
270
+ server.socket().bind(new java.net.InetSocketAddress(address, port));
271
+ String s = createBinding();
272
+ Acceptors.put(s, server);
273
+ server.register(mySelector, SelectionKey.OP_ACCEPT, s);
274
+ return s;
275
+ */
276
+ }
277
+
278
+ public void stopTcpServer (String signature) throws IOException {
279
+ ServerSocketChannel server = Acceptors.remove(signature);
280
+ if (server != null)
281
+ server.close();
282
+ else
283
+ throw new RuntimeException ("failed to close unknown acceptor");
284
+ }
285
+
286
+
287
+ public String openUdpSocket (String address, int port) throws IOException {
288
+ return openUdpSocket (new InetSocketAddress (address, port));
289
+ }
290
+ /**
291
+ *
292
+ * @param address
293
+ * @param port
294
+ * @return
295
+ * @throws IOException
296
+ */
297
+ public String openUdpSocket (InetSocketAddress address) throws IOException {
298
+ // TODO, don't throw an exception out of here.
299
+ DatagramChannel dg = DatagramChannel.open();
300
+ dg.configureBlocking(false);
301
+ dg.socket().bind(address);
302
+ String b = createBinding();
303
+ EventableChannel ec = new EventableDatagramChannel (dg, b, mySelector);
304
+ dg.register(mySelector, SelectionKey.OP_READ, ec);
305
+ Connections.put(b, ec);
306
+ return b;
307
+ }
308
+
309
+ public void sendData (String sig, ByteBuffer bb) throws IOException {
310
+ (Connections.get(sig)).scheduleOutboundData( bb );
311
+ }
312
+ public void sendData (String sig, byte[] data) throws IOException {
313
+ sendData (sig, ByteBuffer.wrap(data));
314
+ //(Connections.get(sig)).scheduleOutboundData( ByteBuffer.wrap(data.getBytes()));
315
+ }
316
+ public void setCommInactivityTimeout (String sig, long mills) {
317
+ (Connections.get(sig)).setCommInactivityTimeout (mills);
318
+ }
319
+
320
+ /**
321
+ *
322
+ * @param sig
323
+ * @param data
324
+ * @param length
325
+ * @param recipAddress
326
+ * @param recipPort
327
+ */
328
+ public void sendDatagram (String sig, String data, int length, String recipAddress, int recipPort) {
329
+ sendDatagram (sig, ByteBuffer.wrap(data.getBytes()), recipAddress, recipPort);
330
+ }
331
+
332
+ /**
333
+ *
334
+ * @param sig
335
+ * @param bb
336
+ * @param recipAddress
337
+ * @param recipPort
338
+ */
339
+ public void sendDatagram (String sig, ByteBuffer bb, String recipAddress, int recipPort) {
340
+ (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
341
+ }
342
+
343
+
344
+ /**
345
+ *
346
+ * @param address
347
+ * @param port
348
+ * @return
349
+ * @throws ClosedChannelException
350
+ */
351
+ public String connectTcpServer (String address, int port) throws ClosedChannelException {
352
+ String b = createBinding();
353
+
354
+ try {
355
+ SocketChannel sc = SocketChannel.open();
356
+ sc.configureBlocking(false);
357
+ EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
358
+
359
+ if (sc.connect (new InetSocketAddress (address, port))) {
360
+ // Connection returned immediately. Can happen with localhost connections.
361
+ // WARNING, this code is untested due to lack of available test conditions.
362
+ // Ought to be be able to come here from a localhost connection, but that
363
+ // doesn't happen on Linux. (Maybe on FreeBSD?)
364
+ // The reason for not handling this until we can test it is that we
365
+ // really need to return from this function WITHOUT triggering any EM events.
366
+ // That's because until the user code has seen the signature we generated here,
367
+ // it won't be able to properly dispatch them. The C++ EM deals with this
368
+ // by setting pending mode as a flag in ALL eventable descriptors and making
369
+ // the descriptor select for writable. Then, it can send UNBOUND and
370
+ // CONNECTION_COMPLETED on the next pass through the loop, because writable will
371
+ // fire.
372
+ throw new RuntimeException ("immediate-connect unimplemented");
373
+ }
374
+ else {
375
+ Connections.put (b, ec);
376
+ ec.setConnectPending();
377
+ }
378
+ } catch (IOException e) {
379
+ // Can theoretically come here if a connect failure can be determined immediately.
380
+ // I don't know how to make that happen for testing purposes.
381
+ throw new RuntimeException ("immediate-connect unimplemented");
382
+ }
383
+ return b;
384
+ }
385
+
386
+ public void closeConnection (String sig, boolean afterWriting) throws ClosedChannelException {
387
+ Connections.get(sig).scheduleClose (afterWriting);
388
+ }
389
+
390
+ String createBinding() {
391
+ return new String ("BND_" + (++BindingIndex));
392
+ }
393
+
394
+ public void signalLoopbreak() {
395
+ loopBreaker.set(true);
396
+ mySelector.wakeup();
397
+ }
398
+
399
+ public void startTls (String sig) throws NoSuchAlgorithmException, KeyManagementException {
400
+ Connections.get(sig).startTls();
401
+ }
402
+
403
+ public void setTimerQuantum (int mills) {
404
+ if (mills < 5 || mills > 2500)
405
+ throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
406
+ timerQuantum = mills;
407
+ }
408
+ }