mirror of
https://github.com/ChronosX88/JGUN.git
synced 2024-11-21 14:02:19 +00:00
Implement updating nodes for MemoryStorage, handling deferred nodes
This commit is contained in:
parent
3338c54680
commit
1eb00914a8
@ -11,7 +11,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
public class Gun {
|
public class Gun {
|
||||||
private GunClient gunClient;
|
private GunClient gunClient;
|
||||||
private final Map<String, NodeChangeListener> changeListeners = new ConcurrentHashMap<>();
|
private final Map<String, NodeChangeListener> changeListeners = new ConcurrentHashMap<>();
|
||||||
private final Map<String, NodeChangeListener.ForEach> forEachListeners = new ConcurrentHashMap<>();
|
private final Map<String, NodeChangeListener.Map> mapChangeListeners = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public Gun(InetAddress address, int port, Storage storage) {
|
public Gun(InetAddress address, int port, Storage storage) {
|
||||||
try {
|
try {
|
||||||
@ -32,7 +32,7 @@ public class Gun {
|
|||||||
changeListeners.put(nodeID, listener);
|
changeListeners.put(nodeID, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addForEachChangeListener(String nodeID, NodeChangeListener.ForEach listener) {
|
protected void addMapChangeListener(String nodeID, NodeChangeListener.Map listener) {
|
||||||
forEachListeners.put(nodeID, listener);
|
mapChangeListeners.put(nodeID, listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import io.github.chronosx88.JGUN.models.Node;
|
|||||||
public interface NodeChangeListener {
|
public interface NodeChangeListener {
|
||||||
void onChange(Node node);
|
void onChange(Node node);
|
||||||
|
|
||||||
interface ForEach {
|
interface Map {
|
||||||
void onChange(String key, Object value);
|
void onChange(String key, Object value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ public class PathReference {
|
|||||||
database.addChangeListener(String.join("/", path), changeListener);
|
database.addChangeListener(String.join("/", path), changeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void map(NodeChangeListener.ForEach forEachListener) {
|
public void map(NodeChangeListener.Map forEachListener) {
|
||||||
database.addForEachChangeListener(String.join("/", path), forEachListener);
|
database.addMapChangeListener(String.join("/", path), forEachListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package io.github.chronosx88.JGUN.models;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Delayed;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class DeferredNode extends Node implements Delayed {
|
||||||
|
private long deferredUntil = 0;
|
||||||
|
|
||||||
|
DeferredNode(NodeMetadata metadata, Map<String, Object> values) {
|
||||||
|
super(metadata, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDelay(TimeUnit timeUnit) {
|
||||||
|
long delay = deferredUntil - System.currentTimeMillis();
|
||||||
|
return timeUnit.convert(delay, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDelay(long delayDuration) {
|
||||||
|
this.deferredUntil = System.currentTimeMillis() + delayDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Delayed delayed) {
|
||||||
|
return Math.toIntExact(this.deferredUntil - ((DeferredNode) delayed).deferredUntil);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,18 +1,43 @@
|
|||||||
package io.github.chronosx88.JGUN.storage;
|
package io.github.chronosx88.JGUN.storage;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.Expiry;
|
||||||
|
import io.github.chronosx88.JGUN.models.DeferredNode;
|
||||||
import io.github.chronosx88.JGUN.models.Node;
|
import io.github.chronosx88.JGUN.models.Node;
|
||||||
|
import org.checkerframework.checker.index.qual.NonNegative;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class MemoryStorage extends Storage {
|
public class MemoryStorage extends Storage {
|
||||||
private final Map<String, Node> nodes;
|
private final Map<String, Node> nodes = new ConcurrentHashMap<>();
|
||||||
|
private final Cache<String, DeferredNode> deferredNodes;
|
||||||
|
|
||||||
public MemoryStorage() {
|
public MemoryStorage() {
|
||||||
nodes = new LinkedHashMap<>();
|
deferredNodes = Caffeine.newBuilder().expireAfter(new Expiry<String, DeferredNode>() {
|
||||||
|
@Override
|
||||||
|
public long expireAfterCreate(String key, DeferredNode value, long currentTime) {
|
||||||
|
return value.getDelay(TimeUnit.NANOSECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long expireAfterUpdate(String key, DeferredNode value, long currentTime, @NonNegative long currentDuration) {
|
||||||
|
return Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long expireAfterRead(String key, DeferredNode value, long currentTime, @NonNegative long currentDuration) {
|
||||||
|
return Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.evictionListener((key, value, cause) -> {
|
||||||
|
assert value != null;
|
||||||
|
this.mergeNode(value, System.currentTimeMillis());
|
||||||
|
}).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Node getNode(String id) {
|
public Node getNode(String id) {
|
||||||
@ -21,8 +46,9 @@ public class MemoryStorage extends Storage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void updateNode(Node node) {
|
void updateNode(Node node) {
|
||||||
// TODO
|
Node currentNode = nodes.get(node.getMetadata().getNodeID());
|
||||||
throw new UnsupportedOperationException("TODO");
|
currentNode.values.putAll(node.values);
|
||||||
|
currentNode.getMetadata().getStates().putAll(node.getMetadata().getStates());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addNode(String id, Node incomingNode) {
|
public void addNode(String id, Node incomingNode) {
|
||||||
@ -44,4 +70,9 @@ public class MemoryStorage extends Storage {
|
|||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return nodes.isEmpty();
|
return nodes.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void putDeferredNode(DeferredNode node) {
|
||||||
|
deferredNodes.put(node.getMetadata().getNodeID(), node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,28 +2,41 @@ package io.github.chronosx88.JGUN.storage;
|
|||||||
|
|
||||||
import io.github.chronosx88.JGUN.HAM;
|
import io.github.chronosx88.JGUN.HAM;
|
||||||
import io.github.chronosx88.JGUN.NodeChangeListener;
|
import io.github.chronosx88.JGUN.NodeChangeListener;
|
||||||
|
import io.github.chronosx88.JGUN.models.DeferredNode;
|
||||||
import io.github.chronosx88.JGUN.models.MemoryGraph;
|
import io.github.chronosx88.JGUN.models.MemoryGraph;
|
||||||
import io.github.chronosx88.JGUN.models.Node;
|
import io.github.chronosx88.JGUN.models.Node;
|
||||||
import io.github.chronosx88.JGUN.models.NodeMetadata;
|
import io.github.chronosx88.JGUN.models.NodeMetadata;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public abstract class Storage {
|
public abstract class Storage {
|
||||||
abstract Node getNode(String id);
|
abstract Node getNode(String id);
|
||||||
|
|
||||||
abstract void updateNode(Node node);
|
abstract void updateNode(Node node);
|
||||||
|
|
||||||
abstract void addNode(String id, Node node);
|
abstract void addNode(String id, Node node);
|
||||||
|
|
||||||
abstract boolean hasNode(String id);
|
abstract boolean hasNode(String id);
|
||||||
|
|
||||||
abstract Set<Map.Entry<String, Node>> entries();
|
abstract Set<Map.Entry<String, Node>> entries();
|
||||||
|
|
||||||
abstract Collection<Node> nodes();
|
abstract Collection<Node> nodes();
|
||||||
|
|
||||||
abstract boolean isEmpty();
|
abstract boolean isEmpty();
|
||||||
|
|
||||||
|
abstract void putDeferredNode(DeferredNode node);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge graph update (usually received from the network)
|
* Merge graph update (usually received from the network)
|
||||||
* @param update Graph update
|
*
|
||||||
* @param changeListeners
|
* @param update Graph update
|
||||||
* @param forEachListeners
|
* @param changeListeners User callbacks which fired when Node has changed (.on() API)
|
||||||
|
* @param mapChangeListeners User callbacks which fired when Node has changed (.map() API)
|
||||||
*/
|
*/
|
||||||
public void mergeUpdate(MemoryGraph update, Map<String, NodeChangeListener> changeListeners, Map<String, NodeChangeListener.ForEach> forEachListeners) {
|
public void mergeUpdate(MemoryGraph update, Map<String, NodeChangeListener> changeListeners, Map<String, NodeChangeListener.Map> mapChangeListeners) {
|
||||||
long machine = System.currentTimeMillis();
|
long machine = System.currentTimeMillis();
|
||||||
MemoryGraph diff = new MemoryGraph();
|
MemoryGraph diff = new MemoryGraph();
|
||||||
for (Map.Entry<String, Node> entry : update.getNodes().entrySet()) {
|
for (Map.Entry<String, Node> entry : update.getNodes().entrySet()) {
|
||||||
@ -45,9 +58,9 @@ public abstract class Storage {
|
|||||||
if (changeListeners.containsKey(diffEntry.getKey())) {
|
if (changeListeners.containsKey(diffEntry.getKey())) {
|
||||||
changeListeners.get(diffEntry.getKey()).onChange(diffEntry.getValue());
|
changeListeners.get(diffEntry.getKey()).onChange(diffEntry.getValue());
|
||||||
}
|
}
|
||||||
if (forEachListeners.containsKey(diffEntry.getKey())) {
|
if (mapChangeListeners.containsKey(diffEntry.getKey())) {
|
||||||
for (Map.Entry<String, Object> nodeEntry : changedNode.getValues().entrySet()) {
|
for (Map.Entry<String, Object> nodeEntry : changedNode.getValues().entrySet()) {
|
||||||
forEachListeners.get(nodeEntry.getKey()).onChange(nodeEntry.getKey(), nodeEntry.getValue());
|
mapChangeListeners.get(nodeEntry.getKey()).onChange(nodeEntry.getKey(), nodeEntry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,6 +69,7 @@ public abstract class Storage {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge updated node
|
* Merge updated node
|
||||||
|
*
|
||||||
* @param incomingNode Updated node
|
* @param incomingNode Updated node
|
||||||
* @param machineState Current machine state
|
* @param machineState Current machine state
|
||||||
* @return Node with changes or null if no changes
|
* @return Node with changes or null if no changes
|
||||||
@ -79,7 +93,9 @@ public abstract class Storage {
|
|||||||
|
|
||||||
if (!ham.incoming) {
|
if (!ham.incoming) {
|
||||||
if (ham.defer) {
|
if (ham.defer) {
|
||||||
// TODO handle deferred value
|
DeferredNode deferred = (DeferredNode) incomingNode;
|
||||||
|
deferred.setDelay(state - machineState);
|
||||||
|
this.putDeferredNode(deferred);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user