Initial commit

This commit is contained in:
Christopher Schnick
2021-12-01 19:17:54 +01:00
commit 63cdfb40c5
92 changed files with 3882 additions and 0 deletions
@@ -0,0 +1,8 @@
package io.xpipe.beacon;
public class ClientException extends Exception {
public ClientException(String message) {
super(message);
}
}
@@ -0,0 +1,23 @@
package io.xpipe.beacon;
public class ConnectorException extends Exception {
public ConnectorException() {
}
public ConnectorException(String message) {
super(message);
}
public ConnectorException(String message, Throwable cause) {
super(message, cause);
}
public ConnectorException(Throwable cause) {
super(cause);
}
public ConnectorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,8 @@
package io.xpipe.beacon;
public class ServerException extends Exception {
public ServerException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,59 @@
package io.xpipe.beacon;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.socket.SocketClient;
import org.apache.commons.lang3.function.FailableBiConsumer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.function.Consumer;
public abstract class XPipeConnector {
protected abstract void waitForStartup();
protected SocketClient constructSocket() throws ConnectorException {
if (!XPipeDaemon.isDaemonRunning()) {
try {
XPipeDaemon.startDaemon();
waitForStartup();
if (!XPipeDaemon.isDaemonRunning()) {
throw new ConnectorException("Unable to start xpipe daemon");
}
} catch (Exception ex) {
throw new ConnectorException("Unable to start xpipe daemon: " + ex.getMessage());
}
}
try {
return new SocketClient();
} catch (Exception ex) {
throw new ConnectorException("Unable to connect to running xpipe daemon: " + ex.getMessage());
}
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performExchange(
SocketClient socket,
REQ req,
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
boolean keepOpen) throws ServerException, ConnectorException, ClientException {
performExchange(socket, req, null, responseConsumer, keepOpen);
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performExchange(
SocketClient socket,
REQ req,
Consumer<OutputStream> output,
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
boolean keepOpen) throws ServerException, ConnectorException, ClientException {
socket.exchange(req, output, responseConsumer, keepOpen);
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> RES performSimpleExchange(
SocketClient socket,
REQ req) throws ServerException, ConnectorException, ClientException {
return socket.simpleExchange(req);
}
}
@@ -0,0 +1,58 @@
package io.xpipe.beacon;
import io.xpipe.app.Main;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.beacon.socket.SocketServer;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
public class XPipeDaemon {
private static final String IN_PROCESS_PROP = "io.xpipe.beacon.startInProcess";
public static Path getUserDir() {
return Path.of(System.getProperty("user.home"), ".xpipe");
}
private static boolean isPortAvailable(int port) {
try (var ss = new ServerSocket(port); var ds = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
return false;
}
}
public static boolean isDaemonRunning() {
var port = SocketServer.determineUsedPort();
return !isPortAvailable(port);
}
public static void startDaemon() throws Exception {
if (Optional.ofNullable(System.getProperty("io.xpipe.beacon.startInProcess"))
.map(Boolean::parseBoolean).orElse(false)) {
startInProcess();
return;
}
// if (System.getenv().containsKey(EXEC_PROPERTY)) {
// Runtime.getRuntime().exec(System.getenv(EXEC_PROPERTY));
// return;
// }
var file = getUserDir().resolve("run");
if (Files.exists(file)) {
Runtime.getRuntime().exec(Files.readString(file));
}
throw new IllegalArgumentException("Unable to find xpipe daemon installation");
}
private static void startInProcess() {
ThreadHelper.create("XPipe daemon", false, () -> Main.main(new String[0])).start();
}
}
@@ -0,0 +1,10 @@
package io.xpipe.beacon.message;
import io.xpipe.beacon.ClientException;
public record ClientErrorMessage(String message) {
public ClientException throwException() {
return new ClientException(message);
}
}
@@ -0,0 +1,17 @@
package io.xpipe.beacon.message;
import io.xpipe.beacon.socket.SocketServer;
import java.io.InputStream;
import java.net.Socket;
public interface MessageProvider<RQ extends RequestMessage, RP extends ResponseMessage> {
String getId();
Class<RQ> getRequestClass();
Class<RP> getResponseClass();
default void handleRequest(SocketServer server, RQ msg, InputStream body, Socket clientSocket) throws Exception {}
}
@@ -0,0 +1,42 @@
package io.xpipe.beacon.message;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
public class MessageProviders {
private static Set<MessageProvider> ALL;
private static void loadAll() {
if (ALL == null) {
ALL = ServiceLoader.load(MessageProvider.class).stream()
.map(ServiceLoader.Provider::get).collect(Collectors.toSet());
}
}
public static <RQ extends RequestMessage, RP extends ResponseMessage> Optional<MessageProvider<RQ, RP>> byId(String name) {
loadAll();
var r = ALL.stream().filter(d -> d.getId().equals(name)).findAny();
return Optional.ofNullable(r.orElse(null));
}
public static <RQ extends RequestMessage, RP extends ResponseMessage> Optional<MessageProvider<RQ, RP>> byRequest(RQ req) {
loadAll();
var r = ALL.stream().filter(d -> d.getRequestClass().equals(req.getClass())).findAny();
return Optional.ofNullable(r.orElse(null));
}
public static <RQ extends RequestMessage, RP extends ResponseMessage> Optional<MessageProvider<RQ, RP>> byResponse(RP rep) {
loadAll();
var r = ALL.stream().filter(d -> d.getResponseClass().equals(rep.getClass())).findAny();
return Optional.ofNullable(r.orElse(null));
}
public static Set<MessageProvider> getAll() {
loadAll();
return ALL;
}
}
@@ -0,0 +1,5 @@
package io.xpipe.beacon.message;
public interface RequestMessage {
}
@@ -0,0 +1,5 @@
package io.xpipe.beacon.message;
public interface ResponseMessage {
}
@@ -0,0 +1,12 @@
package io.xpipe.beacon.message;
import io.xpipe.beacon.ServerException;
import java.util.UUID;
public record ServerErrorMessage(UUID requestId, Throwable error) {
public void throwError() throws ServerException {
throw new ServerException(error.getMessage(), error);
}
}
@@ -0,0 +1,40 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import java.io.InputStream;
import java.net.Socket;
import java.util.List;
public class ListCollectionsExchange implements MessageProvider<ListCollectionsExchange.Request, ListCollectionsExchange.Response> {
@Override
public String getId() {
return "listCollections";
}
@Override
public Class<Request> getRequestClass() {
return Request.class;
}
@Override
public Class<Response> getResponseClass() {
return Response.class;
}
public static record Request() implements RequestMessage {
}
public static record Entry(String name, int count) {
}
public static record Response(List<Entry> entries) implements ResponseMessage {
}
}
@@ -0,0 +1,41 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.storage.DataSourceStorage;
import java.io.InputStream;
import java.net.Socket;
import java.util.List;
public class ListEntriesExchange implements MessageProvider<ListEntriesExchange.Request, ListEntriesExchange.Response> {
@Override
public String getId() {
return "listEntries";
}
@Override
public Class<Request> getRequestClass() {
return Request.class;
}
@Override
public Class<Response> getResponseClass() {
return Response.class;
}
public static record Request(String collection) implements RequestMessage {
}
private static record Entry(String name, String type, String description, String date, String size) {
}
public static record Response(List<Entry> entries) implements ResponseMessage {
}
}
@@ -0,0 +1,37 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.app.core.OperationMode;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import java.io.InputStream;
import java.net.Socket;
import java.util.stream.Collectors;
public class ModeExchange implements MessageProvider<ModeExchange.Request, ModeExchange.Response> {
@Override
public String getId() {
return "mode";
}
@Override
public Class<ModeExchange.Request> getRequestClass() {
return ModeExchange.Request.class;
}
@Override
public Class<ModeExchange.Response> getResponseClass() {
return ModeExchange.Response.class;
}
public static record Request(String modeId) implements RequestMessage {
}
public static record Response() implements ResponseMessage {
}
}
@@ -0,0 +1,37 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.storage.DataSourceStorage;
import java.io.InputStream;
import java.net.Socket;
public class ReadStructureExchange implements MessageProvider<ReadStructureExchange.Request, ReadStructureExchange.Response> {
@Override
public String getId() {
return "readStructure";
}
@Override
public Class<Request> getRequestClass() {
return Request.class;
}
@Override
public Class<Response> getResponseClass() {
return Response.class;
}
public static record Request(DataSourceId id) implements RequestMessage {
}
public static record Response() implements ResponseMessage {
}
}
@@ -0,0 +1,37 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.storage.DataSourceStorage;
import java.io.InputStream;
import java.net.Socket;
public class ReadTableDataExchange implements MessageProvider<ReadTableDataExchange.Request, ReadTableDataExchange.Response> {
@Override
public String getId() {
return "readTable";
}
@Override
public Class<ReadTableDataExchange.Request> getRequestClass() {
return ReadTableDataExchange.Request.class;
}
@Override
public Class<ReadTableDataExchange.Response> getResponseClass() {
return ReadTableDataExchange.Response.class;
}
public static record Request(DataSourceId sourceId, int maxLines) implements RequestMessage {
}
public static record Response() implements ResponseMessage {
}
}
@@ -0,0 +1,38 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.storage.DataSourceStorage;
import java.io.InputStream;
import java.net.Socket;
public class ReadTableInfoExchange implements MessageProvider<ReadTableInfoExchange.Request, ReadTableInfoExchange.Response> {
@Override
public String getId() {
return "readTableInfo";
}
@Override
public Class<ReadTableInfoExchange.Request> getRequestClass() {
return ReadTableInfoExchange.Request.class;
}
@Override
public Class<ReadTableInfoExchange.Response> getResponseClass() {
return ReadTableInfoExchange.Response.class;
}
public static record Request(DataSourceId sourceId) implements RequestMessage {
}
public static record Response(DataSourceId sourceId, DataType dataType, int rowCount) implements ResponseMessage {
}
}
@@ -0,0 +1,36 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.app.core.OperationMode;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import java.io.InputStream;
import java.net.Socket;
public class StatusExchange implements MessageProvider<StatusExchange.Request, StatusExchange.Response> {
@Override
public String getId() {
return "status";
}
@Override
public Class<Request> getRequestClass() {
return Request.class;
}
@Override
public Class<Response> getResponseClass() {
return Response.class;
}
public static record Request() implements RequestMessage {
}
public static record Response(String mode) implements ResponseMessage {
}
}
@@ -0,0 +1,43 @@
package io.xpipe.beacon.message.impl;
import io.xpipe.app.core.AppInstallation;
import io.xpipe.beacon.socket.SocketServer;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import java.io.InputStream;
import java.net.Socket;
public class VersionExchange implements MessageProvider<VersionExchange.Request, VersionExchange.Response> {
@Override
public String getId() {
return "version";
}
@Override
public Class<VersionExchange.Request> getRequestClass() {
return VersionExchange.Request.class;
}
@Override
public Class<VersionExchange.Response> getResponseClass() {
return VersionExchange.Response.class;
}
public static record Request() implements RequestMessage {
}
public static class Response implements ResponseMessage {
public final String version;
public final String jvmVersion;
public Response(String version, String jvmVersion) {
this.version = version;
this.jvmVersion = jvmVersion;
}
}
}
@@ -0,0 +1,200 @@
package io.xpipe.beacon.socket;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.xpipe.beacon.ClientException;
import io.xpipe.beacon.ConnectorException;
import io.xpipe.beacon.ServerException;
import io.xpipe.beacon.message.*;
import io.xpipe.core.util.JacksonHelper;
import org.apache.commons.lang3.function.FailableBiConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Consumer;
import static io.xpipe.beacon.socket.Sockets.BODY_SEPARATOR;
public class SocketClient {
private static final Logger logger = LoggerFactory.getLogger(SocketClient.class);
private final Socket socket;
private final InputStream in;
private final OutputStream out;
public SocketClient() throws IOException {
socket = new Socket(InetAddress.getLoopbackAddress(), SocketServer.determineUsedPort());
in = socket.getInputStream();
out = socket.getOutputStream();
}
public void close() throws ConnectorException {
try {
socket.close();
} catch (IOException ex) {
throw new ConnectorException("Couldn't close socket", ex);
}
}
public <REQ extends RequestMessage, RES extends ResponseMessage> void exchange(
REQ req,
Consumer<OutputStream> output,
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
boolean keepOpen) throws ConnectorException, ClientException, ServerException {
try {
sendRequest(req);
if (output != null) {
out.write(BODY_SEPARATOR);
output.accept(out);
}
var res = this.<RES>receiveResponse();
var sep = in.readNBytes(BODY_SEPARATOR.length);
if (!Arrays.equals(BODY_SEPARATOR, sep)) {
throw new ConnectorException("Invalid body separator");
}
responseConsumer.accept(res, in);
} catch (IOException ex) {
throw new ConnectorException("Couldn't communicate with socket", ex);
} finally {
if (!keepOpen) {
close();
}
}
}
public <REQ extends RequestMessage, RES extends ResponseMessage> RES simpleExchange(REQ req)
throws ServerException, ConnectorException, ClientException {
try {
sendRequest(req);
return this.receiveResponse();
} finally {
close();
}
}
private <T extends RequestMessage> void sendRequest(T req) throws ClientException, ConnectorException {
ObjectNode json = JacksonHelper.newMapper().valueToTree(req);
var prov = MessageProviders.byRequest(req);
if (prov.isEmpty()) {
throw new ClientException("Unknown request class " + req.getClass());
}
json.set("type", new TextNode(prov.get().getId()));
json.set("phase", new TextNode("request"));
//json.set("id", new TextNode(UUID.randomUUID().toString()));
var msg = JsonNodeFactory.instance.objectNode();
msg.set("xPipeMessage", json);
try {
var mapper = JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
var gen = mapper.createGenerator(socket.getOutputStream());
gen.writeTree(msg);
} catch (IOException ex) {
throw new ConnectorException("Couldn't write to socket", ex);
}
}
private <T extends ResponseMessage> T receiveResponse() throws ConnectorException, ClientException, ServerException {
JsonNode read;
try {
var in = socket.getInputStream();
read = JacksonHelper.newMapper().disable(JsonParser.Feature.AUTO_CLOSE_SOURCE).readTree(in);
} catch (IOException ex) {
throw new ConnectorException("Couldn't read from socket", ex);
}
if (Sockets.debugEnabled()) {
System.out.println("Recieved response:");
System.out.println(read.toPrettyString());
}
var se = parseServerError(read);
if (se.isPresent()) {
se.get().throwError();
}
var ce = parseClientError(read);
if (ce.isPresent()) {
throw ce.get().throwException();
}
return parseResponse(read);
}
private Optional<ClientErrorMessage> parseClientError(JsonNode node) throws ConnectorException {
ObjectNode content = (ObjectNode) node.get("xPipeClientError");
if (content == null) {
return Optional.empty();
}
try {
var reader = JacksonHelper.newMapper().readerFor(ClientErrorMessage.class);
return Optional.of(reader.readValue(content));
} catch (IOException ex) {
throw new ConnectorException("Couldn't parse client error message", ex);
}
}
private Optional<ServerErrorMessage> parseServerError(JsonNode node) throws ConnectorException {
ObjectNode content = (ObjectNode) node.get("xPipeServerError");
if (content == null) {
return Optional.empty();
}
try {
var reader = JacksonHelper.newMapper().readerFor(ServerErrorMessage.class);
return Optional.of(reader.readValue(content));
} catch (IOException ex) {
throw new ConnectorException("Couldn't parse server error message", ex);
}
}
private <T extends ResponseMessage> T parseResponse(JsonNode header) throws ConnectorException {
ObjectNode content = (ObjectNode) header.required("xPipeMessage");
var type = content.required("type").textValue();
var phase = content.required("phase").textValue();
//var requestId = UUID.fromString(content.required("id").textValue());
if (!phase.equals("response")) {
throw new IllegalArgumentException();
}
content.remove("type");
content.remove("phase");
//content.remove("id");
var prov = MessageProviders.byId(type);
if (prov.isEmpty()) {
throw new IllegalArgumentException("Unknown response id " + type);
}
try {
var reader = JacksonHelper.newMapper().readerFor(prov.get().getResponseClass());
return reader.readValue(content);
} catch (IOException ex) {
throw new ConnectorException("Couldn't parse response", ex);
}
}
public InputStream getInputStream() {
return in;
}
public OutputStream getOutputStream() {
return out;
}
}
@@ -0,0 +1,186 @@
package io.xpipe.beacon.socket;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.xpipe.beacon.message.*;
import io.xpipe.core.util.JacksonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
public class SocketServer {
private static final String BEACON_PORT_PROP = "io.xpipe.beacon.port";
private static final Logger logger = LoggerFactory.getLogger(SocketServer.class);
private static final int DEFAULT_PORT = 21721;
private static SocketServer INSTANCE;
private final int port;
private ServerSocket socket;
private boolean running;
private int connectionCounter;
private SocketServer(int port) {
this.port = port;
}
public static Path getUserDir() {
return Path.of(System.getProperty("user.home"), ".xpipe");
}
public static int determineUsedPort() {
if (System.getProperty(BEACON_PORT_PROP) != null) {
return Integer.parseInt(System.getProperty(BEACON_PORT_PROP));
}
var file = getUserDir().resolve("port");
if (Files.exists(file)) {
try {
return Integer.parseInt(Files.readString(file));
} catch (IOException ex) {
ex.printStackTrace();
}
}
return DEFAULT_PORT;
}
public static void init() throws IOException {
var port = determineUsedPort();
INSTANCE = new SocketServer(port);
INSTANCE.createSocket();
}
public static void reset() {
INSTANCE.stop();
INSTANCE = null;
}
private void stop() {
}
private void createSocket() throws IOException {
socket = new ServerSocket(port, 1000, InetAddress.getLoopbackAddress());
running = true;
var t = new Thread(() -> {
while (running) {
try {
var clientSocket = socket.accept();
handleClientConnection(clientSocket);
} catch (Exception ex) {
ex.printStackTrace();
}
connectionCounter++;
}
}, "socket server");
t.setDaemon(true);
t.start();
}
private void handleClientConnection(Socket clientSocket) {
var t = new Thread(() -> {
try {
var in = clientSocket.getInputStream();
var read = JacksonHelper.newMapper().disable(JsonParser.Feature.AUTO_CLOSE_SOURCE).readTree(in);
logger.debug("Received request: \n" + read.toPrettyString());
var req = parseRequest(read);
var prov = MessageProviders.byRequest(req).get();
prov.onRequestReceived(this, req, in, clientSocket);
} catch (SocketException ex) {
try {
ex.printStackTrace();
} catch (Exception ioex) {
ioex.printStackTrace();
}
} catch (Exception ex) {
try {
ex.printStackTrace();
sendServerErrorResponse(clientSocket, ex);
} catch (Exception ioex) {
ioex.printStackTrace();
}
} finally {
try {
clientSocket.close();
} catch (Exception ioex) {
ioex.printStackTrace();
}
}
}, "socket connection #" + connectionCounter);
t.setDaemon(true);
t.start();
}
public void prepareBody(Socket outSocket) throws IOException {
outSocket.getOutputStream().write(Sockets.BODY_SEPARATOR);
}
public <T extends ResponseMessage> void sendResponse(Socket outSocket, T obj) throws Exception {
ObjectNode json = JacksonHelper.newMapper().valueToTree(obj);
var prov = MessageProviders.byResponse(obj).get();
json.set("type", new TextNode(prov.getId()));
json.set("phase", new TextNode("response"));
var msg = JsonNodeFactory.instance.objectNode();
msg.set("xPipeMessage", json);
var mapper = JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
var gen = mapper.createGenerator(outSocket.getOutputStream());
gen.writeTree(msg);
}
public void sendClientErrorResponse(Socket outSocket, String message) throws Exception {
var err = new ClientErrorMessage(message);
ObjectNode json = JacksonHelper.newMapper().valueToTree(err);
var msg = JsonNodeFactory.instance.objectNode();
msg.set("xPipeClientError", json);
var mapper = JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
var gen = mapper.createGenerator(outSocket.getOutputStream());
gen.writeTree(msg);
}
public void sendServerErrorResponse(Socket outSocket, Throwable ex) throws Exception {
var err = new ServerErrorMessage(UUID.randomUUID(), ex);
ObjectNode json = JacksonHelper.newMapper().valueToTree(err);
var msg = JsonNodeFactory.instance.objectNode();
msg.set("xPipeServerError", json);
var mapper = JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
var gen = mapper.createGenerator(outSocket.getOutputStream());
gen.writeTree(msg);
}
private <T extends RequestMessage> T parseRequest(JsonNode header) throws Exception {
ObjectNode content = (ObjectNode) header.required("xPipeMessage");
var type = content.required("type").textValue();
var phase = content.required("phase").textValue();
if (!phase.equals("request")) {
throw new IllegalArgumentException();
}
content.remove("type");
content.remove("phase");
var prov = MessageProviders.byId(type);
if (prov.isEmpty()) {
throw new IllegalArgumentException();
}
var reader = JacksonHelper.newMapper().readerFor(prov.get().getRequestClass());
return reader.readValue(content);
}
}
@@ -0,0 +1,16 @@
package io.xpipe.beacon.socket;
import java.nio.charset.StandardCharsets;
public class Sockets {
public static final byte[] BODY_SEPARATOR = "\n\n".getBytes(StandardCharsets.UTF_8);
private static final String DEBUG_PROP = "io.xpipe.beacon.debugOutput";
public static boolean debugEnabled() {
if (System.getProperty(DEBUG_PROP) != null) {
return Boolean.parseBoolean(System.getProperty(DEBUG_PROP));
}
return false;
}
}
+28
View File
@@ -0,0 +1,28 @@
import io.xpipe.app.core.BeaconProvider;
import io.xpipe.beacon.BeaconProviderImpl;
import io.xpipe.beacon.message.MessageProvider;
import io.xpipe.beacon.message.impl.*;
module io.xpipe.beacon {
exports io.xpipe.beacon;
exports io.xpipe.beacon.message;
exports io.xpipe.beacon.message.impl;
requires org.slf4j;
requires org.slf4j.simple;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.module.paramnames;
requires io.xpipe.core;
opens io.xpipe.beacon;
opens io.xpipe.beacon.message;
opens io.xpipe.beacon.message.impl;
exports io.xpipe.beacon.socket;
opens io.xpipe.beacon.socket;
requires org.apache.commons.lang;
uses MessageProvider;
provides MessageProvider with ListCollectionsExchange, ListEntriesExchange, ReadTableDataExchange, VersionExchange, StatusExchange, ModeExchange, ReadTableInfoExchange;
}