001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.fusesource.hawtdispatch.example.discovery;
018
019 import org.fusesource.hawtdispatch.*;
020
021 import static org.fusesource.hawtdispatch.Dispatch.*;
022
023 import java.io.ByteArrayOutputStream;
024 import java.io.EOFException;
025 import java.io.IOException;
026 import java.net.InetSocketAddress;
027 import java.net.URI;
028 import java.nio.ByteBuffer;
029 import java.nio.channels.*;
030 import java.util.ArrayList;
031 import java.util.concurrent.TimeUnit;
032
033 /**
034 * An example of a networks of servers which advertise connection information to each other.
035 */
036 public class EchoNetJava {
037 public static void main(String[] args) throws Exception {
038 run();
039 }
040
041 public static void run() throws Exception {
042 Server a = new Server(4444).start();
043 Server b = new Server(5555).start();
044 Server c = new Server(6666).start();
045
046 Thread.sleep(200);
047
048 a.connect(3333);
049 a.connect(b);
050 b.connect(c);
051 System.in.read();
052 }
053
054 static class Server {
055 final int port;
056 final URI me;
057 final ServerSocketChannel serverChannel;
058 final ArrayList<URI> seen = new ArrayList<URI>();
059 final DispatchQueue queue;
060 final DispatchSource accept_source;
061
062
063 public Server(int port) throws Exception {
064 this.port = port;
065 this.me = URI.create("conn://localhost:" + port);
066 this.serverChannel = ServerSocketChannel.open();
067 serverChannel.socket().bind(new InetSocketAddress(port));
068 serverChannel.configureBlocking(false);
069 queue = createQueue(me.toString());
070 accept_source = createSource(serverChannel, SelectionKey.OP_ACCEPT, queue);
071
072 accept_source.setEventHandler(new Runnable() {
073 public void run() {
074 // we are a server
075
076 // when you are a server, we must first listen for the
077 // address of the client before sending data.
078
079 // once they send us their address, we will send our
080 // full list of known addresses, followed by our own
081 // address to signal that we are done.
082
083 // Afterward we will only pulls our heartbeat
084 SocketChannel client = null;
085 try {
086 client = serverChannel.accept();
087
088 InetSocketAddress address = (InetSocketAddress) client.socket().getRemoteSocketAddress();
089 trace("accept " + address.getPort());
090 client.configureBlocking(false);
091
092 // Server sessions start by reading the client's greeting
093 Session session = new Session(Server.this, client, address);
094 session.start_read_greeting();
095
096 } catch (Exception e) {
097 try {
098 client.close();
099 } catch (IOException e1) {
100 }
101 }
102
103 }
104 });
105
106 accept_source.setCancelHandler(new Runnable() {
107 public void run() {
108 try {
109 serverChannel.close();
110 } catch (Throwable e) {
111 }
112 }
113 });
114 trace("Listening");
115 }
116
117 public Server start() {
118 accept_source.resume();
119 return this;
120 }
121
122 public void stop() {
123 accept_source.suspend();
124 }
125
126 public void close() {
127 accept_source.release();
128 queue.release();
129 }
130
131 public void connect(Server s) {
132 connect(s.port);
133 }
134
135 public void connect(int port) {
136 connect(URI.create("conn://localhost:" + port));
137 }
138
139 public void connect(final URI uri) {
140 queue.dispatchAsync(new Runnable() {
141 public void run() {
142 if (me.equals(uri) || seen.contains(uri))
143 return;
144
145 try {
146 int port = uri.getPort();
147 String host = uri.getHost();
148
149 trace("open " + uri);
150
151 final SocketChannel socketChannel = SocketChannel.open();
152 socketChannel.configureBlocking(false);
153
154 final InetSocketAddress address = new InetSocketAddress(host, port);
155
156 socketChannel.connect(address);
157
158 final DispatchSource connect_source = createSource(socketChannel, SelectionKey.OP_CONNECT, queue);
159 connect_source.setEventHandler(new Runnable() {
160 public void run() {
161 connect_source.release();
162 try {
163 socketChannel.finishConnect();
164 trace("connected " + uri);
165 Session session = new Session(Server.this, socketChannel, address, uri);
166 session.start_write_greeting();
167 } catch (IOException e) {
168 trace("connect to " + uri + " FAILED.");
169 }
170 }
171 });
172 connect_source.resume();
173 seen.add(uri);
174
175 } catch (IOException e) {
176 e.printStackTrace();
177 }
178 }
179 });
180 }
181
182 public void trace(String str) {
183 System.out.println(String.format("%5d - %s", port, str));
184 }
185
186 }
187
188 static class Session {
189
190 Server server;
191 SocketChannel channel;
192 InetSocketAddress address;
193 URI uri;
194
195 ByteBuffer read_buffer = ByteBuffer.allocate(1024);
196
197 DispatchQueue queue;
198 DispatchSource read_source;
199 DispatchSource write_source;
200 ArrayList<URI> seen;
201 ArrayList<URI> listed = new ArrayList<URI>();
202
203
204 public Session(Server server, SocketChannel channel, InetSocketAddress address, URI uri) {
205 this.server = server;
206 this.channel = channel;
207 this.address = address;
208 this.uri = uri;
209
210 this.queue = createQueue(uri.toString());
211 this.read_source = createSource(channel, SelectionKey.OP_READ, queue);
212 this.write_source = createSource(channel, SelectionKey.OP_WRITE, queue);
213 this.seen = new ArrayList<URI>(server.seen);
214
215 }
216
217 public Session(Server server, SocketChannel channel, InetSocketAddress address) {
218 this(server, channel, address, URI.create("conn://" + address.getHostName() + ":" + address.getPort()));
219 }
220
221
222 public void start_read_greeting() {
223 read_source.setEventHandler(read_greeting());
224 read_source.resume();
225 }
226
227
228 public Runnable read_greeting() {
229 return new Runnable() {
230 public void run() {
231 try {
232 String message = read_frame();
233 if (message != null) {
234 // stop looking for read events..
235 read_source.suspend();
236 URI uri = URI.create(message);
237 trace("welcome");
238
239 // Send them our seen uris..
240 ArrayList<Object> list = new ArrayList<Object>(seen);
241 list.remove(server.me);
242 list.remove(uri);
243 list.add("end");
244
245 start_write_data(new Runnable() {
246 public void run() {
247 start_read_hearbeat();
248 }
249 }, list.toArray(new Object[list.size()]));
250 }
251 } catch (IOException e) {
252 e.printStackTrace();
253 }
254 }
255 };
256 }
257
258 public void start_write_greeting() throws IOException {
259 trace("hello");
260 start_write_data(new Runnable() {
261 public void run() {
262 start_read_server_listings();
263 }
264 }, server.me);
265 }
266
267 public void start_read_server_listings() {
268 read_source.setEventHandler(read_server_listings());
269 read_source.resume();
270 }
271
272
273 public Runnable read_server_listings() {
274 return new Runnable() {
275 public void run() {
276 try {
277 String message = read_frame();
278 if (message != null) {
279 if (!message.equals("end")) {
280 URI uri = URI.create(message);
281 listed.add(uri);
282 server.connect(uri);
283 } else {
284 // Send them our seen uris..
285 ArrayList<Object> list = new ArrayList<Object>(seen);
286 list.removeAll(listed);
287 list.remove(server.me);
288 list.add("end");
289 start_write_data(new Runnable(){
290 public void run() {
291 start_write_hearbeat();
292 }
293 }, list.toArray(new Object[list.size()]));
294 }
295 }
296 } catch (IOException e) {
297 e.printStackTrace();
298 }
299 }
300 };
301 }
302
303 public void start_read_client_listings() {
304 read_source.setEventHandler(read_clientlistings());
305 read_source.resume();
306 }
307
308 public Runnable read_clientlistings() {
309 return new Runnable() {
310 public void run() {
311 try {
312 String message = read_frame();
313 if (message != null) {
314 if (!message.equals("end")) {
315 server.connect(URI.create(message));
316 } else {
317 start_read_hearbeat();
318 }
319 }
320 } catch (IOException e) {
321 e.printStackTrace();
322 }
323 }
324 };
325 }
326
327 public void start_write_hearbeat() {
328 queue.dispatchAfter(1, TimeUnit.SECONDS, new Runnable() {
329 public void run() {
330 try {
331 trace("ping");
332 start_write_data(new Runnable() {
333 public void run() {
334 start_write_hearbeat();
335 }
336 }, "ping");
337 } catch (IOException e) {
338 e.printStackTrace();
339 }
340 }
341 });
342 }
343
344
345 public void start_read_hearbeat() {
346 read_source.setEventHandler(read_hearbeat());
347 read_source.resume();
348 }
349
350 public Runnable read_hearbeat() {
351 return new Runnable() {
352 public void run() {
353 try {
354 String message = read_frame();
355 if (message != null) {
356 trace("pong");
357 }
358 } catch (IOException e) {
359 e.printStackTrace();
360 }
361 }
362 };
363 }
364
365 public void start_write_data(Runnable onDone, Object... list) throws IOException {
366 ByteArrayOutputStream baos = new ByteArrayOutputStream();
367 for (Object next : list) {
368 baos.write(next.toString().getBytes("UTF-8"));
369 baos.write(0);
370 }
371 ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
372 write_source.setEventHandler(write_data(buffer, onDone));
373 write_source.resume();
374 }
375
376 public Runnable write_data(final ByteBuffer buffer, final Runnable onDone) {
377 return new Runnable() {
378 public void run() {
379 try {
380 channel.write(buffer);
381 if (buffer.remaining() == 0) {
382 write_source.suspend();
383 onDone.run();
384 }
385 } catch (IOException e) {
386 e.printStackTrace();
387 }
388 }
389 };
390 }
391
392 public String read_frame() throws IOException {
393 if (channel.read(read_buffer) == -1) {
394 throw new EOFException();
395 }
396 byte[] buf = read_buffer.array();
397 int endPos = eof(buf, 0, read_buffer.position());
398 if (endPos < 0) {
399 trace(" --- ");
400 return null;
401 }
402 String rc = new String(buf, 0, endPos);
403 int newPos = read_buffer.position() - endPos;
404 System.arraycopy(buf, endPos + 1, buf, 0, newPos);
405 read_buffer.position(newPos);
406 return rc;
407 }
408
409 public int eof(byte[] data, int offset, int pos) {
410 int i = offset;
411 while (i < pos) {
412 if (data[i] == 0) {
413 return i;
414 }
415 i++;
416 }
417 return -1;
418 }
419
420 public void trace(String str) {
421 System.out.println(String.format("%5d %5d - %s", server.port, uri.getPort(), str));
422 }
423
424
425 }
426
427 }