From 58b36d525b792514f1eac58e89f774998b226684 Mon Sep 17 00:00:00 2001 From: aslakhellesoy Date: Mon, 7 Feb 2011 15:23:30 -0400 Subject: [PATCH] Moved NIO stuff into abstractions for server and connection. --- src/main/java/dnode/Callback.java | 2 +- src/main/java/dnode/Connection.java | 11 +++ src/main/java/dnode/ConnectionHandler.java | 4 ++ src/main/java/dnode/DNode.java | 78 ++++++---------------- src/main/java/dnode/DNodeObject.java | 6 +- src/main/java/dnode/Server.java | 9 +++ src/main/java/dnode/nio/NIOConnection.java | 47 +++++++++++++ src/main/java/dnode/nio/NIOServer.java | 36 ++++++++++ src/test/java/dnode/DNodeObjectTest.java | 5 +- src/test/java/dnode/DNodeTest.java | 24 +++++-- 10 files changed, 153 insertions(+), 69 deletions(-) create mode 100644 src/main/java/dnode/Connection.java create mode 100644 src/main/java/dnode/ConnectionHandler.java create mode 100644 src/main/java/dnode/Server.java create mode 100644 src/main/java/dnode/nio/NIOConnection.java create mode 100644 src/main/java/dnode/nio/NIOServer.java diff --git a/src/main/java/dnode/Callback.java b/src/main/java/dnode/Callback.java index f1075aa..f6ee6dc 100644 --- a/src/main/java/dnode/Callback.java +++ b/src/main/java/dnode/Callback.java @@ -1,5 +1,5 @@ package dnode; public interface Callback { - void call(Object... args) throws RuntimeException; + void call(Object... args) throws RuntimeException; } diff --git a/src/main/java/dnode/Connection.java b/src/main/java/dnode/Connection.java new file mode 100644 index 0000000..1e4eda7 --- /dev/null +++ b/src/main/java/dnode/Connection.java @@ -0,0 +1,11 @@ +package dnode; + +import java.io.IOException; + +public interface Connection { + void send(String data); + + String read() throws IOException; + + void close() throws IOException; +} diff --git a/src/main/java/dnode/ConnectionHandler.java b/src/main/java/dnode/ConnectionHandler.java new file mode 100644 index 0000000..7aae505 --- /dev/null +++ b/src/main/java/dnode/ConnectionHandler.java @@ -0,0 +1,4 @@ +package dnode; + +public interface ConnectionHandler { +} diff --git a/src/main/java/dnode/DNode.java b/src/main/java/dnode/DNode.java index 0e2377d..f05789d 100644 --- a/src/main/java/dnode/DNode.java +++ b/src/main/java/dnode/DNode.java @@ -1,46 +1,41 @@ package dnode; import com.google.gson.*; +import dnode.nio.NIOServer; import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; import java.util.HashMap; import java.util.Map; public class DNode { - private static Charset charset = Charset.forName("UTF-8"); - private static CharsetEncoder encoder = charset.newEncoder(); - private static CharsetDecoder decoder = charset.newDecoder(); - private final DNodeObject instance; - private SocketChannel sc; private Map callbacks = new HashMap(); - private ServerSocketChannel ssc; + private Server server; public DNode(Object instance) { this.instance = new DNodeObject(instance); } - public void listen(int port) throws IOException { - sc = connect(port); - send(methods()); - String clientMethods = read(); - String invocation = read(); + public void shutdown() throws IOException { + this.server.shutdown(); + } + + public void listen(Server server) throws IOException { + this.server = server; + server.listen(this); + } + + public void handle(final Connection connection) { try { + connection.send(methods()); + String clientMethods = connection.read(); + String invocation = connection.read(); invoke(invocation, new Callback() { public void call(Object... args) { JsonArray jsonArgs = transform(args); - send(responseString(0, jsonArgs, new JsonObject(), new JsonArray())); + connection.send(responseString(0, jsonArgs, new JsonObject(), new JsonArray())); try { - shutdown(); + connection.close(); } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } @@ -52,6 +47,10 @@ public void call(Object... args) { } } + public void listen(int port) throws IOException { + listen(new NIOServer(port)); + } + private void invoke(String invocation, Callback callback) throws Throwable { JsonObject invocationJson = (JsonObject) new JsonParser().parse(invocation); instance.invoke(invocationJson, callback); @@ -77,43 +76,10 @@ private JsonElement toJson(Object o) { return e; } - private void shutdown() throws IOException { - ssc.close(); - sc.close(); - } - - private SocketChannel connect(int port) throws IOException { - ssc = ServerSocketChannel.open(); - InetSocketAddress isa = new InetSocketAddress(InetAddress.getLocalHost(), port); - ssc.socket().bind(isa); - emit("ready"); - return ssc.accept(); - } - - private void emit(String event, Object... args) { + public void emit(String event, Object... args) { callbacks.get(event).call(args); } - private String read() throws IOException { - ByteBuffer bb = ByteBuffer.allocate(1024); - sc.read(bb); - bb.flip(); - CharBuffer response = decoder.decode(bb); - return response.toString(); - } - - private void send(String data) { - try { - sc.write(encoder.encode(CharBuffer.wrap(data + "\r\n"))); - } catch (IOException e) { - try { - sc.close(); - } catch (IOException e1) { - e1.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - } - } - } - private String methods() { JsonArray arguments = new JsonArray(); arguments.add(instance.getSignature()); diff --git a/src/main/java/dnode/DNodeObject.java b/src/main/java/dnode/DNodeObject.java index 45c3c73..0831748 100644 --- a/src/main/java/dnode/DNodeObject.java +++ b/src/main/java/dnode/DNodeObject.java @@ -19,7 +19,7 @@ public DNodeObject(Object instance) { public JsonElement getSignature() { Class klass = this.instance.getClass(); JsonObject signature = new JsonObject(); - for(Method m : klass.getDeclaredMethods()) { + for (Method m : klass.getDeclaredMethods()) { signature.addProperty(m.getName(), "[Function]"); } return signature; @@ -29,10 +29,10 @@ public JsonElement getCallbacks() { Class klass = this.instance.getClass(); JsonObject callbacks = new JsonObject(); int index = 0; - for(Method m : klass.getDeclaredMethods()) { + for (Method m : klass.getDeclaredMethods()) { Class[] parameterTypes = m.getParameterTypes(); for (Class parameterType : parameterTypes) { - if(Callback.class.isAssignableFrom(parameterType)) { + if (Callback.class.isAssignableFrom(parameterType)) { JsonArray path = new JsonArray(); path.add(new JsonPrimitive("0")); path.add(new JsonPrimitive(m.getName())); diff --git a/src/main/java/dnode/Server.java b/src/main/java/dnode/Server.java new file mode 100644 index 0000000..01f62e1 --- /dev/null +++ b/src/main/java/dnode/Server.java @@ -0,0 +1,9 @@ +package dnode; + +import java.io.IOException; + +public interface Server { + void listen(DNode dnode) throws IOException; + + void shutdown() throws IOException; +} diff --git a/src/main/java/dnode/nio/NIOConnection.java b/src/main/java/dnode/nio/NIOConnection.java new file mode 100644 index 0000000..7a6c174 --- /dev/null +++ b/src/main/java/dnode/nio/NIOConnection.java @@ -0,0 +1,47 @@ +package dnode.nio; + +import dnode.Connection; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.SocketChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class NIOConnection implements Connection { + private static Charset charset = Charset.forName("UTF-8"); + private static CharsetEncoder encoder = charset.newEncoder(); + private static CharsetDecoder decoder = charset.newDecoder(); + + private final SocketChannel channel; + + public NIOConnection(SocketChannel channel) { + this.channel = channel; + } + + public void send(String data) { + try { + channel.write(encoder.encode(CharBuffer.wrap(data + "\r\n"))); + } catch (IOException e) { + try { + close(); + } catch (IOException e1) { + e1.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + } + + public String read() throws IOException { + ByteBuffer bb = ByteBuffer.allocate(1024); + channel.read(bb); + bb.flip(); + CharBuffer response = decoder.decode(bb); + return response.toString(); + } + + public void close() throws IOException { + channel.close(); + } +} diff --git a/src/main/java/dnode/nio/NIOServer.java b/src/main/java/dnode/nio/NIOServer.java new file mode 100644 index 0000000..a6f2795 --- /dev/null +++ b/src/main/java/dnode/nio/NIOServer.java @@ -0,0 +1,36 @@ +package dnode.nio; + +import dnode.Connection; +import dnode.DNode; +import dnode.Server; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +public class NIOServer implements Server { + private ServerSocketChannel ssc; + private final int port; + + public NIOServer(int port) { + this.port = port; + } + + public void listen(DNode dnode) throws IOException { + ssc = ServerSocketChannel.open(); + InetSocketAddress isa = new InetSocketAddress(InetAddress.getLocalHost(), port); + ssc.socket().bind(isa); + dnode.emit("ready"); + SocketChannel channel = ssc.accept(); + + Connection conn = new NIOConnection(channel); + dnode.handle(conn); + } + + public void shutdown() throws IOException { + ssc.close(); + } + +} diff --git a/src/test/java/dnode/DNodeObjectTest.java b/src/test/java/dnode/DNodeObjectTest.java index 3ff2793..25e7e07 100644 --- a/src/test/java/dnode/DNodeObjectTest.java +++ b/src/test/java/dnode/DNodeObjectTest.java @@ -7,10 +7,11 @@ public class DNodeObjectTest { public static class Cat { public void say(Callback callback) { - + } + public void meow(Callback callback) { - + } } diff --git a/src/test/java/dnode/DNodeTest.java b/src/test/java/dnode/DNodeTest.java index 32ba6b7..0f17e09 100644 --- a/src/test/java/dnode/DNodeTest.java +++ b/src/test/java/dnode/DNodeTest.java @@ -1,13 +1,18 @@ package dnode; import junit.framework.AssertionFailedError; +import org.junit.After; import org.junit.Test; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import static org.junit.Assert.assertEquals; public class DNodeTest { + private DNode dNode; + public static class Mooer { private final int moo; @@ -20,29 +25,34 @@ public void moo(Callback cb) { } public void boo(Callback cb) { - cb.call(moo*10); + cb.call(moo * 10); } } + @After + public void shutdownServer() throws IOException { + dNode.shutdown(); + } + private final Object signals = new Object(); @Test public void shouldTalk() throws IOException, InterruptedException { - final DNode dNode = new DNode(new Mooer(100)); + dNode = new DNode(new Mooer(100)); runServer(dNode); assertEquals("100\n", runClient("moo")); } @Test public void shouldUseDataInInstance() throws IOException, InterruptedException { - final DNode dNode = new DNode(new Mooer(200)); + dNode = new DNode(new Mooer(200)); runServer(dNode); assertEquals("200\n", runClient("moo")); } @Test public void shouldCallRightMethod() throws IOException, InterruptedException { - final DNode dNode = new DNode(new Mooer(300)); + dNode = new DNode(new Mooer(300)); runServer(dNode); assertEquals("3000\n", runClient("boo")); } @@ -82,8 +92,8 @@ private String runClient(String method) throws IOException, InterruptedException result.append(line).append("\n"); } int exit = client.waitFor(); - if(exit != 0) - throw new AssertionFailedError("Exit value from external process was " + exit + + if (exit != 0) + throw new AssertionFailedError("Exit value from external process was " + exit + " (with stdout/stderr: " + result + ")"); return result.toString(); }