From 791f29a177306c0a5711acfd5b6f634a17e6e30c Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 3 May 2019 20:52:05 +0400 Subject: [PATCH] Implemented key-lookup --- .../io/github/chronosx88/GunJava/Client.java | 35 ++++++-- .../io/github/chronosx88/GunJava/Dup.java | 11 +-- .../io/github/chronosx88/GunJava/Graph.java | 6 ++ .../io/github/chronosx88/GunJava/HAM.java | 81 ++++++++++++++++--- .../chronosx88/GunJava/MainClientServer.java | 15 ++++ .../io/github/chronosx88/GunJava/Node.java | 17 +++- .../io/github/chronosx88/GunJava/Server.java | 30 ++++--- .../io/github/chronosx88/GunJava/Utils.java | 25 ++++++ 8 files changed, 182 insertions(+), 38 deletions(-) create mode 100644 src/main/java/io/github/chronosx88/GunJava/MainClientServer.java diff --git a/src/main/java/io/github/chronosx88/GunJava/Client.java b/src/main/java/io/github/chronosx88/GunJava/Client.java index 33ec4d8..62debd0 100644 --- a/src/main/java/io/github/chronosx88/GunJava/Client.java +++ b/src/main/java/io/github/chronosx88/GunJava/Client.java @@ -10,6 +10,7 @@ import java.net.URISyntaxException; public class Client extends WebSocketClient { private Dup dup = new Dup(); + private Graph graph = new Graph(); public Client(InetAddress address, int port) throws URISyntaxException { super(new URI("ws://" + address.getHostAddress() + ":" + port)); @@ -20,31 +21,53 @@ public class Client extends WebSocketClient { System.out.println("Connection open. Status: " + handshakeData.getHttpStatus()); Utils.setTimeout(() -> { JSONObject msg = new JSONObject(); - msg.put("#", dup.track(Dup.random(3))); + msg + .put("#", dup.track(Dup.random())) + .put("get", new JSONObject() + .put("#", "FDSA") + .put(".", "species")); + this.send(msg.toString()); + }, 2000); + + Utils.setTimeout(() -> { + JSONObject msg = new JSONObject(); + msg.put("#", dup.track(Dup.random())); msg.put("put", new JSONObject() .put("ASDF", Utils.newNode("ASDF", new JSONObject() .put("name", "Mark Nadal") .put("boss", new JSONObject().put("#", "FDSA"))).toJSONObject()) .put("FDSA", Utils.newNode("FDSA", new JSONObject().put("name", "Fluffy").put("species", "a kitty").put("slave", new JSONObject().put("#", "ASDF"))).toJSONObject())); this.send(msg.toString()); - }, 1000); + }, (int) (1000 * Math.random())); Utils.setTimeout(() -> { JSONObject msg = new JSONObject(); - msg.put("#", dup.track(Dup.random(3))); + msg.put("#", dup.track(Dup.random())); msg.put("put", new JSONObject() .put("ASDF", Utils.newNode("ASDF", new JSONObject() .put("name", "Mark")).toJSONObject()) .put("FDSA", Utils.newNode("FDSA", new JSONObject().put("species", "felis silvestris").put("color", "ginger")).toJSONObject())); this.send(msg.toString()); - }, 2000); + }, (int) (1000 * Math.random())); } @Override public void onMessage(String message) { JSONObject msg = new JSONObject(message); - if(dup.check(msg.getString("#"))) { return; } + if(dup.check(msg.getString("#"))){ return; } dup.track(msg.getString("#")); - System.out.println(msg.toString()); + if(msg.opt("put") != null) { + HAM.mix(new Graph(msg.getJSONObject("put")), graph); + } + if(msg.opt("get") != null) { + Graph getResults = Utils.getRequest(msg.getJSONObject("get"), graph); + JSONObject ack = new JSONObject() + .put("#", dup.track(Dup.random())) + .put("@", msg.getString("#")) + .put("put", getResults.toJSONObject()); + this.send(ack.toString()); + } + System.out.println("---------------"); + System.out.println(msg.toString(2)); this.send(message); } diff --git a/src/main/java/io/github/chronosx88/GunJava/Dup.java b/src/main/java/io/github/chronosx88/GunJava/Dup.java index 5faf3d9..788274b 100644 --- a/src/main/java/io/github/chronosx88/GunJava/Dup.java +++ b/src/main/java/io/github/chronosx88/GunJava/Dup.java @@ -2,10 +2,11 @@ package io.github.chronosx88.GunJava; import java.util.Map; import java.util.Random; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class Dup { - private static char[] randomPack = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + private static char[] randomSeed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); private static Random random = new Random(System.currentTimeMillis()); private Map s = new ConcurrentHashMap<>(); private DupOpt opt = new DupOpt(); @@ -40,11 +41,7 @@ public class Dup { } } - public static String random(int len) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < len; i++) { - sb.append(randomPack[random.nextInt(randomPack.length)]); - } - return sb.toString(); + public static String random() { + return UUID.randomUUID().toString(); } } diff --git a/src/main/java/io/github/chronosx88/GunJava/Graph.java b/src/main/java/io/github/chronosx88/GunJava/Graph.java index 7d0355f..34f2742 100644 --- a/src/main/java/io/github/chronosx88/GunJava/Graph.java +++ b/src/main/java/io/github/chronosx88/GunJava/Graph.java @@ -35,6 +35,8 @@ public class Graph { return nodes.entrySet(); } + public Collection nodes() { return nodes.values(); } + @Override public String toString() { JSONObject jsonObject = new JSONObject(); @@ -59,4 +61,8 @@ public class Graph { } return jsonObject; } + + public boolean isEmpty() { + return nodes.isEmpty(); + } } \ No newline at end of file diff --git a/src/main/java/io/github/chronosx88/GunJava/HAM.java b/src/main/java/io/github/chronosx88/GunJava/HAM.java index e5c3de5..04786b3 100644 --- a/src/main/java/io/github/chronosx88/GunJava/HAM.java +++ b/src/main/java/io/github/chronosx88/GunJava/HAM.java @@ -2,10 +2,11 @@ package io.github.chronosx88.GunJava; import org.json.JSONObject; -import java.util.Iterator; import java.util.Map; public class HAM { + private static long defer = Long.MAX_VALUE; + static class HAMResult { public boolean defer = false; public boolean historical = false; @@ -52,7 +53,7 @@ public class HAM { return result; } - public static Graph mix(Graph change, Graph graph) { + public static Graph mix(Graph change, Graph data) { long machine = System.currentTimeMillis(); Graph diff = null; for(Map.Entry entry : change.entries()) { @@ -63,21 +64,23 @@ public class HAM { long state = node.states.getLong(key); long was = -1; Object known = null; - if(graph == null) { - graph = new Graph(); + if(data == null) { + data = new Graph(); } - if(graph.hasNode(node.soul)) { - if(graph.getNode(node.soul).states.opt(key) != null) { - was = graph.getNode(node.soul).states.getLong(key); + if(data.hasNode(node.soul)) { + if(data.getNode(node.soul).states.opt(key) != null) { + was = data.getNode(node.soul).states.getLong(key); } - known = graph.getNode(node.soul).values.opt(key) == null ? 0 : graph.getNode(node.soul).values.opt(key); + known = data.getNode(node.soul).values.opt(key) == null ? 0 : data.getNode(node.soul).values.opt(key); } HAMResult ham = ham(machine, state, was, value, known); if(!ham.incoming) { if(ham.defer) { System.out.println("DEFER: " + key + " " + value); - // FIXME + // Hack for accessing value in lambda without making the variable final + Graph[] graph = new Graph[] {data}; + Utils.setTimeout(() -> mix(node, graph[0]), (int) (state - System.currentTimeMillis())); } continue; } @@ -90,18 +93,70 @@ public class HAM { diff.addNode(node.soul, Utils.newNode(node.soul, new JSONObject())); } - if(!graph.hasNode(node.soul)) { - graph.addNode(node.soul, Utils.newNode(node.soul, new JSONObject())); + if(!data.hasNode(node.soul)) { + data.addNode(node.soul, Utils.newNode(node.soul, new JSONObject())); } - graph.getNode(node.soul).values.put(key, value); + data.getNode(node.soul).values.put(key, value); diff.getNode(node.soul).values.put(key, value); diff.getNode(node.soul).states.put(key, state); - graph.getNode(node.soul).states.put(key, state); + data.getNode(node.soul).states.put(key, state); } } return diff; } + + public static Graph mix(Node incomingNode, Graph data) { + long machine = System.currentTimeMillis(); + Graph diff = null; + + for(String key : incomingNode.values.keySet()) { + Object value = incomingNode.values.get(key); + if ("_".equals(key)) { continue; } + long state = incomingNode.states.getLong(key); + long was = -1; + Object known = null; + if(data == null) { + data = new Graph(); + } + if(data.hasNode(incomingNode.soul)) { + if(data.getNode(incomingNode.soul).states.opt(key) != null) { + was = data.getNode(incomingNode.soul).states.getLong(key); + } + known = data.getNode(incomingNode.soul).values.opt(key) == null ? 0 : data.getNode(incomingNode.soul).values.opt(key); + } + + HAMResult ham = ham(machine, state, was, value, known); + if(!ham.incoming) { + if(ham.defer) { + System.out.println("DEFER: " + key + " " + value); + // Hack for accessing value in lambda without making the variable final + Graph[] graph = new Graph[] {data}; + Utils.setTimeout(() -> mix(incomingNode, graph[0]), (int) (state - System.currentTimeMillis())); + } + continue; + } + + if(diff == null) { + diff = new Graph(); + } + + if(!diff.hasNode(incomingNode.soul)) { + diff.addNode(incomingNode.soul, Utils.newNode(incomingNode.soul, new JSONObject())); + } + + if(!data.hasNode(incomingNode.soul)) { + data.addNode(incomingNode.soul, Utils.newNode(incomingNode.soul, new JSONObject())); + } + + data.getNode(incomingNode.soul).values.put(key, value); + diff.getNode(incomingNode.soul).values.put(key, value); + + diff.getNode(incomingNode.soul).states.put(key, state); + data.getNode(incomingNode.soul).states.put(key, state); + } + return diff; + } } diff --git a/src/main/java/io/github/chronosx88/GunJava/MainClientServer.java b/src/main/java/io/github/chronosx88/GunJava/MainClientServer.java new file mode 100644 index 0000000..fdf014f --- /dev/null +++ b/src/main/java/io/github/chronosx88/GunJava/MainClientServer.java @@ -0,0 +1,15 @@ +package io.github.chronosx88.GunJava; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.URISyntaxException; +import java.net.UnknownHostException; + +public class MainClientServer { + public static void main(String[] args) throws URISyntaxException, UnknownHostException { + Server server = new Server(21334); + server.start(); + Client client = new Client(Inet4Address.getByAddress(new byte[]{127, 0, 0, 1}), 21334); + client.connect(); + } +} diff --git a/src/main/java/io/github/chronosx88/GunJava/Node.java b/src/main/java/io/github/chronosx88/GunJava/Node.java index 9fb48c2..3d4c576 100644 --- a/src/main/java/io/github/chronosx88/GunJava/Node.java +++ b/src/main/java/io/github/chronosx88/GunJava/Node.java @@ -4,8 +4,8 @@ import org.json.JSONObject; public class Node implements Comparable { public JSONObject values; // Data - public final JSONObject states; // Metadata for diff - public final String soul; // i.e. ID of node + public JSONObject states; // Metadata for diff + public String soul; // i.e. ID of node /** * Create a Node from a JSON object. @@ -13,7 +13,7 @@ public class Node implements Comparable { * @param rawData JSON object, which contains the data */ public Node(JSONObject rawData) { - this.values = rawData; + this.values = new JSONObject(rawData.toString()); this.states = values.getJSONObject("_").getJSONObject(">"); this.soul = values.getJSONObject("_").getString("#"); values.remove("_"); @@ -61,4 +61,15 @@ public class Node implements Comparable { jsonObject.put("_", new JSONObject().put("#", soul).put(">", states)); return jsonObject; } + + public JSONObject getMetadata() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("_", new JSONObject().put("#", soul).put(">", states)); + return jsonObject; + } + + public void setMetadata(JSONObject metadata) { + soul = metadata.getJSONObject("_").getString("#"); + states = metadata.getJSONObject("_").getJSONObject(">"); + } } \ No newline at end of file diff --git a/src/main/java/io/github/chronosx88/GunJava/Server.java b/src/main/java/io/github/chronosx88/GunJava/Server.java index 049f7a7..a9ee1a4 100644 --- a/src/main/java/io/github/chronosx88/GunJava/Server.java +++ b/src/main/java/io/github/chronosx88/GunJava/Server.java @@ -6,27 +6,26 @@ import org.java_websocket.server.WebSocketServer; import org.json.JSONObject; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.Timer; public class Server extends WebSocketServer { private Timer timer = new Timer(true); private Dup dup = new Dup(); - private ArrayList peers = new ArrayList<>(); private Graph graph = new Graph(); public Server(int port) { super(new InetSocketAddress(port)); + setReuseAddr(true); } @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { - peers.add(conn); + System.out.println("Connected new peer: " + conn.getRemoteSocketAddress().toString()); } @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { - // + System.out.println("Peer " + conn.getRemoteSocketAddress().toString() + " closed the connection for reason (code): " + reason + " (" + code + ")"); } @Override @@ -36,12 +35,19 @@ public class Server extends WebSocketServer { dup.track(msg.getString("#")); if(msg.opt("put") != null) { HAM.mix(new Graph(msg.getJSONObject("put")), graph); - System.out.println("----------------"); - System.out.println(graph.toPrettyString()); } - for (WebSocket peer : peers) { - peer.send(message); + if(msg.opt("get") != null) { + Graph result = Utils.getRequest(msg.optJSONObject("get"), graph); + if(!result.isEmpty()) { + JSONObject ack = new JSONObject(); + emit(ack + .put("#", dup.track(Dup.random())) + .put("@", msg.getString("#")) + .put("put", result.toJSONObject()) + .toString()); + } } + emit(message); } @Override @@ -51,6 +57,12 @@ public class Server extends WebSocketServer { @Override public void onStart() { - // + System.out.println("Server started on port: " + getPort()); + } + + public void emit(String data) { + for(WebSocket conn : this.getConnections()) { + conn.send(data); + } } } diff --git a/src/main/java/io/github/chronosx88/GunJava/Utils.java b/src/main/java/io/github/chronosx88/GunJava/Utils.java index 3032336..13133d0 100644 --- a/src/main/java/io/github/chronosx88/GunJava/Utils.java +++ b/src/main/java/io/github/chronosx88/GunJava/Utils.java @@ -25,4 +25,29 @@ public class Utils { data.put("_", new JSONObject().put("#", soul).put(">", states)); return new Node(data); } + + public static Graph getRequest(JSONObject lex, Graph graph) { + String soul = lex.getString("#"); + String key = lex.optString(".", null); + Node node = graph.getNode(soul); + Object tmp; + if(node == null) { + return new Graph(); + } + if(key != null) { + tmp = node.values.opt(key); + if(tmp == null) { + return new Graph(); + } + Node node1 = new Node(node.toJSONObject()); + node = Utils.newNode(node.soul, new JSONObject()); + node.setMetadata(node1.getMetadata()); + node.values.put(key, tmp); + JSONObject tmpStates = node1.states; + node.states.put(key, tmpStates.get(key)); + } + Graph ack = new Graph(); + ack.addNode(soul, node); + return ack; + } }