[WIP] Removing Put/GetBuilders, renamed project to JGUN (final), added get operation.

This commit is contained in:
ChronosX88 2019-05-06 20:42:55 +04:00
parent 7d61993a3c
commit ee98da62a3
11 changed files with 54 additions and 461 deletions

View File

@ -1,47 +0,0 @@
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[] randomSeed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private static Random random = new Random(System.currentTimeMillis());
private Map<String, Long> s = new ConcurrentHashMap<>();
private DupOpt opt = new DupOpt();
private Thread to = null;
public Dup() {
opt.max = 1000;
opt.age = 1000 * 9;
}
public String track(String id) {
s.put(id, System.currentTimeMillis());
if(to == null) {
Utils.setTimeout(() -> {
for(Map.Entry<String, Long> entry : s.entrySet()) {
if(opt.age > (System.currentTimeMillis() - entry.getValue()))
continue;
s.remove(entry.getKey());
}
to = null;
}, opt.age);
}
return id;
}
public boolean check(String id) {
if(s.containsKey(id)) {
track(id);
return true;
} else {
return false;
}
}
public static String random() {
return UUID.randomUUID().toString();
}
}

View File

@ -1,6 +0,0 @@
package io.github.chronosx88.GunJava;
public class DupOpt {
public int max;
public int age;
}

View File

@ -1,164 +0,0 @@
package io.github.chronosx88.GunJava;
import io.github.chronosx88.GunJava.storageBackends.MemoryBackend;
import io.github.chronosx88.GunJava.storageBackends.StorageBackend;
import org.json.JSONObject;
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;
public boolean converge = false;
public boolean incoming = false;
public boolean current = false;
public boolean state = false;
public String err = null;
}
public static HAMResult ham(long machineState, long incomingState, long currentState, Object incomingValue, Object currentValue) {
HAMResult result = new HAMResult();
if(machineState < incomingState) {
result.defer = true;
return result;
}
if(incomingState < currentState){
result.historical = true;
return result;
}
if(currentState < incomingState) {
result.converge = true;
result.incoming = true;
return result;
}
if(incomingState == currentState) {
if(incomingValue.equals(currentValue)) {
result.state = true;
return result;
}
if((incomingValue.toString().compareTo(currentValue.toString())) < 0) {
result.converge = true;
result.current = true;
return result;
}
if((currentValue.toString().compareTo(incomingValue.toString())) < 0) {
result.converge = true;
result.incoming = true;
return result;
}
}
result.err = "Invalid CRDT Data: "+ incomingValue +" to "+ currentValue +" at "+ incomingState +" to "+ currentState +"!";
return result;
}
public static MemoryBackend mix(MemoryBackend change, StorageBackend data) {
long machine = System.currentTimeMillis();
MemoryBackend diff = null;
for(Map.Entry<String, Node> entry : change.entries()) {
Node node = entry.getValue();
for(String key : node.values.keySet()) {
Object value = node.values.get(key);
if ("_".equals(key)) { continue; }
long state = node.states.getLong(key);
long was = -1;
Object known = null;
if(data == null) {
data = new MemoryBackend();
}
if(data.hasNode(node.soul)) {
if(data.getNode(node.soul).states.opt(key) != null) {
was = data.getNode(node.soul).states.getLong(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);
// Hack for accessing value in lambda without making the variable final
StorageBackend[] graph = new StorageBackend[] {data};
Utils.setTimeout(() -> mix(node, graph[0]), (int) (state - System.currentTimeMillis()));
}
continue;
}
if(diff == null) {
diff = new MemoryBackend();
}
if(!diff.hasNode(node.soul)) {
diff.addNode(node.soul, Utils.newNode(node.soul, new JSONObject()));
}
if(!data.hasNode(node.soul)) {
data.addNode(node.soul, Utils.newNode(node.soul, new JSONObject()));
}
data.getNode(node.soul).values.put(key, value);
diff.getNode(node.soul).values.put(key, value);
diff.getNode(node.soul).states.put(key, state);
data.getNode(node.soul).states.put(key, state);
}
}
return diff;
}
public static MemoryBackend mix(Node incomingNode, StorageBackend data) {
long machine = System.currentTimeMillis();
MemoryBackend 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 MemoryBackend();
}
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
StorageBackend[] graph = new StorageBackend[] {data};
Utils.setTimeout(() -> mix(incomingNode, graph[0]), (int) (state - System.currentTimeMillis()));
}
continue;
}
if(diff == null) {
diff = new MemoryBackend();
}
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;
}
}

View File

