Reimplement .put() API

This commit is contained in:
ChronosX88 2023-11-15 02:37:19 +03:00
parent 87da965616
commit b83e3ee14e
17 changed files with 347 additions and 55 deletions

View File

@ -20,6 +20,11 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.3'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.15.3'
implementation 'com.github.ben-manes.caffeine:jcache:3.1.5'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
}
test {
useJUnitPlatform()
}

View File

@ -0,0 +1,99 @@
package io.github.chronosx88.JGUN;
import io.github.chronosx88.JGUN.models.MemoryGraph;
import io.github.chronosx88.JGUN.models.Node;
import io.github.chronosx88.JGUN.models.NodeLink;
import io.github.chronosx88.JGUN.models.NodeMetadata;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.UUID;
public class GraphNodeBuilder {
private final MemoryGraph graph;
private final Node rootNode;
protected static final String ROOT_NODE = "__ROOT__";
public GraphNodeBuilder() {
this.graph = MemoryGraph.builder().build();
this.rootNode = Node.builder()
.metadata(NodeMetadata.builder()
.nodeID(null)
.build())
.build();
graph.nodes.put(ROOT_NODE, this.rootNode);
}
private GraphNodeBuilder addScalar(String name, Object value) {
rootNode.values.put(name, value);
rootNode.getMetadata().getStates().put(name, System.currentTimeMillis());
return this;
}
public GraphNodeBuilder add(String name, String value) {
return addScalar(name, value);
}
public GraphNodeBuilder add(String name, BigInteger value) {
return addScalar(name, value);
}
public GraphNodeBuilder add(String name, BigDecimal value) {
return addScalar(name, value);
}
public GraphNodeBuilder add(String name, int value) {
return addScalar(name, value);
}
public GraphNodeBuilder add(String name, long value) {
return addScalar(name, value);
}
public GraphNodeBuilder add(String name, double value) {
return addScalar(name, value);
}
public GraphNodeBuilder add(String name, boolean value) {
return addScalar(name, value);
}
public GraphNodeBuilder addNull(String name) {
return addScalar(name, null);
}
public GraphNodeBuilder add(String name, GraphNodeBuilder builder) {
String newNodeID = UUID.randomUUID().toString();
rootNode.values.put(name, NodeLink.builder()
.link(newNodeID)
.build());
MemoryGraph innerGraph = builder.build();
innerGraph.nodes.get(ROOT_NODE).getMetadata().setNodeID(newNodeID);
innerGraph.nodes.put(newNodeID, innerGraph.nodes.get(ROOT_NODE));
innerGraph.nodes.remove(ROOT_NODE);
graph.nodes.putAll(innerGraph.nodes);
return this;
}
public GraphNodeBuilder add(String name, NodeArrayBuilder builder) {
MemoryGraph innerGraph = builder.build();
//noinspection unchecked
var innerArray = (List<Object>) innerGraph.nodes.get(ROOT_NODE).values.get(NodeArrayBuilder.ARRAY_FIELD);
rootNode.values.put(name, innerArray);
rootNode.getMetadata().getStates().put(name, innerGraph
.nodes
.get(ROOT_NODE)
.getMetadata()
.getStates()
.get(NodeArrayBuilder.ARRAY_FIELD));
innerGraph.nodes.remove(ROOT_NODE);
graph.nodes.putAll(innerGraph.nodes);
return this;
}
public MemoryGraph build() {
return this.graph;
}
}

View File

