use of io.datatree.Tree in project moleculer-java by moleculer-java.
the class DefaultServiceRegistry method ping.
// --- PING / PONG HANDLING ---
@Override
public Promise ping(long timeoutMillis, String nodeID) {
// Create new promise
Promise promise = new Promise();
// Set timeout
long timeoutAt;
if (timeoutMillis > 0) {
timeoutAt = System.currentTimeMillis() + timeoutMillis;
} else {
timeoutAt = 0;
}
// Register promise (timeout and response handling)
String id = uid.nextUID();
register(id, promise, timeoutAt);
// Send request via transporter
Tree message = transporter.createPingPacket(id);
transporter.publish(Transporter.PACKET_PING, nodeID, message);
// Return promise
return promise;
}
use of io.datatree.Tree in project moleculer-java by moleculer-java.
the class DefaultServiceRegistry method receiveResponse.
// --- RECEIVE RESPONSE FROM REMOTE SERVICE ---
@Override
public void receiveResponse(Tree message) {
// Verify protocol version
if (checkVersion) {
String ver = message.get("ver", "unknown");
if (!ServiceBroker.PROTOCOL_VERSION.equals(ver)) {
logger.warn("Invalid protocol version (" + ver + ")!");
return;
}
}
// Get response's unique ID
String id = message.get("id", (String) null);
if (id == null || id.isEmpty()) {
logger.warn("Missing \"id\" property!", message);
return;
}
// Get stored promise
PendingPromise pending = promises.remove(id);
if (pending == null) {
logger.warn("Unknown (maybe timeouted) response received!", message);
return;
}
try {
// Get response status (successed or not?)
boolean success = message.get("success", true);
if (success) {
// Ok -> resolve
pending.promise.complete(message.get("data"));
} else {
// Failed -> reject
Tree error = message.get("error");
String errorMessage = null;
String trace = null;
if (error != null) {
errorMessage = error.get("message", (String) null);
trace = error.get("trace", (String) null);
if (trace != null && !trace.isEmpty()) {
logger.error("Remote invaction failed!\r\n" + trace);
}
}
if (errorMessage == null || errorMessage.isEmpty()) {
errorMessage = "Unknow error!";
}
if (trace == null || trace.isEmpty()) {
logger.error("Remote invoction failed (unknown error occured)!");
}
pending.promise.complete(new RemoteException(errorMessage));
return;
}
} catch (Throwable cause) {
logger.error("Unable to pass on incoming response!", cause);
}
}
use of io.datatree.Tree in project moleculer-java by moleculer-java.
the class DefaultServiceRegistry method addOnlineActions.
protected void addOnlineActions(String serviceName, Service service) {
// Service name with version
if (serviceName == null || serviceName.isEmpty()) {
serviceName = service.getName();
}
serviceName = serviceName.replace(' ', '-');
Class<? extends Service> clazz = service.getClass();
Field[] fields = clazz.getFields();
int actionCounter = 0;
writeLock.lock();
try {
// Initialize actions in service
for (Field field : fields) {
if (!Action.class.isAssignableFrom(field.getType())) {
continue;
}
field.setAccessible(true);
Action action = (Action) field.get(service);
// Name of the action (eg. "service.action")
String actionName = nameOf(serviceName, field);
Tree actionConfig = new Tree();
actionConfig.put("name", actionName);
Annotation[] annotations = field.getAnnotations();
convertAnnotations(actionConfig, annotations);
// Register action
LocalActionEndpoint endpoint = new LocalActionEndpoint(nodeID, actionConfig, action);
Strategy<ActionEndpoint> actionStrategy = strategies.get(actionName);
if (actionStrategy == null) {
// Create strategy
actionStrategy = strategyFactory.create();
strategies.put(actionName, actionStrategy);
}
actionStrategy.addEndpoint(endpoint);
// Apply middlewares
for (Middleware middleware : middlewares) {
endpoint.use(middleware);
}
// Write log about this action
logger.info("Action \"" + actionName + "\" registered.");
actionCounter++;
}
services.put(serviceName, service);
service.started(broker);
} catch (Exception cause) {
logger.error("Unable to register local service!", cause);
return;
} finally {
// Delete cached node descriptor
clearDescriptorCache();
writeLock.unlock();
}
// Notify local listeners about the new LOCAL service
broadcastServicesChanged(true);
// Write log about this service
StringBuilder msg = new StringBuilder(64);
msg.append("Service \"");
msg.append(serviceName);
msg.append("\" started ");
if (actionCounter == 0) {
msg.append("without any actions.");
} else if (actionCounter == 1) {
msg.append("with 1 action.");
} else {
msg.append("with ");
msg.append(actionCounter);
msg.append(" actions.");
}
logger.info(msg.toString());
}
use of io.datatree.Tree in project moleculer-java by moleculer-java.
the class DefaultServiceRegistry method broadcastServicesChanged.
// --- NOTIFY OTHER SERVICES ---
protected void broadcastServicesChanged(boolean local) {
Tree message = new Tree();
message.put("localService", true);
eventbus.broadcast("$services.changed", message, null, true);
}
use of io.datatree.Tree in project moleculer-java by moleculer-java.
the class TcpTransporter method processGossipRequest.
// --- GOSSIP REQUEST MESSAGE RECEIVED ---
protected Tree processGossipRequest(Tree data) throws Exception {
// Debug
String sender = data.get("sender", (String) null);
if (debug) {
logger.info("Gossip request received from \"" + sender + "\" node:\r\n" + data);
}
// Add "online" and "offline" response blocks
LinkedList<NodeDescriptor> allNodes = new LinkedList<>(nodes.values());
NodeDescriptor descriptor = getDescriptor();
allNodes.add(descriptor);
int size = allNodes.size() + 1;
FastBuildTree onlineRsp = new FastBuildTree(size);
FastBuildTree offlineRsp = new FastBuildTree(size);
// Online / offline nodes in request
Tree onlineReq = data.get("online");
Tree offlineReq = data.get("offline");
// Loop in nodes
LinkedList<NodeDescriptor> disconnectedNodes = new LinkedList<>();
for (NodeDescriptor node : allNodes) {
node.writeLock.lock();
try {
Tree online = onlineReq == null ? null : onlineReq.get(node.nodeID);
Tree offline = offlineReq == null ? null : offlineReq.get(node.nodeID);
// Online or offline sequence number
long seq = 0;
// CPU data
long cpuSeq = 0;
int cpu = 0;
if (offline != null) {
if (!offline.isPrimitive()) {
logger.warn("Invalid \"offline\" block: " + offline.toString(false));
continue;
}
seq = offline.asLong();
} else if (online != null) {
if (!online.isEnumeration() || online.size() != 3) {
logger.warn("Invalid \"online\" block: " + online.toString(false));
continue;
}
seq = online.get(0).asLong();
cpuSeq = online.get(1).asLong();
cpu = online.get(2).asInteger();
}
if ((seq == 0 || seq < node.seq) && node.seq > 0) {
// We have newer info or requester doesn't know it
if (node.offlineSince == 0) {
if (!node.info.isEmpty()) {
if ((cpuSeq == 0 || cpuSeq < node.cpuSeq) && node.cpuSeq > 0) {
ArrayList<Object> array = new ArrayList<>(3);
array.add(node.info.asObject());
array.add(node.cpuSeq);
array.add(node.cpu);
onlineRsp.putUnsafe(node.nodeID, array);
} else {
onlineRsp.putUnsafe(node.nodeID, Collections.singletonList(node.info.asObject()));
}
}
} else {
offlineRsp.putUnsafe(node.nodeID, node.seq);
}
}
if (offline != null) {
// Requester said it is OFFLINE
if (node.offlineSince > 0) {
// We also knew it as offline
node.markAsOffline(seq);
continue;
}
if (!node.local) {
if (node.offlineSince == 0) {
// We know it is online, so we change it to offline
if (node.markAsOffline(seq)) {
// Remove remote actions and listeners
registry.removeActions(node.nodeID);
eventbus.removeListeners(node.nodeID);
writer.close(node.nodeID);
disconnectedNodes.add(node);
} else if (seq == node.seq) {
// We send back that this node is online
node.seq = seq + 1;
node.info.put("seq", node.seq);
if (cpuSeq < node.cpuSeq && node.cpuSeq > 0) {
ArrayList<Object> array = new ArrayList<>(3);
array.add(node.info.asObject());
array.add(node.cpuSeq);
array.add(node.cpu);
onlineRsp.putUnsafe(node.nodeID, array);
} else {
onlineRsp.putUnsafe(node.nodeID, Collections.singletonList(node.info.asObject()));
}
}
}
continue;
}
} else if (online != null) {
// Requester said it is ONLINE
if (node.offlineSince == 0) {
if (cpuSeq > node.cpuSeq) {
// We update our CPU info
node.updateCpu(cpuSeq, cpu);
} else if (cpuSeq < node.cpuSeq && node.cpuSeq > 0) {
// We have newer CPU value, send back
ArrayList<Object> array = new ArrayList<>(2);
array.add(node.cpuSeq);
array.add(node.cpu);
onlineRsp.putUnsafe(node.nodeID, array);
}
} else {
// request it and we'll receive its INFO
continue;
}
}
} finally {
node.writeLock.unlock();
}
}
// Create gossip response
FastBuildTree root = new FastBuildTree(4);
root.putUnsafe("ver", ServiceBroker.PROTOCOL_VERSION);
root.putUnsafe("sender", nodeID);
// Remove empty blocks
boolean emptyOnlineBlock = onlineRsp.isEmpty();
boolean emptyOfflineBlock = offlineRsp.isEmpty();
if (emptyOnlineBlock && emptyOfflineBlock) {
// Message is empty
return root;
}
if (!emptyOnlineBlock) {
root.putUnsafe("online", onlineRsp.asObject());
}
if (!emptyOfflineBlock) {
root.putUnsafe("offline", offlineRsp.asObject());
}
// Debug
if (debug) {
logger.info("Gossip response submitting to \"" + sender + "\" node:\r\n" + root);
}
// Serialize response
byte[] packet = serialize(PACKET_GOSSIP_RSP_ID, root);
// Send response
writer.send(sender, packet);
// Notify listeners (unexpected disconnection)
for (NodeDescriptor node : disconnectedNodes) {
logger.info("Node \"" + node.nodeID + "\" disconnected.");
broadcastNodeDisconnected(node.info, true);
}
// For unit testing
return root;
}
Aggregations