@ -1,76 +0,0 @@
package io.github.chronosx88.GunJava;
import io.github.chronosx88.GunJava.storageBackends.MemoryBackend;
import org.json.JSONObject;
public class Node implements Comparable<Node> {
public JSONObject values; // Data
public JSONObject states; // Metadata for diff
public String soul; // i.e. ID of node
/**
* Create a Node from a JSON object.
*
* @param rawData JSON object, which contains the data
*/
public Node(JSONObject rawData) {
this.values = new JSONObject(rawData.toString());
this.states = values.getJSONObject("_").getJSONObject(">");
this.soul = values.getJSONObject("_").getString("#");
values.remove("_");
}
@Override
public int compareTo(Node other) {
return soul.compareTo(other.soul);
}
@Override
public boolean equals(Object other) {
if (other == null)
return false;
if (other instanceof String)
return soul.equals(other);
if (other instanceof Node)
return compareTo((Node) other) == 0;
return false;
}
@Override
public int hashCode() {
return soul.hashCode();
}
public boolean isNode(String key) {
return values.optJSONObject(key) != null;
}
public Node getNode(String key, MemoryBackend g) {
String soulRef = values.getJSONObject(key).getString("#");
return g.getNode(soulRef);
}
@Override
public String toString() {
JSONObject jsonObject = new JSONObject(values.toString());
jsonObject.put("_", new JSONObject().put("#", soul).put(">", states));
return jsonObject.toString();
}
public JSONObject toJSONObject() {
JSONObject jsonObject = new JSONObject(values.toString());
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(">");
}
}

View File

@ -1,55 +0,0 @@
package io.github.chronosx88.GunJava;
import io.github.chronosx88.GunJava.storageBackends.MemoryBackend;
import io.github.chronosx88.GunJava.storageBackends.StorageBackend;
import org.json.JSONObject;
public class Utils {
public static Thread setTimeout(Runnable runnable, int delay){
Thread thread = new Thread(() -> {
try {
Thread.sleep(delay);
runnable.run();
}
catch (Exception e){
e.printStackTrace();
}
});
thread.start();
return thread;
}
public static Node newNode(String soul, JSONObject data) {
JSONObject states = new JSONObject();
for (String key : data.keySet()) {
states.put(key, System.currentTimeMillis());
}
data.put("_", new JSONObject().put("#", soul).put(">", states));
return new Node(data);
}
public static MemoryBackend getRequest(JSONObject lex, StorageBackend graph) {
String soul = lex.getString("#");
String key = lex.optString(".", null);
Node node = graph.getNode(soul);
Object tmp;
if(node == null) {
return new MemoryBackend();
}
if(key != null) {
tmp = node.values.opt(key);
if(tmp == null) {
return new MemoryBackend();
}
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));
}
MemoryBackend ack = new MemoryBackend();
ack.addNode(soul, node);
return ack;
}
}

View File

@ -1,69 +0,0 @@
package io.github.chronosx88.GunJava.storageBackends;
import io.github.chronosx88.GunJava.Node;
import org.json.JSONObject;
import java.util.*;
public class MemoryBackend implements StorageBackend {
private final HashMap<String, Node> nodes;
public MemoryBackend(JSONObject source) {
nodes = new LinkedHashMap<>();
for (String soul : source.keySet())
nodes.put(soul, new Node(source.getJSONObject(soul)));
}
public MemoryBackend() {
nodes = new LinkedHashMap<>();
}
public Node getNode(String soul) {
return nodes.getOrDefault(soul, null);
}
public void addNode(String soul, Node incomingNode) {
nodes.put(soul, incomingNode);
}
public boolean hasNode(String soul) {
return nodes.containsKey(soul);
}
public Set<Map.Entry<String, Node>> entries() {
return nodes.entrySet();
}
public Collection<Node> nodes() { return nodes.values(); }
@Override
public String toString() {
JSONObject jsonObject = new JSONObject();
for(Map.Entry<String, Node> entry : nodes.entrySet()) {
jsonObject.put(entry.getKey(), entry.getValue().toJSONObject());
}
return jsonObject.toString();
}
public String toPrettyString() {
JSONObject jsonObject = new JSONObject();
for(Map.Entry<String, Node> entry : nodes.entrySet()) {
jsonObject.put(entry.getKey(), entry.getValue().toJSONObject());
}
return jsonObject.toString(2);
}
public JSONObject toJSONObject() {
JSONObject jsonObject = new JSONObject();
for(Map.Entry<String, Node> entry : nodes.entrySet()) {
jsonObject.put(entry.getKey(), entry.getValue().toJSONObject());
}
return jsonObject;
}
public boolean isEmpty() {
return nodes.isEmpty();
}
}

View File

@ -1,20 +0,0 @@
package io.github.chronosx88.GunJava.storageBackends;
import io.github.chronosx88.GunJava.Node;
import org.json.JSONObject;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public interface StorageBackend {
Node getNode(String soul);
void addNode(String soul, Node node);
boolean hasNode(String soul);
Set<Map.Entry<String, Node>> entries();
Collection<Node> nodes();
String toString();
String toPrettyString();
JSONObject toJSONObject();
boolean isEmpty();
}

View File

@ -46,13 +46,6 @@ public class Dispatcher {
}).start(); }).start();
} }
public void sendGetRequest(String key) {
new Thread(() -> {
JSONObject jsonGet = Utils.formatGetRequest(key, null);
peer.emit(jsonGet.toString());
}).start();
}
public void sendGetRequest(String key, String field) { public void sendGetRequest(String key, String field) {
new Thread(() -> { new Thread(() -> {
JSONObject jsonGet = Utils.formatGetRequest(key, field); JSONObject jsonGet = Utils.formatGetRequest(key, field);

View File

@ -1,10 +1,13 @@
package io.github.chronosx88.JGUN; package io.github.chronosx88.JGUN;
import io.github.chronosx88.JGUN.futures.builders.GetBuilder; import io.github.chronosx88.JGUN.futures.FutureGet;
import io.github.chronosx88.JGUN.futures.builders.PutBuilder; import io.github.chronosx88.JGUN.futures.builders.PutBuilder;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class PathRef { public class PathRef {
private final ArrayList<String> path = new ArrayList<>(); private final ArrayList<String> path = new ArrayList<>();
@ -19,8 +22,50 @@ public class PathRef {
return this; return this;
} }
public GetBuilder getData() { public FutureGet getData() {
return new GetBuilder(path); FutureGet futureGet = new FutureGet(Dup.random());
Iterator<String> iterator = path.iterator();
new Thread(() -> {
CompletableFuture<JSONObject> future = CompletableFuture.supplyAsync(() -> {
String rootSoul = iterator.next();
String field = iterator.hasNext() ? iterator.next() : null;
FutureGet futureGetRootSoul = new FutureGet(Dup.random());
dispatcher.addPendingFuture(futureGetRootSoul);
dispatcher.sendGetRequest(rootSoul, field);
futureGetRootSoul.awaitUninterruptibly();
if(futureGetRootSoul.isSuccess() && futureGetRootSoul.getData() != null) {
return futureGetRootSoul.getData();
} else {
return null;
}
});
while(iterator.hasNext()) {
future = future.thenApply(jsonObject -> {
String soul = iterator.next();
String field = iterator.hasNext() ? iterator.next() : null;
if(jsonObject != null) {
String nodeRef = jsonObject.getJSONObject(soul).getString("#");
FutureGet get = new FutureGet(Dup.random());
dispatcher.addPendingFuture(get);
dispatcher.sendGetRequest(nodeRef, field);
get.awaitUninterruptibly();
if(get.isSuccess() && get.getData() != null) {
return get.getData();
} else {
return null;
}
}
return null;
});
}
try {
JSONObject data = future.get();
futureGet.done(data);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}).start();
return futureGet;
} }
public PutBuilder put(JSONObject data) { public PutBuilder put(JSONObject data) {

View File

@ -28,14 +28,17 @@ public class FutureGet extends BaseFuture<FutureGet> {
public FutureGet done(JSONObject data) { public FutureGet done(JSONObject data) {
synchronized (lock) { synchronized (lock) {
if(data != null) {
if (!data.isEmpty()) { if (!data.isEmpty()) {
this.getStatus = GetStatus.OK; this.getStatus = GetStatus.OK;
this.type = FutureType.OK; this.type = FutureType.OK;
}
} else { } else {
this.getStatus = GetStatus.NOT_FOUND; this.getStatus = GetStatus.NOT_FOUND;
this.type = FutureType.FAILED; this.type = FutureType.FAILED;
this.reason = "Not found"; this.reason = "Not found";
} }
this.data = data; this.data = data;
if (!completedAndNotify()) { if (!completedAndNotify()) {
return this; return this;

View File

@ -1,11 +0,0 @@
package io.github.chronosx88.JGUN.futures.builders;
import java.util.ArrayList;
public class GetBuilder {
private final ArrayList<String> path;
public GetBuilder(ArrayList<String> path) {
this.path = path;
}
}