@ -1,25 +1,34 @@
package io.github.chronosx88.JGUN;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import io.github.chronosx88.JGUN.futures.FuturePut;
import io.github.chronosx88.JGUN.models.MemoryGraph;
import io.github.chronosx88.JGUN.models.requests.PutRequest;
import io.github.chronosx88.JGUN.nodes.GunClient;
import io.github.chronosx88.JGUN.storage.Storage;
import java.net.InetAddress;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Gun {
private GunClient gunClient;
private GunClient peer;
private final Storage storage;
private final ObjectMapper objectMapper;
private final Executor executorService = Executors.newCachedThreadPool();
public Gun(InetAddress address, int port, Storage storage) {
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
this.storage = storage;
try {
this.gunClient = new GunClient(address, port, storage);
this.gunClient.connectBlocking();
this.peer = new GunClient(address, port, storage);
this.peer.connectBlocking();
} catch (URISyntaxException | InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@ -37,11 +46,27 @@ public class Gun {
storage.addMapChangeListener(nodeID, listener);
}
protected void sendPutRequest(MemoryGraph data) {
// TODO
protected FuturePut sendPutRequest(MemoryGraph data) {
String reqID = Dup.random();
executorService.execute(() -> {
storage.mergeUpdate(data);
var request = PutRequest.builder()
.id(reqID)
.graph(data)
.build();
String encodedRequest;
try {
encodedRequest = this.objectMapper.writeValueAsString(request);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
peer.emit(encodedRequest);
});
return new FuturePut(reqID);
}
protected void sendGetRequest(String key, String field) {
// TODO
throw new UnsupportedOperationException("TODO");
}
}

View File

@ -2,6 +2,7 @@ package io.github.chronosx88.JGUN;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import io.github.chronosx88.JGUN.futures.FutureGet;
import io.github.chronosx88.JGUN.futures.FuturePut;
import io.github.chronosx88.JGUN.futures.GetResult;
@ -31,12 +32,15 @@ public class NetworkHandler {
private final Storage storage;
private final Dup dup;
private final Executor executorService = Executors.newCachedThreadPool();
private final ObjectMapper objectMapper = new ObjectMapper();
private final ObjectMapper objectMapper;
public NetworkHandler(Storage storage, Peer peer, Dup dup) {
this.storage = storage;
this.peer = peer;
this.dup = dup;
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
}
public void addPendingGetRequest(FutureGet future) {

View File

@ -0,0 +1,88 @@
package io.github.chronosx88.JGUN;
import io.github.chronosx88.JGUN.models.MemoryGraph;
import io.github.chronosx88.JGUN.models.Node;
import io.github.chronosx88.JGUN.models.NodeLink;
import io.github.chronosx88.JGUN.models.NodeMetadata;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
public class NodeArrayBuilder {
private final MemoryGraph graph;
private final Node rootNode;
private final List<Object> innerArray;
protected static final String ARRAY_FIELD = "__ARRAY__";
public NodeArrayBuilder() {
this.graph = MemoryGraph.builder().build();
this.innerArray = new ArrayList<>();
this.rootNode = Node.builder()
.metadata(NodeMetadata.builder()
.nodeID(null)
.states(new HashMap<>(Map.of(ARRAY_FIELD, System.currentTimeMillis())))
.build())
.values(Map.of(ARRAY_FIELD, innerArray))
.build();
graph.nodes.put(GraphNodeBuilder.ROOT_NODE, this.rootNode);
}
private NodeArrayBuilder addScalar(Object value) {
this.innerArray.add(value);
this.rootNode.getMetadata().getStates().put(ARRAY_FIELD, System.currentTimeMillis());
return this;
}
public NodeArrayBuilder add(String value) {
return addScalar(value);
}
public NodeArrayBuilder add(BigInteger value) {
return addScalar(value);
}
public NodeArrayBuilder add(BigDecimal value) {
return addScalar(value);
}
public NodeArrayBuilder add(int value) {
return addScalar(value);
}
public NodeArrayBuilder add(long value) {
return addScalar(value);
}
public NodeArrayBuilder add(double value) {
return addScalar(value);
}
public NodeArrayBuilder add(boolean value) {
return addScalar(value);
}
public NodeArrayBuilder addNull(String name) {
return addScalar(null);
}
public NodeArrayBuilder add(GraphNodeBuilder builder) {
String newNodeID = UUID.randomUUID().toString();
//noinspection unchecked
List<Object> innerArray = (List<Object>) rootNode.values.get(ARRAY_FIELD);
innerArray.add(NodeLink.builder()
.link(newNodeID)
.build());
MemoryGraph innerGraph = builder.build();
innerGraph.nodes.get(GraphNodeBuilder.ROOT_NODE).getMetadata().setNodeID(newNodeID);
innerGraph.nodes.put(newNodeID, innerGraph.nodes.get(GraphNodeBuilder.ROOT_NODE));
innerGraph.nodes.remove(GraphNodeBuilder.ROOT_NODE);
this.graph.nodes.putAll(innerGraph.nodes);
return this;
}
public MemoryGraph build() {
return this.graph;
}
}

View File

@ -2,15 +2,15 @@ package io.github.chronosx88.JGUN;
import io.github.chronosx88.JGUN.futures.FutureGet;
import io.github.chronosx88.JGUN.futures.FuturePut;
import io.github.chronosx88.JGUN.nodes.GunClient;
import io.github.chronosx88.JGUN.models.MemoryGraph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class PathReference {
private final ArrayList<String> path = new ArrayList<>();
private final List<String> path = new ArrayList<>();
private Gun gun;
private final Gun gun;
public PathReference(Gun gun) {
this.gun = gun;
@ -21,14 +21,13 @@ public class PathReference {
return this;
}
public FutureGet getData() {
public FutureGet once() {
// TODO
return null;
throw new UnsupportedOperationException("TODO");
}
public FuturePut put(HashMap<String, Object> data) {
// TODO
return null;
public FuturePut put(MemoryGraph graph) {
return gun.sendPutRequest(graph);
}
public void on(NodeChangeListener changeListener) {

View File

@ -1,10 +1,8 @@
package io.github.chronosx88.JGUN.futures;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.CompletableFuture;
import lombok.Getter;
import java.util.concurrent.CompletableFuture;
@Getter
public class BaseCompletableFuture<T> extends CompletableFuture<T> {
@ -14,24 +12,4 @@ public class BaseCompletableFuture<T> extends CompletableFuture<T> {
super();
futureID = id;
}
public void addListener(final BaseFutureListener<T> listener) {
this.whenCompleteAsync((t, throwable) -> {
if(throwable == null) {
listener.onComplete(t);
} else {
listener.onError(t, throwable);
}
});
}
public T await() {
T t = null;
try {
t = super.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return t;
}
}

View File

@ -1,6 +0,0 @@
package io.github.chronosx88.JGUN.futures;
public interface BaseFutureListener<T> {
void onComplete(T result);
void onError(T result, Throwable exception);
}

View File

@ -0,0 +1,14 @@
package io.github.chronosx88.JGUN.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Data;
import lombok.extern.jackson.Jacksonized;
@Data
@Builder
@Jacksonized
public class NodeLink {
@JsonProperty("#")
String link;
}

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.extern.jackson.Jacksonized;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
@ -14,7 +15,7 @@ import java.util.Map;
public class NodeMetadata {
@JsonProperty(">")
@Builder.Default
private Map<String, Long> states = new HashMap<>(); // field -> state
private Map<String, Long> states = new LinkedHashMap<>(); // field -> state
@JsonProperty("#")
private String nodeID;

View File

@ -5,10 +5,11 @@ import io.github.chronosx88.JGUN.models.BaseMessage;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@Data
@Builder
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
@Jacksonized
public class GetRequest extends BaseMessage {

View File

@ -7,11 +7,12 @@ import io.github.chronosx88.JGUN.models.Node;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
@Data
@Jacksonized
@Builder
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
public class PutRequest extends BaseMessage {
@JsonProperty("put")

View File

@ -2,6 +2,8 @@ package io.github.chronosx88.JGUN.nodes;
import io.github.chronosx88.JGUN.Dup;
import io.github.chronosx88.JGUN.NetworkHandler;
import io.github.chronosx88.JGUN.futures.FutureGet;
import io.github.chronosx88.JGUN.futures.FuturePut;
import io.github.chronosx88.JGUN.storage.Storage;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
@ -44,4 +46,19 @@ public class GunClient extends WebSocketClient implements Peer {
public void emit(String data) {
this.send(data);
}
@Override
public void addPendingPutRequest(FuturePut futurePut) {
this.handler.addPendingPutRequest(futurePut);
}
@Override
public void addPendingGetRequest(FutureGet futureGet) {
this.handler.addPendingGetRequest(futureGet);
}
@Override
public void start() {
this.connect();
}
}

View File

@ -1,4 +0,0 @@
package io.github.chronosx88.JGUN.nodes;
public class GunNodeBuilder {
}

View File

@ -1,5 +1,12 @@
package io.github.chronosx88.JGUN.nodes;
import io.github.chronosx88.JGUN.futures.BaseCompletableFuture;
import io.github.chronosx88.JGUN.futures.FutureGet;
import io.github.chronosx88.JGUN.futures.FuturePut;
public interface Peer {
void emit(String data);
void addPendingPutRequest(FuturePut futurePut);
void addPendingGetRequest(FutureGet futureGet);
void start();
}

View File

@ -36,7 +36,7 @@ public abstract class Storage {
*/
public void mergeUpdate(MemoryGraph update) {
long machine = System.currentTimeMillis();
MemoryGraph diff = new MemoryGraph();
MemoryGraph diff = MemoryGraph.builder().build();
for (Map.Entry<String, Node> entry : update.getNodes().entrySet()) {
Node node = entry.getValue();
Node diffNode = this.mergeNode(node, machine);

View File

@ -0,0 +1,63 @@
package io.github.chronosx88.JGUN;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import org.junit.jupiter.api.Test;
class GraphNodeBuilderTest {
@Test
void Test_sampleGraph1() {
var objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
var graph = new GraphNodeBuilder()
.add("firstName", "John")
.add("lastName", "Smith")
.add("age", 25)
.add("address", new GraphNodeBuilder()
.add("streetAddress", "21 2nd Street")
.add("city", "New York")
.add("state", "NY")
.add("postalCode", "10021"))
.add("phoneNumber", new NodeArrayBuilder()
.add(new GraphNodeBuilder()
.add("type", "home")
.add("number", "212 555-1234"))
.add(new GraphNodeBuilder()
.add("type", "fax")
.add("number", "646 555-4567")))
.build();
String graphJSON = null;
try {
graphJSON = objectMapper.writeValueAsString(graph);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
System.out.println(graphJSON);
}
@Test
void Test_sampleGraph2() {
var objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
var graph = new GraphNodeBuilder()
.add("a", new NodeArrayBuilder()
.add(new GraphNodeBuilder()
.add("b", new GraphNodeBuilder()
.add("c", true)))
.add(0))
.build();
String graphJSON = null;
try {
graphJSON = objectMapper.writeValueAsString(graph);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
System.out.println(graphJSON);
}
}