org.fusesource.hawtdispatch.example.discovery.EchoNetScala.scala Maven / Gradle / Ivy
/**
* Copyright (C) 2012 FuseSource, Inc.
* http://fusesource.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.hawtdispatch.example.discovery
import _root_.java.io.{EOFException, ByteArrayOutputStream}
import _root_.java.net.{ConnectException, InetSocketAddress, URI}
import _root_.java.util.concurrent.TimeUnit
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import org.fusesource.hawtdispatch._
/**
* An example of a networks of servers which advertise connection information to each other.
*/
object EchoNetScala {
def main(args:Array[String]):Unit = {
run
}
def run() = {
val a = new Server(4444).start();
val b = new Server(5555).start();
val c = new Server(6666).start();
Thread.sleep(200);
a.connect(3333);
a.connect(b);
b.connect(c);
System.in.read
}
class Server(val port: Int) {
val me = URI.create("conn://localhost:" + port);
val serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);
var seen = List[URI]()
val queue = createQueue(me.toString)
val accept_source = createSource(serverChannel, SelectionKey.OP_ACCEPT, queue);
accept_source.setEventHandler(^ {
// we are a server
// when you are a server, we must first listen for the
// address of the client before sending data.
// once they send us their address, we will send our
// full list of known addresses, followed by our own
// address to signal that we are done.
// Afterward we will only pulls our heartbeat
val client = serverChannel.accept();
try {
val address = client.socket.getRemoteSocketAddress.asInstanceOf[InetSocketAddress]
trace("accept " + address.getPort());
client.configureBlocking(false);
// Server sessions start by reading the client's greeting
val session = new Session(this, client, address)
session.start_read_greeting
} catch {
case e: Exception =>
client.close
}
});
trace("Listening");
def start() = {
accept_source.resume
this
}
def stop() = {
accept_source.suspend
}
def close() = {
accept_source.cancel
}
accept_source.onCancel {
serverChannel.close();
}
def connect(s: Server):Unit = {
connect(s.port);
}
def connect(port: Int):Unit = {
connect(URI.create("conn://localhost:" + port));
}
def connect(uri: URI): Unit = ^{
if ( me.equals(uri) || seen.contains(uri) )
return;
val port = uri.getPort();
val host = uri.getHost();
trace("open " + uri);
val socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
val address = new InetSocketAddress(host, port);
socketChannel.connect(address);
val connect_source = createSource(socketChannel, SelectionKey.OP_CONNECT, queue);
connect_source.onEvent {
connect_source.cancel
try {
socketChannel.finishConnect
trace("connected " + uri);
val session = new Session(this, socketChannel, address, uri)
session.start_write_greeting
}
catch {
case e:ConnectException =>
trace("connect to "+uri+" FAILED.");
}
}
connect_source.resume
seen = uri :: seen;
} >>: queue
def trace(str: String) {
println(String.format("%5d - %s", new java.lang.Integer(port), str));
}
}
class Session(val server:Server, val channel: SocketChannel, val address: InetSocketAddress, val uri: URI) {
def this(server:Server, channel: SocketChannel, address: InetSocketAddress) = {
this (server, channel, address, URI.create("conn://" + address.getHostName() + ":" + address.getPort()))
}
val read_buffer = ByteBuffer.allocate(1024);
val queue = createQueue(uri.toString)
val read_source = createSource(channel, SelectionKey.OP_READ, queue);
val write_source = createSource(channel, SelectionKey.OP_WRITE, queue);
val seen = server.seen
def start_read_greeting = {
read_source.setEventHandler(read_greeting)
read_source.resume
}
def read_greeting = ^{
val message = read_frame
if (message!=null) {
// stop looking for read events..
read_source.suspend
val uri = URI.create(message);
trace("welcome");
// Send them our seen uris..
var list:List[Any] = seen.filterNot(x=> x==server.me || x==uri )
list = list ::: List("end");
start_write_data(list, ^{
start_read_hearbeat
})
}
}
def start_write_greeting = {
trace("hello");
start_write_data(server.me::Nil, ^{
start_read_server_listings
})
}
def start_read_server_listings = {
read_source.setEventHandler(read_server_listings)
read_source.resume
}
var listed = List[URI]()
def read_server_listings = ^ {
val message = read_frame
if (message!=null) {
if( message != "end" ) {
val uri: URI = URI.create(message)
listed = uri :: listed;
server.connect(uri)
} else {
// Send them our seen uris..
var list:List[Any] = seen.filterNot(x=> listed.contains(x) || x==server.me )
list = list ::: List("end");
start_write_data(list, ^{
// once done, start sending heartbeats.
start_write_hearbeat
})
}
}
}
def start_read_client_listings = {
read_source.setEventHandler(read_clientlistings)
read_source.resume
}
def read_clientlistings = ^ {
val message = read_frame
if (message!=null) {
if( message != "end" ) {
server.connect(URI.create(message))
} else {
start_read_hearbeat
}
}
}
def start_write_hearbeat:Unit = {
queue.executeAfter(1, TimeUnit.SECONDS, ^{
trace("ping");
start_write_data("ping"::Nil, ^{
start_write_hearbeat
})
});
}
def start_read_hearbeat = {
read_source.setEventHandler(read_hearbeat)
read_source.resume
}
def read_hearbeat = ^ {
val message = read_frame
if (message != null) {
trace("pong");
}
}
def start_write_data(list:List[Any], onDone:Runnable) = {
val baos = new ByteArrayOutputStream()
list.foreach { next =>
baos.write(next.toString().getBytes("UTF-8"))
baos.write(0)
}
val buffer = ByteBuffer.wrap(baos.toByteArray)
write_source.setEventHandler(write_data(buffer, onDone))
write_source.resume
}
def write_data(buffer:ByteBuffer, onDone:Runnable) = ^ {
channel.write(buffer)
if (buffer.remaining == 0) {
write_source.suspend
onDone.run
}
}
def read_frame(): String = {
if( channel.read(read_buffer) == -1 ) {
throw new EOFException();
}
val buf = read_buffer.array
val endPos = eof(buf, 0, read_buffer.position)
if (endPos < 0) {
trace(" --- ");
return null
}
var rc = new String(buf, 0, endPos)
val newPos = read_buffer.position - endPos
System.arraycopy(buf, endPos + 1, buf, 0, newPos)
read_buffer.position(newPos)
// trace(rc);
return rc
}
def eof(data: Array[Byte], offset: Int, pos: Int): Int = {
var i = offset
while (i < pos) {
if (data(i) == 0) {
return i
}
i += 1
}
return - 1
}
def trace(str: String) = {
println(String.format("%5d %5d - %s", new java.lang.Integer(server.port), new java.lang.Integer(uri.getPort()), str));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy