Added /websockets/admin/status and improved GET version.

NodeStatus contructor now fills in fields, which themselves are now 'final'.
NodeStatus also includes numberOfConnections and height as per systray.

AdminResource.status() unified with websocket version.
This commit is contained in:
catbref 2020-06-24 11:54:06 +01:00
parent 448e984995
commit e2e4555009
6 changed files with 137 additions and 9 deletions

View File

@ -40,6 +40,7 @@ import org.glassfish.jersey.servlet.ServletContainer;
import org.qortal.api.resource.AnnotationPostProcessor;
import org.qortal.api.resource.ApiDefinition;
import org.qortal.api.websocket.ActiveChatsWebSocket;
import org.qortal.api.websocket.AdminStatusWebSocket;
import org.qortal.api.websocket.BlocksWebSocket;
import org.qortal.api.websocket.ChatMessagesWebSocket;
import org.qortal.settings.Settings;
@ -192,6 +193,7 @@ public class ApiService {
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/")); // redirect to add trailing slash if missing
}
context.addServlet(AdminStatusWebSocket.class, "/websockets/admin/status");
context.addServlet(BlocksWebSocket.class, "/websockets/blocks");
context.addServlet(ActiveChatsWebSocket.class, "/websockets/chat/active/*");
context.addServlet(ChatMessagesWebSocket.class, "/websockets/chat/messages");

View File

@ -3,16 +3,34 @@ package org.qortal.api.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import org.qortal.controller.Controller;
import org.qortal.network.Network;
@XmlAccessorType(XmlAccessType.FIELD)
public class NodeStatus {
public boolean isMintingPossible;
public boolean isSynchronizing;
public final boolean isMintingPossible;
public final boolean isSynchronizing;
// Not always present
public Integer syncPercent;
public final Integer syncPercent;
public final int numberOfConnections;
public final int height;
public NodeStatus() {
isMintingPossible = Controller.getInstance().isMintingPossible();
isSynchronizing = Controller.getInstance().isSynchronizing();
if (isSynchronizing)
syncPercent = Controller.getInstance().getSyncPercent();
else
syncPercent = null;
numberOfConnections = Network.getInstance().getHandshakedPeers().size();
height = Controller.getInstance().getChainHeight();
}
}

View File

@ -137,12 +137,6 @@ public class AdminResource {
NodeStatus nodeStatus = new NodeStatus();
nodeStatus.isMintingPossible = Controller.getInstance().isMintingPossible();
nodeStatus.isSynchronizing = Controller.getInstance().isSynchronizing();
if (nodeStatus.isSynchronizing)
nodeStatus.syncPercent = Controller.getInstance().getSyncPercent();
return nodeStatus;
}

View File

@ -0,0 +1,68 @@
package org.qortal.api.websocket;
import java.io.IOException;
import java.io.StringWriter;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.qortal.api.model.NodeStatus;
import org.qortal.controller.StatusNotifier;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
@WebSocket
@SuppressWarnings("serial")
public class AdminStatusWebSocket extends WebSocketServlet implements ApiWebSocket {
@Override
public void configure(WebSocketServletFactory factory) {
factory.register(AdminStatusWebSocket.class);
}
@OnWebSocketConnect
public void onWebSocketConnect(Session session) {
AtomicReference<String> previousOutput = new AtomicReference<>(null);
StatusNotifier.Listener listener = timestamp -> onNotify(session, previousOutput);
StatusNotifier.getInstance().register(session, listener);
this.onNotify(session, previousOutput);
}
@OnWebSocketClose
public void onWebSocketClose(Session session, int statusCode, String reason) {
StatusNotifier.getInstance().deregister(session);
}
@OnWebSocketMessage
public void onWebSocketMessage(Session session, String message) {
}
private void onNotify(Session session,AtomicReference<String> previousOutput) {
try (final Repository repository = RepositoryManager.getRepository()) {
NodeStatus nodeStatus = new NodeStatus();
StringWriter stringWriter = new StringWriter();
this.marshall(stringWriter, nodeStatus);
// Only output if something has changed
String output = stringWriter.toString();
if (output.equals(previousOutput.get()))
return;
previousOutput.set(output);
session.getRemote().sendString(output);
} catch (DataException | IOException e) {
// No output this time?
}
}
}

View File

@ -647,6 +647,10 @@ public class Controller extends Thread {
String tooltip = String.format("%s - %d %s - %s %d", actionText, numberOfPeers, connectionsText, heightText, height);
SysTray.getInstance().setToolTipText(tooltip);
this.callbackExecutor.execute(() -> {
StatusNotifier.getInstance().onStatusChange(NTP.getTime());
});
}
public void deleteExpiredTransactions() {

View File

@ -0,0 +1,42 @@
package org.qortal.controller;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.websocket.api.Session;
public class StatusNotifier {
private static StatusNotifier instance;
@FunctionalInterface
public interface Listener {
void notify(long timestamp);
}
private Map<Session, Listener> listenersBySession = new HashMap<>();
private StatusNotifier() {
}
public static synchronized StatusNotifier getInstance() {
if (instance == null)
instance = new StatusNotifier();
return instance;
}
public synchronized void register(Session session, Listener listener) {
this.listenersBySession.put(session, listener);
}
public synchronized void deregister(Session session) {
this.listenersBySession.remove(session);
}
public synchronized void onStatusChange(long now) {
for (Listener listener : this.listenersBySession.values())
listener.notify(now);
}
}