mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 00:50:31 +00:00
[stage]
This commit is contained in:
parent
6dd0e6d255
commit
b68ada9e78
120 changed files with 1084 additions and 3160 deletions
|
@ -45,9 +45,7 @@ dependencies {
|
|||
api 'io.sentry:sentry:7.8.0'
|
||||
api 'commons-io:commons-io:2.16.1'
|
||||
api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "2.17.1"
|
||||
api group: 'com.fasterxml.jackson.module', name: 'jackson-module-parameter-names', version: "2.17.1"
|
||||
api group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: "2.17.1"
|
||||
api group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jdk8', version: "2.17.1"
|
||||
api group: 'org.kordamp.ikonli', name: 'ikonli-material2-pack', version: "12.2.0"
|
||||
api group: 'org.kordamp.ikonli', name: 'ikonli-materialdesign2-pack', version: "12.2.0"
|
||||
api group: 'org.kordamp.ikonli', name: 'ikonli-javafx', version: "12.2.0"
|
||||
|
@ -86,7 +84,6 @@ run {
|
|||
systemProperty 'io.xpipe.app.fullVersion', rootProject.fullVersion
|
||||
systemProperty 'io.xpipe.app.staging', isStage
|
||||
// systemProperty "io.xpipe.beacon.port", "21724"
|
||||
// systemProperty "io.xpipe.beacon.printMessages", "true"
|
||||
// systemProperty 'io.xpipe.app.debugPlatform', "true"
|
||||
|
||||
// Apply passed xpipe properties
|
||||
|
|
90
app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java
Normal file
90
app/src/main/java/io/xpipe/app/beacon/AppBeaconServer.java
Normal file
|
@ -0,0 +1,90 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.beacon.BeaconConfig;
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AppBeaconServer {
|
||||
|
||||
private static AppBeaconServer INSTANCE;
|
||||
private final int port;
|
||||
private boolean running;
|
||||
private HttpServer server;
|
||||
@Getter
|
||||
private Set<BeaconSession> sessions = new HashSet<>();
|
||||
|
||||
private AppBeaconServer(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
int port = -1;
|
||||
try {
|
||||
port = BeaconConfig.getUsedPort();
|
||||
INSTANCE = new AppBeaconServer(port);
|
||||
INSTANCE.start();
|
||||
|
||||
TrackEvent.withInfo("Initialized http server")
|
||||
.tag("port", port)
|
||||
.build()
|
||||
.handle();
|
||||
} catch (Exception ex) {
|
||||
// Not terminal!
|
||||
// We can still continue without the running server
|
||||
ErrorEvent.fromThrowable(ex)
|
||||
.description("Unable to start local http server on port " + port)
|
||||
.build()
|
||||
.handle();
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
if (INSTANCE != null) {
|
||||
INSTANCE.stop();
|
||||
INSTANCE = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addSession(BeaconSession session) {
|
||||
this.sessions.add(session);
|
||||
}
|
||||
|
||||
public static AppBeaconServer get() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = false;
|
||||
server.stop(1);
|
||||
}
|
||||
|
||||
private void start() throws IOException {
|
||||
server = HttpServer.create(new InetSocketAddress("localhost", port), 10);
|
||||
BeaconInterface.getAll().forEach(beaconInterface -> {
|
||||
server.createContext(beaconInterface.getPath(), new BeaconRequestHandler<>(beaconInterface));
|
||||
});
|
||||
server.setExecutor(Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = Executors.defaultThreadFactory().newThread(r);
|
||||
t.setName("http handler");
|
||||
t.setUncaughtExceptionHandler((t1, e) -> {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
});
|
||||
return t;
|
||||
}));
|
||||
server.start();
|
||||
running = true;
|
||||
}
|
||||
}
|
122
app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java
Normal file
122
app/src/main/java/io/xpipe/app/beacon/BeaconRequestHandler.java
Normal file
|
@ -0,0 +1,122 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.beacon.*;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class BeaconRequestHandler<T> implements HttpHandler {
|
||||
|
||||
private final BeaconInterface<T> beaconInterface;
|
||||
|
||||
public BeaconRequestHandler(BeaconInterface<T> beaconInterface) {this.beaconInterface = beaconInterface;}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
if (beaconInterface.requiresAuthentication()) {
|
||||
var auth = exchange.getRequestHeaders().getFirst("Authorization");
|
||||
if (auth == null) {
|
||||
writeError(exchange, new BeaconClientErrorResponse("Missing Authorization header"), 401);
|
||||
return;
|
||||
}
|
||||
|
||||
var token = auth.replace("Bearer ", "");
|
||||
var session = AppBeaconServer.get().getSessions().stream().filter(s -> s.getToken().equals(token)).findFirst().orElse(null);
|
||||
if (session == null) {
|
||||
writeError(exchange, new BeaconClientErrorResponse("Unknown token"), 403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
handleAuthenticatedRequest(exchange);
|
||||
}
|
||||
|
||||
private void handleAuthenticatedRequest(HttpExchange exchange) {
|
||||
T object;
|
||||
Object response;
|
||||
try {
|
||||
try (InputStream is = exchange.getRequestBody()) {
|
||||
var tree = JacksonMapper.getDefault().readTree(is);
|
||||
TrackEvent.trace("Parsed raw request:\n" + tree.toPrettyString());
|
||||
var emptyRequestClass = tree.isEmpty() && beaconInterface.getRequestClass().getDeclaredFields().length == 0;
|
||||
object = emptyRequestClass ? createDefaultRequest(beaconInterface) : JacksonMapper.getDefault().treeToValue(tree, beaconInterface.getRequestClass());
|
||||
TrackEvent.trace("Parsed request object:\n" + object);
|
||||
}
|
||||
response = beaconInterface.handle(exchange, object);
|
||||
} catch (BeaconClientException clientException) {
|
||||
ErrorEvent.fromThrowable(clientException).omit().expected().handle();
|
||||
writeError(exchange, new BeaconClientErrorResponse(clientException.getMessage()), 400);
|
||||
return;
|
||||
} catch (BeaconServerException serverException) {
|
||||
var cause = serverException.getCause() != null ? serverException.getCause() : serverException;
|
||||
ErrorEvent.fromThrowable(cause).handle();
|
||||
writeError(exchange, new BeaconServerErrorResponse(cause), 500);
|
||||
return;
|
||||
} catch (IOException ex) {
|
||||
// Handle serialization errors as normal exceptions and other IO exceptions as assuming that the connection is broken
|
||||
if (!ex.getClass().getName().contains("jackson")) {
|
||||
ErrorEvent.fromThrowable(ex).omit().expected().handle();
|
||||
} else {
|
||||
ErrorEvent.fromThrowable(ex).omit().expected().handle();
|
||||
writeError(exchange, new BeaconClientErrorResponse(ex.getMessage()), 400);
|
||||
}
|
||||
return;
|
||||
} catch (Throwable other) {
|
||||
ErrorEvent.fromThrowable(other).handle();
|
||||
writeError(exchange, new BeaconServerErrorResponse(other), 500);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (response != null) {
|
||||
TrackEvent.trace("Sending response:\n" + object);
|
||||
var tree = JacksonMapper.getDefault().valueToTree(response);
|
||||
TrackEvent.trace("Sending raw response:\n" + tree.toPrettyString());
|
||||
var bytes = tree.toPrettyString().getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(200, bytes.length);
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(bytes);
|
||||
}
|
||||
} else {
|
||||
exchange.sendResponseHeaders(200, -1);
|
||||
}
|
||||
} catch (IOException ioException) {
|
||||
ErrorEvent.fromThrowable(ioException).omit().expected().handle();
|
||||
} catch (Throwable other) {
|
||||
ErrorEvent.fromThrowable(other).handle();
|
||||
writeError(exchange, new BeaconServerErrorResponse(other), 500);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeError(HttpExchange exchange, Object errorMessage, int code) {
|
||||
try {
|
||||
var bytes = JacksonMapper.getDefault().writeValueAsString(errorMessage).getBytes(StandardCharsets.UTF_8);
|
||||
exchange.sendResponseHeaders(code, bytes.length);
|
||||
try (OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(bytes);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ErrorEvent.fromThrowable(ex).omit().expected().handle();
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("unchecked")
|
||||
private <REQ> REQ createDefaultRequest(BeaconInterface<?> beaconInterface) {
|
||||
var c = beaconInterface.getRequestClass().getDeclaredMethod("builder");
|
||||
c.setAccessible(true);
|
||||
var b = c.invoke(null);
|
||||
var m = b.getClass().getDeclaredMethod("build");
|
||||
m.setAccessible(true);
|
||||
return (REQ) beaconInterface.getRequestClass().cast(m.invoke(b));
|
||||
}
|
||||
}
|
11
app/src/main/java/io/xpipe/app/beacon/BeaconSession.java
Normal file
11
app/src/main/java/io/xpipe/app/beacon/BeaconSession.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package io.xpipe.app.beacon;
|
||||
|
||||
import io.xpipe.beacon.BeaconClientInformation;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class BeaconSession {
|
||||
|
||||
BeaconClientInformation clientInformation;
|
||||
String token;
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package io.xpipe.app.exchange;
|
||||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.SecretManager;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.AskpassExchange;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.AskpassExchange;
|
||||
|
||||
public class AskpassExchangeImpl extends AskpassExchange
|
||||
implements MessageExchangeImpl<AskpassExchange.Request, AskpassExchange.Response> {
|
||||
import java.io.IOException;
|
||||
|
||||
public class AskpassExchangeImpl extends AskpassExchange {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var found = msg.getSecretId() != null
|
||||
? SecretManager.getProgress(msg.getRequest(), msg.getSecretId())
|
||||
: SecretManager.getProgress(msg.getRequest());
|
|
@ -0,0 +1,20 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.FocusExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class FocusExchangeImpl extends FocusExchange {
|
||||
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
|
||||
OperationMode.switchUp(OperationMode.map(msg.getMode()));
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.beacon.BeaconSession;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.HandshakeExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HandshakeExchangeImpl extends HandshakeExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request body) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var session = new BeaconSession(body.getClient(), UUID.randomUUID().toString());
|
||||
AppBeaconServer.get().addSession(session);
|
||||
return Response.builder().token(session.getToken()).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.ModeExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ModeExchangeImpl extends ModeExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
// Wait for startup
|
||||
while (OperationMode.get() == null) {
|
||||
ThreadHelper.sleep(100);
|
||||
}
|
||||
|
||||
var mode = OperationMode.map(msg.getMode());
|
||||
if (!mode.isSupported()) {
|
||||
throw new BeaconClientException("Unsupported mode: " + msg.getMode().getDisplayName() + ". Supported: "
|
||||
+ String.join(
|
||||
", ",
|
||||
OperationMode.getAll().stream()
|
||||
.filter(OperationMode::isSupported)
|
||||
.map(OperationMode::getId)
|
||||
.toList()));
|
||||
}
|
||||
|
||||
OperationMode.switchToSyncIfPossible(mode);
|
||||
return ModeExchange.Response.builder()
|
||||
.usedMode(OperationMode.map(OperationMode.get()))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.launcher.LauncherInput;
|
||||
import io.xpipe.app.util.PlatformState;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.OpenExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OpenExchangeImpl extends OpenExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
if (msg.getArguments().isEmpty()) {
|
||||
if (!OperationMode.switchToSyncIfPossible(OperationMode.GUI)) {
|
||||
throw new BeaconServerException(PlatformState.getLastError());
|
||||
}
|
||||
}
|
||||
|
||||
LauncherInput.handle(msg.getArguments());
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.StatusExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StatusExchangeImpl extends StatusExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request body) throws IOException, BeaconClientException, BeaconServerException {
|
||||
String mode;
|
||||
if (OperationMode.get() == null) {
|
||||
mode = "none";
|
||||
} else {
|
||||
mode = OperationMode.get().getId();
|
||||
}
|
||||
|
||||
return Response.builder().mode(mode).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.StopExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class StopExchangeImpl extends StopExchange {
|
||||
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
ThreadHelper.sleep(1000);
|
||||
OperationMode.close();
|
||||
});
|
||||
return Response.builder().success(true).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.TerminalLaunchExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TerminalLaunchExchangeImpl extends TerminalLaunchExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var r = TerminalLauncherManager.performLaunch(msg.getRequest());
|
||||
return Response.builder().targetFile(r).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.TerminalWaitExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TerminalWaitExchangeImpl extends TerminalWaitExchange {
|
||||
@Override
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
TerminalLauncherManager.waitForCompletion(msg.getRequest());
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package io.xpipe.app.exchange;
|
||||
package io.xpipe.app.beacon.impl;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.app.core.AppProperties;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.VersionExchange;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.beacon.api.VersionExchange;
|
||||
|
||||
public class VersionExchangeImpl extends VersionExchange
|
||||
implements MessageExchangeImpl<VersionExchange.Request, VersionExchange.Response> {
|
||||
import java.io.IOException;
|
||||
|
||||
public class VersionExchangeImpl extends VersionExchange {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
public Object handle(HttpExchange exchange, Request msg) throws IOException, BeaconClientException, BeaconServerException {
|
||||
var jvmVersion = System.getProperty("java.vm.vendor") + " "
|
||||
+ System.getProperty("java.vm.name") + " ("
|
||||
+ System.getProperty("java.vm.version") + ")";
|
|
@ -1,358 +0,0 @@
|
|||
package io.xpipe.app.core;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpls;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.beacon.*;
|
||||
import io.xpipe.beacon.exchange.MessageExchanges;
|
||||
import io.xpipe.beacon.exchange.data.ClientErrorMessage;
|
||||
import io.xpipe.beacon.exchange.data.ServerErrorMessage;
|
||||
import io.xpipe.core.util.Deobfuscator;
|
||||
import io.xpipe.core.util.FailableRunnable;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class AppSocketServer {
|
||||
|
||||
private static AppSocketServer INSTANCE;
|
||||
private final int port;
|
||||
private ServerSocket socket;
|
||||
private boolean running;
|
||||
private int connectionCounter;
|
||||
private Thread listenerThread;
|
||||
|
||||
private AppSocketServer(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
int port = -1;
|
||||
try {
|
||||
port = BeaconConfig.getUsedPort();
|
||||
INSTANCE = new AppSocketServer(port);
|
||||
INSTANCE.createSocketListener();
|
||||
|
||||
TrackEvent.withInfo("Initialized socket server")
|
||||
.tag("port", port)
|
||||
.build()
|
||||
.handle();
|
||||
} catch (Exception ex) {
|
||||
// Not terminal!
|
||||
ErrorEvent.fromThrowable(ex)
|
||||
.description("Unable to start local socket server on port " + port)
|
||||
.build()
|
||||
.handle();
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
if (INSTANCE != null) {
|
||||
INSTANCE.stop();
|
||||
INSTANCE = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = false;
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
try {
|
||||
listenerThread.join();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void createSocketListener() throws IOException {
|
||||
socket = new ServerSocket(port, 10000, InetAddress.getLoopbackAddress());
|
||||
running = true;
|
||||
listenerThread = new Thread(
|
||||
() -> {
|
||||
while (running) {
|
||||
Socket clientSocket;
|
||||
try {
|
||||
clientSocket = socket.accept();
|
||||
} catch (Exception ex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
performExchangesAsync(clientSocket);
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).build().handle();
|
||||
}
|
||||
connectionCounter++;
|
||||
}
|
||||
},
|
||||
"socket server");
|
||||
listenerThread.start();
|
||||
}
|
||||
|
||||
private boolean performExchange(Socket clientSocket, int id) throws Exception {
|
||||
if (clientSocket.isClosed()) {
|
||||
TrackEvent.trace("Socket closed");
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonNode node;
|
||||
try (InputStream blockIn = BeaconFormat.readBlocks(clientSocket.getInputStream())) {
|
||||
node = JacksonMapper.getDefault().readTree(blockIn);
|
||||
}
|
||||
if (node.isMissingNode()) {
|
||||
TrackEvent.trace("Received EOF");
|
||||
return false;
|
||||
}
|
||||
|
||||
TrackEvent.trace("Received raw request: \n" + node.toPrettyString());
|
||||
|
||||
var req = parseRequest(node);
|
||||
TrackEvent.trace("Parsed request: \n" + req.toString());
|
||||
|
||||
var prov = MessageExchangeImpls.byRequest(req);
|
||||
if (prov.isEmpty()) {
|
||||
throw new IllegalArgumentException("Unknown request id: " + req.getClass());
|
||||
}
|
||||
AtomicReference<FailableRunnable<Exception>> post = new AtomicReference<>();
|
||||
var res = prov.get()
|
||||
.handleRequest(
|
||||
new BeaconHandler() {
|
||||
@Override
|
||||
public void postResponse(FailableRunnable<Exception> r) {
|
||||
post.set(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream sendBody() throws IOException {
|
||||
TrackEvent.trace("Starting writing body for #" + id);
|
||||
return AppSocketServer.this.sendBody(clientSocket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream receiveBody() throws IOException {
|
||||
TrackEvent.trace("Starting to read body for #" + id);
|
||||
return AppSocketServer.this.receiveBody(clientSocket);
|
||||
}
|
||||
},
|
||||
req);
|
||||
|
||||
TrackEvent.trace("Sending response to #" + id + ": \n" + res.toString());
|
||||
AppSocketServer.this.sendResponse(clientSocket, res);
|
||||
|
||||
try {
|
||||
// If this fails, we sadly can't send an error response. Therefore just report it on the server side
|
||||
if (post.get() != null) {
|
||||
post.get().run();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).handle();
|
||||
}
|
||||
|
||||
TrackEvent.builder()
|
||||
.type("trace")
|
||||
.message("Socket connection #" + id + " performed exchange "
|
||||
+ req.getClass().getSimpleName())
|
||||
.build()
|
||||
.handle();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void performExchanges(Socket clientSocket, int id) {
|
||||
try {
|
||||
JsonNode informationNode;
|
||||
try (InputStream blockIn = BeaconFormat.readBlocks(clientSocket.getInputStream())) {
|
||||
informationNode = JacksonMapper.getDefault().readTree(blockIn);
|
||||
}
|
||||
if (informationNode.isMissingNode()) {
|
||||
TrackEvent.trace("Received EOF");
|
||||
return;
|
||||
}
|
||||
var information =
|
||||
JacksonMapper.getDefault().treeToValue(informationNode, BeaconClient.ClientInformation.class);
|
||||
try (var blockOut = BeaconFormat.writeBlocks(clientSocket.getOutputStream())) {
|
||||
blockOut.write("\"ACK\"".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
TrackEvent.builder()
|
||||
.type("trace")
|
||||
.message("Created new socket connection #" + id)
|
||||
.tag("client", information != null ? information.toDisplayString() : "Unknown")
|
||||
.build()
|
||||
.handle();
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (!performExchange(clientSocket, id)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
TrackEvent.builder()
|
||||
.type("trace")
|
||||
.message("Socket connection #" + id + " finished successfully")
|
||||
.build()
|
||||
.handle();
|
||||
|
||||
} catch (ClientException ce) {
|
||||
TrackEvent.trace("Sending client error to #" + id + ": " + ce.getMessage());
|
||||
sendClientErrorResponse(clientSocket, ce.getMessage());
|
||||
} catch (ServerException se) {
|
||||
TrackEvent.trace("Sending server error to #" + id + ": " + se.getMessage());
|
||||
Deobfuscator.deobfuscate(se);
|
||||
sendServerErrorResponse(clientSocket, se);
|
||||
var toReport = se.getCause() != null ? se.getCause() : se;
|
||||
ErrorEvent.fromThrowable(toReport).build().handle();
|
||||
} catch (SocketException ex) {
|
||||
// Do not send error and omit it, as this might happen often
|
||||
// This is expected if you kill a running xpipe CLI process
|
||||
// We do not send the error to the client as the socket connection might be broken
|
||||
ErrorEvent.fromThrowable(ex).omitted(true).expected().build().handle();
|
||||
} catch (Throwable ex) {
|
||||
TrackEvent.trace("Sending internal server error to #" + id + ": " + ex.getMessage());
|
||||
Deobfuscator.deobfuscate(ex);
|
||||
sendServerErrorResponse(clientSocket, ex);
|
||||
ErrorEvent.fromThrowable(ex).build().handle();
|
||||
}
|
||||
} catch (SocketException ex) {
|
||||
// Omit it, as this might happen often
|
||||
// This is expected if you kill a running xpipe CLI process
|
||||
ErrorEvent.fromThrowable(ex).expected().omit().build().handle();
|
||||
} catch (Throwable ex) {
|
||||
ErrorEvent.fromThrowable(ex).build().handle();
|
||||
} finally {
|
||||
try {
|
||||
clientSocket.close();
|
||||
TrackEvent.trace("Closed socket #" + id);
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable(e).build().handle();
|
||||
}
|
||||
}
|
||||
|
||||
TrackEvent.builder().type("trace").message("Socket connection #" + id + " finished unsuccessfully");
|
||||
}
|
||||
|
||||
private void performExchangesAsync(Socket clientSocket) {
|
||||
var id = connectionCounter;
|
||||
var t = new Thread(
|
||||
() -> {
|
||||
performExchanges(clientSocket, id);
|
||||
},
|
||||
"socket connection #" + id);
|
||||
t.start();
|
||||
}
|
||||
|
||||
public OutputStream sendBody(Socket outSocket) throws IOException {
|
||||
outSocket.getOutputStream().write(BeaconConfig.BODY_SEPARATOR);
|
||||
return BeaconFormat.writeBlocks(outSocket.getOutputStream());
|
||||
}
|
||||
|
||||
public InputStream receiveBody(Socket outSocket) throws IOException {
|
||||
var read = outSocket.getInputStream().readNBytes(BeaconConfig.BODY_SEPARATOR.length);
|
||||
if (!Arrays.equals(read, BeaconConfig.BODY_SEPARATOR)) {
|
||||
throw new IOException("Expected body start (" + HexFormat.of().formatHex(BeaconConfig.BODY_SEPARATOR)
|
||||
+ ") but got " + HexFormat.of().formatHex(read));
|
||||
}
|
||||
return BeaconFormat.readBlocks(outSocket.getInputStream());
|
||||
}
|
||||
|
||||
public <T extends ResponseMessage> void sendResponse(Socket outSocket, T obj) throws Exception {
|
||||
ObjectNode json = JacksonMapper.getDefault().valueToTree(obj);
|
||||
var prov = MessageExchanges.byResponse(obj).get();
|
||||
json.set("messageType", new TextNode(prov.getId()));
|
||||
json.set("messagePhase", new TextNode("response"));
|
||||
var msg = JsonNodeFactory.instance.objectNode();
|
||||
msg.set("xPipeMessage", json);
|
||||
|
||||
var writer = new StringWriter();
|
||||
var mapper = JacksonMapper.getDefault();
|
||||
try (JsonGenerator g = mapper.createGenerator(writer).setPrettyPrinter(new DefaultPrettyPrinter())) {
|
||||
g.writeTree(msg);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't serialize request", ex);
|
||||
}
|
||||
|
||||
var content = writer.toString();
|
||||
TrackEvent.trace("Sending raw response:\n" + content);
|
||||
try (OutputStream blockOut = BeaconFormat.writeBlocks(outSocket.getOutputStream())) {
|
||||
blockOut.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendClientErrorResponse(Socket outSocket, String message) throws Exception {
|
||||
var err = new ClientErrorMessage(message);
|
||||
ObjectNode json = JacksonMapper.getDefault().valueToTree(err);
|
||||
var msg = JsonNodeFactory.instance.objectNode();
|
||||
msg.set("xPipeClientError", json);
|
||||
|
||||
// Don't log this as it clutters the output
|
||||
// TrackEvent.trace("beacon", "Sending raw client error:\n" + json.toPrettyString());
|
||||
|
||||
var mapper = JacksonMapper.getDefault();
|
||||
try (OutputStream blockOut = BeaconFormat.writeBlocks(outSocket.getOutputStream())) {
|
||||
var gen = mapper.createGenerator(blockOut);
|
||||
gen.writeTree(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendServerErrorResponse(Socket outSocket, Throwable ex) throws Exception {
|
||||
var err = new ServerErrorMessage(UUID.randomUUID(), ex);
|
||||
ObjectNode json = JacksonMapper.getDefault().valueToTree(err);
|
||||
var msg = JsonNodeFactory.instance.objectNode();
|
||||
msg.set("xPipeServerError", json);
|
||||
|
||||
// Don't log this as it clutters the output
|
||||
// TrackEvent.trace("beacon", "Sending raw server error:\n" + json.toPrettyString());
|
||||
|
||||
var mapper = JacksonMapper.getDefault();
|
||||
try (OutputStream blockOut = BeaconFormat.writeBlocks(outSocket.getOutputStream())) {
|
||||
var gen = mapper.createGenerator(blockOut);
|
||||
gen.writeTree(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends RequestMessage> T parseRequest(JsonNode header) throws Exception {
|
||||
ObjectNode content = (ObjectNode) header.required("xPipeMessage");
|
||||
TrackEvent.trace("Parsed raw request:\n" + content.toPrettyString());
|
||||
|
||||
var type = content.required("messageType").textValue();
|
||||
var phase = content.required("messagePhase").textValue();
|
||||
if (!phase.equals("request")) {
|
||||
throw new IllegalArgumentException("Not a request");
|
||||
}
|
||||
content.remove("messageType");
|
||||
content.remove("messagePhase");
|
||||
|
||||
var prov = MessageExchangeImpls.byId(type);
|
||||
if (prov.isEmpty()) {
|
||||
throw new IllegalArgumentException("Unknown request id: " + type);
|
||||
}
|
||||
|
||||
var reader = JacksonMapper.getDefault().readerFor(prov.get().getRequestClass());
|
||||
return reader.readValue(content);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.app.core.mode;
|
||||
|
||||
import io.xpipe.app.beacon.AppBeaconServer;
|
||||
import io.xpipe.app.browser.session.BrowserSessionModel;
|
||||
import io.xpipe.app.comp.store.StoreViewState;
|
||||
import io.xpipe.app.core.*;
|
||||
|
@ -17,7 +18,6 @@ import io.xpipe.app.util.FileBridge;
|
|||
import io.xpipe.app.util.LicenseProvider;
|
||||
import io.xpipe.app.util.LocalShell;
|
||||
import io.xpipe.app.util.UnlockAlert;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
public class BaseMode extends OperationMode {
|
||||
|
||||
|
@ -43,8 +43,6 @@ public class BaseMode extends OperationMode {
|
|||
// if (true) throw new IllegalStateException();
|
||||
|
||||
TrackEvent.info("Initializing base mode components ...");
|
||||
AppExtensionManager.init(true);
|
||||
JacksonMapper.initModularized(AppExtensionManager.getInstance().getExtendedLayer());
|
||||
AppI18n.init();
|
||||
LicenseProvider.get().init();
|
||||
AppPrefs.initLocal();
|
||||
|
@ -56,8 +54,8 @@ public class BaseMode extends OperationMode {
|
|||
AppShellCheck.check();
|
||||
XPipeDistributionType.init();
|
||||
AppPrefs.setDefaults();
|
||||
// Initialize socket server as we should be prepared for git askpass commands
|
||||
AppSocketServer.init();
|
||||
// Initialize beacon server as we should be prepared for git askpass commands
|
||||
AppBeaconServer.init();
|
||||
GitStorageHandler.getInstance().init();
|
||||
GitStorageHandler.getInstance().setupRepositoryAndPull();
|
||||
AppPrefs.initSharedRemote();
|
||||
|
@ -85,8 +83,8 @@ public class BaseMode extends OperationMode {
|
|||
AppResources.reset();
|
||||
AppExtensionManager.reset();
|
||||
AppDataLock.unlock();
|
||||
// Shut down socket server last to keep a non-daemon thread running
|
||||
AppSocketServer.reset();
|
||||
// Shut down server last to keep a non-daemon thread running
|
||||
AppBeaconServer.reset();
|
||||
TrackEvent.info("Background mode shutdown finished");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ public abstract class OperationMode {
|
|||
AppProperties.logArguments(args);
|
||||
AppProperties.logSystemProperties();
|
||||
AppProperties.logPassedProperties();
|
||||
AppExtensionManager.init(true);
|
||||
TrackEvent.info("Finished initial setup");
|
||||
} catch (Throwable ex) {
|
||||
ErrorEvent.fromThrowable(ex).term().handle();
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.DialogExchange;
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.dialog.DialogReference;
|
||||
import io.xpipe.core.util.FailableConsumer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DialogExchangeImpl extends DialogExchange
|
||||
implements MessageExchangeImpl<DialogExchange.Request, DialogExchange.Response> {
|
||||
|
||||
private static final Map<UUID, Dialog> openDialogs = new HashMap<>();
|
||||
private static final Map<UUID, FailableConsumer<?, Exception>> openDialogConsumers = new HashMap<>();
|
||||
|
||||
public static <T> DialogReference add(Dialog d, FailableConsumer<T, Exception> onCompletion) throws Exception {
|
||||
return add(d, UUID.randomUUID(), onCompletion);
|
||||
}
|
||||
|
||||
public static <T> DialogReference add(Dialog d, UUID uuid, FailableConsumer<T, Exception> onCompletion)
|
||||
throws Exception {
|
||||
openDialogs.put(uuid, d);
|
||||
openDialogConsumers.put(uuid, onCompletion);
|
||||
return new DialogReference(uuid, d.start());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogExchange.Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
if (msg.isCancel()) {
|
||||
TrackEvent.withTrace("Received cancel dialog request")
|
||||
.tag("key", msg.getDialogKey())
|
||||
.handle();
|
||||
openDialogs.remove(msg.getDialogKey());
|
||||
openDialogConsumers.remove(msg.getDialogKey());
|
||||
return DialogExchange.Response.builder().element(null).build();
|
||||
}
|
||||
|
||||
var dialog = openDialogs.get(msg.getDialogKey());
|
||||
var e = dialog.receive(msg.getValue());
|
||||
|
||||
TrackEvent.withTrace("Received normal dialog request")
|
||||
.tag("key", msg.getDialogKey())
|
||||
.tag("value", msg.getValue())
|
||||
.tag("newElement", e)
|
||||
.handle();
|
||||
|
||||
if (e == null) {
|
||||
openDialogs.remove(msg.getDialogKey());
|
||||
var con = openDialogConsumers.remove(msg.getDialogKey());
|
||||
con.accept(dialog.getResult());
|
||||
}
|
||||
|
||||
return DialogExchange.Response.builder().element(e).build();
|
||||
//
|
||||
//
|
||||
// var provider = getProvider(msg.getInstance().getProvider());
|
||||
// var completeConfig = toCompleteConfig(provider);
|
||||
//
|
||||
// var option = completeConfig.keySet().stream()
|
||||
// .filter(o -> o.getKey().equals(msg.getKey())).findAny()
|
||||
// .orElseThrow(() -> new ClientException("Invalid config key: " + msg.getKey()));
|
||||
//
|
||||
// String errorMsg = null;
|
||||
// try {
|
||||
// option.getConverter().convertFromString(msg.getValue());
|
||||
// } catch (Exception ex) {
|
||||
// errorMsg = ex.getMessage();
|
||||
// }
|
||||
//
|
||||
// return DialogExchange.Response.builder().errorMsg(errorMsg).build();
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.FocusExchange;
|
||||
|
||||
public class FocusExchangeImpl extends FocusExchange
|
||||
implements MessageExchangeImpl<FocusExchange.Request, FocusExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
OperationMode.switchUp(OperationMode.map(msg.getMode()));
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.LaunchExchange;
|
||||
import io.xpipe.core.store.LaunchableStore;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LaunchExchangeImpl extends LaunchExchange
|
||||
implements MessageExchangeImpl<LaunchExchange.Request, LaunchExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
var store = getStoreEntryById(msg.getId(), false);
|
||||
if (store.getStore() instanceof LaunchableStore s) {
|
||||
// var command = s.prepareLaunchCommand()
|
||||
// .prepareTerminalOpen(TerminalInitScriptConfig.ofName(store.getName()), sc -> null);
|
||||
// return Response.builder().command(split(command)).build();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(store.getName() + " is not launchable");
|
||||
}
|
||||
|
||||
private List<String> split(String command) {
|
||||
var split = Arrays.stream(command.split(" ", 3)).collect(Collectors.toList());
|
||||
var s = split.get(2);
|
||||
if ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'"))) {
|
||||
split.set(2, s.substring(1, s.length() - 1));
|
||||
}
|
||||
return split;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.core.store.DataStoreId;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
public interface MessageExchangeImpl<RQ extends RequestMessage, RS extends ResponseMessage> extends MessageExchange {
|
||||
|
||||
default DataStoreEntry getStoreEntryByName(@NonNull String name, boolean acceptDisabled) throws ClientException {
|
||||
var store = DataStorage.get().getStoreEntryIfPresent(name);
|
||||
if (store.isEmpty()) {
|
||||
throw new ClientException("No store with name " + name + " was found");
|
||||
}
|
||||
if (store.get().isDisabled() && !acceptDisabled) {
|
||||
throw new ClientException(
|
||||
String.format("Store %s is disabled", store.get().getName()));
|
||||
}
|
||||
return store.get();
|
||||
}
|
||||
|
||||
default DataStoreEntry getStoreEntryById(@NonNull DataStoreId id, boolean acceptUnusable) throws ClientException {
|
||||
var store = DataStorage.get().getStoreEntryIfPresent(id);
|
||||
if (store.isEmpty()) {
|
||||
throw new ClientException("No store with id " + id + " was found");
|
||||
}
|
||||
if (store.get().isDisabled() && !acceptUnusable) {
|
||||
throw new ClientException(
|
||||
String.format("Store %s is disabled", store.get().getName()));
|
||||
}
|
||||
if (!store.get().getValidity().isUsable() && !acceptUnusable) {
|
||||
throw new ClientException(String.format(
|
||||
"Store %s is not completely configured", store.get().getName()));
|
||||
}
|
||||
return store.get();
|
||||
}
|
||||
|
||||
String getId();
|
||||
|
||||
RS handleRequest(BeaconHandler handler, RQ msg) throws Exception;
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchanges;
|
||||
import io.xpipe.core.util.ModuleLayerLoader;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MessageExchangeImpls {
|
||||
|
||||
private static List<MessageExchangeImpl<?, ?>> ALL;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <RQ extends RequestMessage, RS extends ResponseMessage> Optional<MessageExchangeImpl<RQ, RS>> byId(
|
||||
String name) {
|
||||
var r = ALL.stream().filter(d -> d.getId().equals(name)).findAny();
|
||||
return Optional.ofNullable((MessageExchangeImpl<RQ, RS>) r.orElse(null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <RQ extends RequestMessage, RS extends ResponseMessage>
|
||||
Optional<MessageExchangeImpl<RQ, RS>> byRequest(RQ req) {
|
||||
var r = ALL.stream()
|
||||
.filter(d -> d.getRequestClass().equals(req.getClass()))
|
||||
.findAny();
|
||||
return Optional.ofNullable((MessageExchangeImpl<RQ, RS>) r.orElse(null));
|
||||
}
|
||||
|
||||
public static List<MessageExchangeImpl<?, ?>> getAll() {
|
||||
return ALL;
|
||||
}
|
||||
|
||||
public static class Loader implements ModuleLayerLoader {
|
||||
|
||||
@Override
|
||||
public void init(ModuleLayer layer) {
|
||||
ALL = ServiceLoader.load(layer, MessageExchangeImpl.class).stream()
|
||||
.map(s -> {
|
||||
// TrackEvent.trace("init", "Loaded exchange implementation " + ex.getId());
|
||||
return (MessageExchangeImpl<?, ?>) s.get();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ALL.forEach(messageExchange -> {
|
||||
if (MessageExchanges.byId(messageExchange.getId()).isEmpty()) {
|
||||
throw new AssertionError("Missing base exchange: " + messageExchange.getId());
|
||||
}
|
||||
});
|
||||
|
||||
MessageExchanges.getAll().forEach(messageExchange -> {
|
||||
if (MessageExchangeImpls.byId(messageExchange.getId()).isEmpty()) {
|
||||
throw new AssertionError("Missing exchange implementation: " + messageExchange.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.launcher.LauncherInput;
|
||||
import io.xpipe.app.util.PlatformState;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ServerException;
|
||||
import io.xpipe.beacon.exchange.OpenExchange;
|
||||
|
||||
public class OpenExchangeImpl extends OpenExchange
|
||||
implements MessageExchangeImpl<OpenExchange.Request, OpenExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws ServerException {
|
||||
if (msg.getArguments().isEmpty()) {
|
||||
if (!OperationMode.switchToSyncIfPossible(OperationMode.GUI)) {
|
||||
throw new ServerException(PlatformState.getLastError());
|
||||
}
|
||||
}
|
||||
|
||||
LauncherInput.handle(msg.getArguments());
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.QueryStoreExchange;
|
||||
import io.xpipe.core.dialog.DialogMapper;
|
||||
|
||||
public class QueryStoreExchangeImpl extends QueryStoreExchange
|
||||
implements MessageExchangeImpl<QueryStoreExchange.Request, QueryStoreExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
var store = getStoreEntryByName(msg.getName(), true);
|
||||
var summary = "";
|
||||
var dialog = store.getProvider().dialogForStore(store.getStore().asNeeded());
|
||||
var config = new DialogMapper(dialog).handle();
|
||||
return Response.builder()
|
||||
.summary(summary)
|
||||
.internalStore(store.getStore())
|
||||
.provider(store.getProvider().getId())
|
||||
.config(config)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.TerminalLaunchExchange;
|
||||
|
||||
public class TerminalLaunchExchangeImpl extends TerminalLaunchExchange
|
||||
implements MessageExchangeImpl<TerminalLaunchExchange.Request, TerminalLaunchExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws ClientException {
|
||||
var r = TerminalLauncherManager.performLaunch(msg.getRequest());
|
||||
return Response.builder().targetFile(r).build();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.xpipe.app.exchange;
|
||||
|
||||
import io.xpipe.app.util.TerminalLauncherManager;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.ServerException;
|
||||
import io.xpipe.beacon.exchange.TerminalWaitExchange;
|
||||
|
||||
public class TerminalWaitExchangeImpl extends TerminalWaitExchange
|
||||
implements MessageExchangeImpl<TerminalWaitExchange.Request, TerminalWaitExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws ServerException, ClientException {
|
||||
TerminalLauncherManager.waitForCompletion(msg.getRequest());
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.DrainExchange;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
public class DrainExchangeImpl extends DrainExchange
|
||||
implements MessageExchangeImpl<DrainExchange.Request, DrainExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
var ds = getStoreEntryById(msg.getSource(), false);
|
||||
|
||||
if (!(ds.getStore() instanceof ShellStore)) {
|
||||
throw new ClientException("Can't open file system for connection");
|
||||
}
|
||||
|
||||
handler.postResponse(() -> {
|
||||
ShellStore store = ds.getStore().asNeeded();
|
||||
try (var fs = store.createFileSystem();
|
||||
var output = handler.sendBody();
|
||||
var inputStream = fs.openInput(msg.getPath())) {
|
||||
inputStream.transferTo(output);
|
||||
}
|
||||
});
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.DialogExchangeImpl;
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.EditStoreExchange;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
public class EditStoreExchangeImpl extends EditStoreExchange
|
||||
implements MessageExchangeImpl<EditStoreExchange.Request, EditStoreExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
var s = getStoreEntryByName(msg.getName(), false);
|
||||
var dialog = s.getProvider().dialogForStore(s.getStore());
|
||||
var reference = DialogExchangeImpl.add(dialog, (DataStore newStore) -> {
|
||||
// s.setStore(newStore);
|
||||
});
|
||||
return Response.builder().dialog(reference).build();
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.ListStoresExchange;
|
||||
import io.xpipe.beacon.exchange.data.StoreListEntry;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ListStoresExchangeImpl extends ListStoresExchange
|
||||
implements MessageExchangeImpl<ListStoresExchange.Request, ListStoresExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
DataStorage s = DataStorage.get();
|
||||
if (s == null) {
|
||||
return Response.builder().entries(List.of()).build();
|
||||
}
|
||||
|
||||
var e = s.getStoreEntries().stream()
|
||||
.filter(entry -> !entry.isDisabled())
|
||||
.map(col -> StoreListEntry.builder()
|
||||
.id(DataStorage.get().getId(col))
|
||||
.type(col.getProvider().getId())
|
||||
.build())
|
||||
.sorted(Comparator.comparing(en -> en.getId().toString()))
|
||||
.toList();
|
||||
return Response.builder().entries(e).build();
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.cli.ModeExchange;
|
||||
|
||||
public class ModeExchangeImpl extends ModeExchange
|
||||
implements MessageExchangeImpl<ModeExchange.Request, ModeExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
// Wait for startup
|
||||
while (OperationMode.get() == null) {
|
||||
ThreadHelper.sleep(100);
|
||||
}
|
||||
|
||||
var mode = OperationMode.map(msg.getMode());
|
||||
if (!mode.isSupported()) {
|
||||
throw new ClientException("Unsupported mode: " + msg.getMode().getDisplayName() + ". Supported: "
|
||||
+ String.join(
|
||||
", ",
|
||||
OperationMode.getAll().stream()
|
||||
.filter(OperationMode::isSupported)
|
||||
.map(OperationMode::getId)
|
||||
.toList()));
|
||||
}
|
||||
|
||||
OperationMode.switchToSyncIfPossible(mode);
|
||||
return ModeExchange.Response.builder()
|
||||
.usedMode(OperationMode.map(OperationMode.get()))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.ReadDrainExchange;
|
||||
|
||||
public class ReadDrainExchangeImpl extends ReadDrainExchange
|
||||
implements MessageExchangeImpl<ReadDrainExchange.Request, ReadDrainExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
return ReadDrainExchange.Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.cli.RemoveStoreExchange;
|
||||
import io.xpipe.core.store.DataStoreId;
|
||||
|
||||
public class RemoveStoreExchangeImpl extends RemoveStoreExchange
|
||||
implements MessageExchangeImpl<RemoveStoreExchange.Request, RemoveStoreExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
var s = getStoreEntryById(DataStoreId.fromString(msg.getStoreName()), true);
|
||||
if (!s.getConfiguration().isDeletable()) {
|
||||
throw new ClientException("Store is not deletable");
|
||||
}
|
||||
|
||||
DataStorage.get().deleteStoreEntry(s);
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.cli.RenameStoreExchange;
|
||||
import io.xpipe.core.store.DataStoreId;
|
||||
|
||||
public class RenameStoreExchangeImpl extends RenameStoreExchange
|
||||
implements MessageExchangeImpl<RenameStoreExchange.Request, RenameStoreExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws ClientException {
|
||||
var s = getStoreEntryById(DataStoreId.fromString(msg.getStoreName()), true);
|
||||
s.setName(msg.getNewName());
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.SinkExchange;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
|
||||
public class SinkExchangeImpl extends SinkExchange
|
||||
implements MessageExchangeImpl<SinkExchange.Request, SinkExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
|
||||
var ds = getStoreEntryById(msg.getSource(), false);
|
||||
|
||||
if (!(ds.getStore() instanceof ShellStore)) {
|
||||
throw new ClientException("Can't open file system for connection");
|
||||
}
|
||||
|
||||
ShellStore store = ds.getStore().asNeeded();
|
||||
try (var fs = store.createFileSystem();
|
||||
var inputStream = handler.receiveBody();
|
||||
var output = fs.openOutput(msg.getPath(), -1)) {
|
||||
inputStream.transferTo(output);
|
||||
}
|
||||
|
||||
return Response.builder().build();
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.StatusExchange;
|
||||
|
||||
public class StatusExchangeImpl extends StatusExchange
|
||||
implements MessageExchangeImpl<StatusExchange.Request, StatusExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
String mode;
|
||||
if (OperationMode.get() == null) {
|
||||
mode = "none";
|
||||
} else {
|
||||
mode = OperationMode.get().getId();
|
||||
}
|
||||
|
||||
return Response.builder().mode(mode).build();
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.core.mode.OperationMode;
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.StopExchange;
|
||||
|
||||
public class StopExchangeImpl extends StopExchange
|
||||
implements MessageExchangeImpl<StopExchange.Request, StopExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
handler.postResponse(() -> {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
ThreadHelper.sleep(1000);
|
||||
OperationMode.close();
|
||||
});
|
||||
});
|
||||
return Response.builder().success(true).build();
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.DialogExchangeImpl;
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.DataStoreProviders;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
|
||||
import io.xpipe.core.dialog.Choice;
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.dialog.QueryConverter;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StoreAddExchangeImpl extends StoreAddExchange
|
||||
implements MessageExchangeImpl<StoreAddExchange.Request, StoreAddExchange.Response> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public StoreAddExchange.Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
Dialog creatorDialog;
|
||||
DataStoreProvider provider;
|
||||
if (msg.getStoreInput() != null) {
|
||||
creatorDialog = Dialog.empty().evaluateTo(msg::getStoreInput);
|
||||
provider = null;
|
||||
} else {
|
||||
if (msg.getType() == null) {
|
||||
throw new ClientException("Missing data store tight");
|
||||
}
|
||||
|
||||
provider = DataStoreProviders.byName(msg.getType()).orElseThrow(() -> {
|
||||
return new ClientException("Unrecognized data store type: " + msg.getType());
|
||||
});
|
||||
|
||||
creatorDialog = provider.dialogForStore(provider.defaultStore());
|
||||
}
|
||||
|
||||
var name = new SimpleStringProperty(msg.getName());
|
||||
var completeDialog = createCompleteDialog(provider, creatorDialog, name);
|
||||
var config = DialogExchangeImpl.add(completeDialog, (DataStore store) -> {
|
||||
if (store == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DataStorage.get().addStoreIfNotPresent(name.getValue(), store);
|
||||
});
|
||||
|
||||
return StoreAddExchange.Response.builder().config(config).build();
|
||||
}
|
||||
|
||||
private Dialog createCompleteDialog(DataStoreProvider provider, Dialog creator, StringProperty name) {
|
||||
var validator = Dialog.header(() -> {
|
||||
DataStore store = creator.getResult();
|
||||
if (store == null) {
|
||||
return "Store is null";
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((String msg) -> {
|
||||
return msg == null ? creator.getResult() : null;
|
||||
});
|
||||
|
||||
var creatorAndValidator = Dialog.chain(creator, Dialog.busy(), validator);
|
||||
|
||||
var nameQ = Dialog.retryIf(
|
||||
Dialog.query("Store name", true, true, false, name.getValue(), QueryConverter.STRING),
|
||||
(String r) -> {
|
||||
return DataStorage.get().getStoreEntryIfPresent(r).isPresent()
|
||||
? "Store with name " + r + " already exists"
|
||||
: null;
|
||||
})
|
||||
.onCompletion((String n) -> name.setValue(n));
|
||||
|
||||
var display = Dialog.header(() -> {
|
||||
if (provider == null) {
|
||||
return "Successfully created data store " + name.get();
|
||||
}
|
||||
|
||||
DataStore s = creator.getResult();
|
||||
String d = "";
|
||||
d = d.indent(2);
|
||||
return "Successfully created data store " + name.get() + ":\n" + d;
|
||||
});
|
||||
|
||||
if (provider == null) {
|
||||
return Dialog.chain(
|
||||
creatorAndValidator, Dialog.skipIf(display, () -> creatorAndValidator.getResult() == null))
|
||||
.evaluateTo(creatorAndValidator);
|
||||
}
|
||||
|
||||
var aborted = new SimpleBooleanProperty();
|
||||
var addStore =
|
||||
Dialog.skipIf(Dialog.chain(nameQ, display), () -> aborted.get() || validator.getResult() == null);
|
||||
|
||||
var prop = new SimpleObjectProperty<Dialog>();
|
||||
var fork = Dialog.skipIf(
|
||||
Dialog.fork(
|
||||
"Choose how to continue",
|
||||
List.of(
|
||||
new Choice('r', "Retry"),
|
||||
new Choice('i', "Ignore and continue"),
|
||||
new Choice('e', "Edit configuration"),
|
||||
new Choice('a', "Abort")),
|
||||
true,
|
||||
0,
|
||||
(Integer choice) -> {
|
||||
if (choice == 0) {
|
||||
return Dialog.chain(Dialog.busy(), validator, prop.get());
|
||||
}
|
||||
if (choice == 1) {
|
||||
return null;
|
||||
}
|
||||
if (choice == 2) {
|
||||
return Dialog.chain(creatorAndValidator, prop.get());
|
||||
}
|
||||
if (choice == 3) {
|
||||
aborted.set(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new AssertionError();
|
||||
})
|
||||
.evaluateTo(() -> null),
|
||||
() -> validator.getResult() != null);
|
||||
prop.set(fork);
|
||||
|
||||
return Dialog.chain(creatorAndValidator, fork, addStore)
|
||||
.evaluateTo(() -> aborted.get() ? null : creator.getResult());
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package io.xpipe.app.exchange.cli;
|
||||
|
||||
import io.xpipe.app.exchange.MessageExchangeImpl;
|
||||
import io.xpipe.app.ext.DataStoreProvider;
|
||||
import io.xpipe.app.ext.DataStoreProviders;
|
||||
import io.xpipe.beacon.BeaconHandler;
|
||||
import io.xpipe.beacon.exchange.cli.StoreProviderListExchange;
|
||||
import io.xpipe.beacon.exchange.data.ProviderEntry;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StoreProviderListExchangeImpl extends StoreProviderListExchange
|
||||
implements MessageExchangeImpl<StoreProviderListExchange.Request, StoreProviderListExchange.Response> {
|
||||
|
||||
@Override
|
||||
public Response handleRequest(BeaconHandler handler, Request msg) {
|
||||
var categories = DataStoreProvider.CreationCategory.values();
|
||||
var all = DataStoreProviders.getAll();
|
||||
var map = Arrays.stream(categories)
|
||||
.collect(Collectors.toMap(category -> getName(category), category -> all.stream()
|
||||
.filter(dataStoreProvider -> category.equals(dataStoreProvider.getCreationCategory()))
|
||||
.map(p -> ProviderEntry.builder()
|
||||
.id(p.getId())
|
||||
.description(p.displayDescription().getValue())
|
||||
.hidden(p.getCreationCategory() == null)
|
||||
.build())
|
||||
.toList()));
|
||||
|
||||
return Response.builder().entries(map).build();
|
||||
}
|
||||
|
||||
private String getName(DataStoreProvider.CreationCategory category) {
|
||||
return category.name().substring(0, 1).toUpperCase()
|
||||
+ category.name().substring(1).toLowerCase();
|
||||
}
|
||||
}
|
|
@ -10,12 +10,11 @@ import io.xpipe.app.issue.TrackEvent;
|
|||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.beacon.BeaconServer;
|
||||
import io.xpipe.beacon.exchange.FocusExchange;
|
||||
import io.xpipe.beacon.exchange.OpenExchange;
|
||||
import io.xpipe.beacon.api.FocusExchange;
|
||||
import io.xpipe.beacon.api.OpenExchange;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import picocli.CommandLine;
|
||||
|
||||
|
@ -83,10 +82,8 @@ public class LauncherCommand implements Callable<Integer> {
|
|||
try {
|
||||
if (BeaconServer.isReachable()) {
|
||||
try (var con = new LauncherConnection()) {
|
||||
con.constructSocket();
|
||||
con.performSimpleExchange(FocusExchange.Request.builder()
|
||||
.mode(getEffectiveMode())
|
||||
.build());
|
||||
con.establishConnection();
|
||||
con.performSimpleExchange(FocusExchange.Request.builder().mode(getEffectiveMode()).build());
|
||||
if (!inputs.isEmpty()) {
|
||||
con.performSimpleExchange(
|
||||
OpenExchange.Request.builder().arguments(inputs).build());
|
||||
|
@ -94,9 +91,11 @@ public class LauncherCommand implements Callable<Integer> {
|
|||
|
||||
if (OsType.getLocal().equals(OsType.MACOS)) {
|
||||
Desktop.getDesktop().setOpenURIHandler(e -> {
|
||||
con.performSimpleExchange(OpenExchange.Request.builder()
|
||||
.arguments(List.of(e.getURI().toString()))
|
||||
.build());
|
||||
try {
|
||||
con.performSimpleExchange(OpenExchange.Request.builder().arguments(List.of(e.getURI().toString())).build());
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).expected().omit().handle();
|
||||
}
|
||||
});
|
||||
ThreadHelper.sleep(1000);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
package io.xpipe.app.launcher;
|
||||
|
||||
import io.xpipe.beacon.BeaconClient;
|
||||
import io.xpipe.beacon.BeaconClientInformation;
|
||||
import io.xpipe.beacon.BeaconConnection;
|
||||
import io.xpipe.beacon.BeaconException;
|
||||
import io.xpipe.beacon.BeaconConnectorException;
|
||||
|
||||
public class LauncherConnection extends BeaconConnection {
|
||||
|
||||
@Override
|
||||
protected void constructSocket() {
|
||||
protected void establishConnection() throws Exception {
|
||||
try {
|
||||
beaconClient = BeaconClient.establishConnection(
|
||||
BeaconClient.DaemonInformation.builder().build());
|
||||
BeaconClientInformation.DaemonInformation.builder().build());
|
||||
} catch (Exception ex) {
|
||||
throw new BeaconException("Unable to connect to running xpipe daemon", ex);
|
||||
throw new BeaconConnectorException("Unable to connect to running xpipe daemon", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.ServerException;
|
||||
import io.xpipe.beacon.BeaconClientException;
|
||||
import io.xpipe.beacon.BeaconServerException;
|
||||
import io.xpipe.core.process.ProcessControl;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.TerminalInitScriptConfig;
|
||||
import io.xpipe.core.process.WorkingDirectoryFunction;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.NonFinal;
|
||||
|
@ -73,10 +72,10 @@ public class TerminalLauncherManager {
|
|||
return latch;
|
||||
}
|
||||
|
||||
public static Path waitForCompletion(UUID request) throws ClientException, ServerException {
|
||||
public static Path waitForCompletion(UUID request) throws BeaconClientException, BeaconServerException {
|
||||
var e = entries.get(request);
|
||||
if (e == null) {
|
||||
throw new ClientException("Unknown launch request " + request);
|
||||
throw new BeaconClientException("Unknown launch request " + request);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
@ -89,21 +88,21 @@ public class TerminalLauncherManager {
|
|||
if (r instanceof ResultFailure failure) {
|
||||
entries.remove(request);
|
||||
var t = failure.getThrowable();
|
||||
throw new ServerException(t);
|
||||
throw new BeaconServerException(t);
|
||||
}
|
||||
|
||||
return ((ResultSuccess) r).getTargetScript();
|
||||
}
|
||||
}
|
||||
|
||||
public static Path performLaunch(UUID request) throws ClientException {
|
||||
public static Path performLaunch(UUID request) throws BeaconClientException {
|
||||
var e = entries.remove(request);
|
||||
if (e == null) {
|
||||
throw new ClientException("Unknown launch request " + request);
|
||||
throw new BeaconClientException("Unknown launch request " + request);
|
||||
}
|
||||
|
||||
if (!(e.result instanceof ResultSuccess)) {
|
||||
throw new ClientException("Invalid launch request state " + request);
|
||||
throw new BeaconClientException("Invalid launch request state " + request);
|
||||
}
|
||||
|
||||
return ((ResultSuccess) e.getResult()).getTargetScript();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import com.fasterxml.jackson.databind.Module;
|
||||
import io.xpipe.app.beacon.impl.*;
|
||||
import io.xpipe.app.browser.action.BrowserAction;
|
||||
import io.xpipe.app.core.AppLogs;
|
||||
import io.xpipe.app.exchange.*;
|
||||
import io.xpipe.app.exchange.cli.*;
|
||||
import io.xpipe.app.ext.*;
|
||||
import io.xpipe.app.issue.EventHandler;
|
||||
import io.xpipe.app.issue.EventHandlerImpl;
|
||||
|
@ -10,12 +10,11 @@ import io.xpipe.app.util.AppJacksonModule;
|
|||
import io.xpipe.app.util.LicenseProvider;
|
||||
import io.xpipe.app.util.ProxyManagerProviderImpl;
|
||||
import io.xpipe.app.util.TerminalLauncher;
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import io.xpipe.core.util.DataStateProvider;
|
||||
import io.xpipe.core.util.ModuleLayerLoader;
|
||||
import io.xpipe.core.util.ProxyFunction;
|
||||
import io.xpipe.core.util.ProxyManagerProvider;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import org.slf4j.spi.SLF4JServiceProvider;
|
||||
|
||||
open module io.xpipe.app {
|
||||
|
@ -52,6 +51,7 @@ open module io.xpipe.app {
|
|||
requires com.vladsch.flexmark;
|
||||
requires com.fasterxml.jackson.core;
|
||||
requires com.fasterxml.jackson.databind;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires net.synedra.validatorfx;
|
||||
requires org.kordamp.ikonli.feather;
|
||||
requires io.xpipe.modulefs;
|
||||
|
@ -79,14 +79,6 @@ open module io.xpipe.app {
|
|||
requires net.steppschuh.markdowngenerator;
|
||||
requires com.shinyhut.vernacular;
|
||||
|
||||
// Required by extensions
|
||||
requires java.security.jgss;
|
||||
requires java.security.sasl;
|
||||
requires java.xml;
|
||||
requires java.xml.crypto;
|
||||
requires java.sql;
|
||||
requires java.sql.rowset;
|
||||
|
||||
// Required runtime modules
|
||||
requires jdk.charsets;
|
||||
requires jdk.crypto.cryptoki;
|
||||
|
@ -100,8 +92,8 @@ open module io.xpipe.app {
|
|||
// For debugging
|
||||
requires jdk.jdwp.agent;
|
||||
requires org.kordamp.ikonli.core;
|
||||
requires jdk.httpserver;
|
||||
|
||||
uses MessageExchangeImpl;
|
||||
uses TerminalLauncher;
|
||||
uses io.xpipe.app.ext.ActionProvider;
|
||||
uses EventHandler;
|
||||
|
@ -113,11 +105,11 @@ open module io.xpipe.app {
|
|||
uses BrowserAction;
|
||||
uses LicenseProvider;
|
||||
uses io.xpipe.app.util.LicensedFeature;
|
||||
uses io.xpipe.beacon.BeaconInterface;
|
||||
|
||||
provides Module with
|
||||
AppJacksonModule;
|
||||
provides ModuleLayerLoader with
|
||||
MessageExchangeImpls.Loader,
|
||||
DataStoreProviders.Loader,
|
||||
ActionProvider.Loader,
|
||||
PrefsProvider.Loader,
|
||||
|
@ -132,26 +124,15 @@ open module io.xpipe.app {
|
|||
AppLogs.Slf4jProvider;
|
||||
provides EventHandler with
|
||||
EventHandlerImpl;
|
||||
provides MessageExchangeImpl with
|
||||
ReadDrainExchangeImpl,
|
||||
EditStoreExchangeImpl,
|
||||
StoreProviderListExchangeImpl,
|
||||
provides BeaconInterface with
|
||||
OpenExchangeImpl,
|
||||
LaunchExchangeImpl,
|
||||
FocusExchangeImpl,
|
||||
StatusExchangeImpl,
|
||||
DrainExchangeImpl,
|
||||
SinkExchangeImpl,
|
||||
StopExchangeImpl,
|
||||
HandshakeExchangeImpl,
|
||||
ModeExchangeImpl,
|
||||
DialogExchangeImpl,
|
||||
RemoveStoreExchangeImpl,
|
||||
RenameStoreExchangeImpl,
|
||||
ListStoresExchangeImpl,
|
||||
StoreAddExchangeImpl,
|
||||
AskpassExchangeImpl,
|
||||
TerminalWaitExchangeImpl,
|
||||
TerminalLaunchExchangeImpl,
|
||||
QueryStoreExchangeImpl,
|
||||
VersionExchangeImpl;
|
||||
}
|
||||
|
|
|
@ -1,67 +1,28 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.beacon.exchange.MessageExchanges;
|
||||
import io.xpipe.beacon.exchange.data.ClientErrorMessage;
|
||||
import io.xpipe.beacon.exchange.data.ServerErrorMessage;
|
||||
import io.xpipe.core.util.Deobfuscator;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.xpipe.beacon.api.HandshakeExchange;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
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 lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.xpipe.beacon.BeaconConfig.BODY_SEPARATOR;
|
||||
public class BeaconClient {
|
||||
|
||||
public class BeaconClient implements AutoCloseable {
|
||||
private String token;
|
||||
|
||||
@Getter
|
||||
private final AutoCloseable base;
|
||||
|
||||
private final InputStream in;
|
||||
private final OutputStream out;
|
||||
|
||||
private BeaconClient(AutoCloseable base, InputStream in, OutputStream out) {
|
||||
this.base = base;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public static BeaconClient establishConnection(ClientInformation information) throws Exception {
|
||||
var socket = new Socket();
|
||||
socket.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), BeaconConfig.getUsedPort()), 5000);
|
||||
socket.setSoTimeout(5000);
|
||||
var client = new BeaconClient(socket, socket.getInputStream(), socket.getOutputStream());
|
||||
client.sendObject(JacksonMapper.getDefault().valueToTree(information));
|
||||
var res = client.receiveObject();
|
||||
if (!res.isTextual() || !"ACK".equals(res.asText())) {
|
||||
throw new BeaconException("Daemon responded with invalid acknowledgement");
|
||||
}
|
||||
socket.setSoTimeout(0);
|
||||
public static BeaconClient establishConnection(BeaconClientInformation information) throws Exception {
|
||||
var client = new BeaconClient();
|
||||
HandshakeExchange.Response response = client.performRequest(HandshakeExchange.Request.builder().client(information).build());
|
||||
client.token = response.getToken();
|
||||
return client;
|
||||
}
|
||||
|
||||
public static Optional<BeaconClient> tryEstablishConnection(ClientInformation information) {
|
||||
public static Optional<BeaconClient> tryEstablishConnection(BeaconClientInformation information) {
|
||||
try {
|
||||
return Optional.of(establishConnection(information));
|
||||
} catch (Exception ex) {
|
||||
|
@ -69,246 +30,92 @@ public class BeaconClient implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
public void close() throws ConnectorException {
|
||||
try {
|
||||
base.close();
|
||||
} catch (Exception ex) {
|
||||
throw new ConnectorException("Couldn't close client", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream receiveBody() throws ConnectorException {
|
||||
try {
|
||||
var sep = in.readNBytes(BODY_SEPARATOR.length);
|
||||
if (sep.length != 0 && !Arrays.equals(BODY_SEPARATOR, sep)) {
|
||||
throw new ConnectorException("Invalid body separator");
|
||||
}
|
||||
return BeaconFormat.readBlocks(in);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream sendBody() throws ConnectorException {
|
||||
try {
|
||||
out.write(BODY_SEPARATOR);
|
||||
return BeaconFormat.writeBlocks(out);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends RequestMessage> void sendRequest(T req) throws ClientException, ConnectorException {
|
||||
ObjectNode json = JacksonMapper.getDefault().valueToTree(req);
|
||||
var prov = MessageExchanges.byRequest(req);
|
||||
if (prov.isEmpty()) {
|
||||
throw new ClientException("Unknown request class " + req.getClass());
|
||||
}
|
||||
|
||||
json.set("messageType", new TextNode(prov.get().getId()));
|
||||
json.set("messagePhase", new TextNode("request"));
|
||||
// json.set("id", new TextNode(UUID.randomUUID().toString()));
|
||||
var msg = JsonNodeFactory.instance.objectNode();
|
||||
msg.set("xPipeMessage", json);
|
||||
|
||||
if (BeaconConfig.printMessages()) {
|
||||
System.out.println(
|
||||
"Sending request to server of type " + req.getClass().getName());
|
||||
}
|
||||
|
||||
sendObject(msg);
|
||||
}
|
||||
|
||||
public void sendEOF() throws ConnectorException {
|
||||
try (OutputStream ignored = BeaconFormat.writeBlocks(out)) {
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't write to socket", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendObject(JsonNode node) throws ConnectorException {
|
||||
var writer = new StringWriter();
|
||||
var mapper = JacksonMapper.getDefault();
|
||||
try (JsonGenerator g = mapper.createGenerator(writer).setPrettyPrinter(new DefaultPrettyPrinter())) {
|
||||
g.writeTree(node);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't serialize request", ex);
|
||||
}
|
||||
|
||||
var content = writer.toString();
|
||||
@SuppressWarnings("unchecked")
|
||||
public <RES> RES performRequest(BeaconInterface<?> prov, String rawNode) throws
|
||||
BeaconConnectorException, BeaconClientException, BeaconServerException {
|
||||
var content = rawNode;
|
||||
if (BeaconConfig.printMessages()) {
|
||||
System.out.println("Sending raw request:");
|
||||
System.out.println(content);
|
||||
}
|
||||
|
||||
try (OutputStream blockOut = BeaconFormat.writeBlocks(out)) {
|
||||
blockOut.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't write to socket", ex);
|
||||
var client = HttpClient.newHttpClient();
|
||||
HttpResponse<String> response;
|
||||
try {
|
||||
var uri = URI.create("http://localhost:" + BeaconConfig.getUsedPort() + prov.getPath());
|
||||
var builder = HttpRequest.newBuilder();
|
||||
if (token != null) {
|
||||
builder.header("Authorization", "Bearer " + token);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends ResponseMessage> T receiveResponse() throws ConnectorException, ClientException, ServerException {
|
||||
return parseResponse(receiveObject());
|
||||
}
|
||||
|
||||
private JsonNode receiveObject() throws ConnectorException, ClientException, ServerException {
|
||||
JsonNode node;
|
||||
try (InputStream blockIn = BeaconFormat.readBlocks(in)) {
|
||||
node = JacksonMapper.getDefault().readTree(blockIn);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't read from socket", ex);
|
||||
var httpRequest = builder
|
||||
.uri(uri).POST(HttpRequest.BodyPublishers.ofString(content)).build();
|
||||
response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
|
||||
} catch (Exception ex) {
|
||||
throw new BeaconConnectorException("Couldn't send request", ex);
|
||||
}
|
||||
|
||||
if (BeaconConfig.printMessages()) {
|
||||
System.out.println("Received response:");
|
||||
System.out.println(node.toPrettyString());
|
||||
System.out.println("Received raw response:");
|
||||
System.out.println(response.body());
|
||||
}
|
||||
|
||||
if (node.isMissingNode()) {
|
||||
throw new ConnectorException("Received unexpected EOF");
|
||||
}
|
||||
|
||||
var se = parseServerError(node);
|
||||
var se = parseServerError(response);
|
||||
if (se.isPresent()) {
|
||||
se.get().throwError();
|
||||
}
|
||||
|
||||
var ce = parseClientError(node);
|
||||
var ce = parseClientError(response);
|
||||
if (ce.isPresent()) {
|
||||
throw ce.get().throwException();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private Optional<ClientErrorMessage> parseClientError(JsonNode node) throws ConnectorException {
|
||||
ObjectNode content = (ObjectNode) node.get("xPipeClientError");
|
||||
if (content == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
var message = JacksonMapper.getDefault().treeToValue(content, ClientErrorMessage.class);
|
||||
return Optional.of(message);
|
||||
var reader = JacksonMapper.getDefault().readerFor(prov.getResponseClass());
|
||||
var v = (RES) reader.readValue(response.body());
|
||||
return v;
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't parse client error message", ex);
|
||||
throw new BeaconConnectorException("Couldn't parse response", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<ServerErrorMessage> parseServerError(JsonNode node) throws ConnectorException {
|
||||
ObjectNode content = (ObjectNode) node.get("xPipeServerError");
|
||||
if (content == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
var message = JacksonMapper.getDefault().treeToValue(content, ServerErrorMessage.class);
|
||||
Deobfuscator.deobfuscate(message.getError());
|
||||
return Optional.of(message);
|
||||
} 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("messageType").textValue();
|
||||
var phase = content.required("messagePhase").textValue();
|
||||
// var requestId = UUID.fromString(content.required("id").textValue());
|
||||
if (!phase.equals("response")) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
content.remove("messageType");
|
||||
content.remove("messagePhase");
|
||||
// content.remove("id");
|
||||
|
||||
var prov = MessageExchanges.byId(type);
|
||||
public <REQ, RES> RES performRequest(REQ req) throws BeaconConnectorException, BeaconClientException, BeaconServerException {
|
||||
ObjectNode node = JacksonMapper.getDefault().valueToTree(req);
|
||||
var prov = BeaconInterface.byRequest(req);
|
||||
if (prov.isEmpty()) {
|
||||
throw new IllegalArgumentException("Unknown response id " + type);
|
||||
throw new IllegalArgumentException("Unknown request class " + req.getClass());
|
||||
}
|
||||
if (BeaconConfig.printMessages()) {
|
||||
System.out.println("Sending request to server of type " + req.getClass().getName());
|
||||
}
|
||||
|
||||
return performRequest(prov.get(), node.toPrettyString());
|
||||
}
|
||||
|
||||
private Optional<BeaconClientErrorResponse> parseClientError(HttpResponse<String> response) throws BeaconConnectorException {
|
||||
if (response.statusCode() < 400 || response.statusCode() > 499) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
var reader = JacksonMapper.getDefault().readerFor(prov.get().getResponseClass());
|
||||
return reader.readValue(content);
|
||||
var v = JacksonMapper.getDefault().readValue(response.body(), BeaconClientErrorResponse.class);
|
||||
return Optional.of(v);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException("Couldn't parse response", ex);
|
||||
throw new BeaconConnectorException("Couldn't parse client error message", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getRawInputStream() {
|
||||
return in;
|
||||
private Optional<BeaconServerErrorResponse> parseServerError(HttpResponse<String> response) throws BeaconConnectorException {
|
||||
if (response.statusCode() < 500 || response.statusCode() > 599) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public OutputStream getRawOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public abstract static class ClientInformation {
|
||||
|
||||
public final CliClientInformation cli() {
|
||||
return (CliClientInformation) this;
|
||||
}
|
||||
|
||||
public abstract String toDisplayString();
|
||||
}
|
||||
|
||||
@JsonTypeName("cli")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class CliClientInformation extends ClientInformation {
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return "XPipe CLI";
|
||||
try {
|
||||
var v = JacksonMapper.getDefault().readValue(response.body(), BeaconServerErrorResponse.class);
|
||||
return Optional.of(v);
|
||||
} catch (IOException ex) {
|
||||
throw new BeaconConnectorException("Couldn't parse client error message", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("daemon")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class DaemonInformation extends ClientInformation {
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return "Daemon";
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("gateway")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class GatewayClientInformation extends ClientInformation {
|
||||
|
||||
String version;
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return "XPipe Gateway " + version;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("api")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class ApiClientInformation extends ClientInformation {
|
||||
|
||||
String version;
|
||||
String language;
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return String.format("XPipe %s API v%s", language, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
package io.xpipe.beacon.exchange.data;
|
||||
|
||||
import io.xpipe.beacon.ClientException;
|
||||
package io.xpipe.beacon;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
|
@ -12,11 +10,11 @@ import lombok.extern.jackson.Jacksonized;
|
|||
@Builder
|
||||
@Jacksonized
|
||||
@AllArgsConstructor
|
||||
public class ClientErrorMessage {
|
||||
public class BeaconClientErrorResponse {
|
||||
|
||||
String message;
|
||||
|
||||
public ClientException throwException() {
|
||||
return new ClientException(message);
|
||||
public BeaconClientException throwException() {
|
||||
return new BeaconClientException(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* Indicates that a client request was invalid.
|
||||
*/
|
||||
public class BeaconClientException extends Exception {
|
||||
|
||||
public BeaconClientException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BeaconClientException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BeaconClientException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BeaconClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
property = "type")
|
||||
public abstract class BeaconClientInformation {
|
||||
|
||||
public final CliClientInformation cli() {
|
||||
return (CliClientInformation) this;
|
||||
}
|
||||
|
||||
public abstract String toDisplayString();
|
||||
|
||||
@JsonTypeName("cli")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class CliClientInformation extends BeaconClientInformation {
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return "XPipe CLI";
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("daemon")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class DaemonInformation extends BeaconClientInformation {
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return "Daemon";
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("gateway")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class GatewayClientInformation extends BeaconClientInformation {
|
||||
|
||||
String version;
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return "XPipe Gateway " + version;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("api")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public static class ApiClientInformation extends BeaconClientInformation {
|
||||
|
||||
String version;
|
||||
String language;
|
||||
|
||||
@Override
|
||||
public String toDisplayString() {
|
||||
return String.format("XPipe %s API v%s", language, version);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,11 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.core.util.XPipeInstallation;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@UtilityClass
|
||||
public class BeaconConfig {
|
||||
|
||||
public static final byte[] BODY_SEPARATOR = "\n\n".getBytes(StandardCharsets.UTF_8);
|
||||
public static final String BEACON_PORT_PROP = "io.xpipe.beacon.port";
|
||||
public static final String DAEMON_ARGUMENTS_PROP = "io.xpipe.beacon.daemonArgs";
|
||||
private static final String PRINT_MESSAGES_PROPERTY = "io.xpipe.beacon.printMessages";
|
||||
|
@ -17,14 +13,6 @@ public class BeaconConfig {
|
|||
private static final String ATTACH_DEBUGGER_PROP = "io.xpipe.beacon.attachDebuggerToDaemon";
|
||||
private static final String EXEC_DEBUG_PROP = "io.xpipe.beacon.printDaemonOutput";
|
||||
private static final String EXEC_PROCESS_PROP = "io.xpipe.beacon.customDaemonCommand";
|
||||
private static final String LOCAL_PROXY_PROP = "io.xpipe.beacon.localProxy";
|
||||
|
||||
public static boolean localProxy() {
|
||||
if (System.getProperty(LOCAL_PROXY_PROP) != null) {
|
||||
return Boolean.parseBoolean(System.getProperty(LOCAL_PROXY_PROP));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean printMessages() {
|
||||
if (System.getProperty(PRINT_MESSAGES_PROPERTY) != null) {
|
||||
|
|
|
@ -1,189 +1,27 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.core.util.FailableBiConsumer;
|
||||
import io.xpipe.core.util.FailableConsumer;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
@Getter
|
||||
public abstract class BeaconConnection implements AutoCloseable {
|
||||
|
||||
@Getter
|
||||
protected BeaconClient beaconClient;
|
||||
|
||||
private InputStream bodyInput;
|
||||
private OutputStream bodyOutput;
|
||||
|
||||
protected abstract void constructSocket();
|
||||
protected abstract void establishConnection() throws Exception;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
if (beaconClient != null) {
|
||||
beaconClient.close();
|
||||
}
|
||||
beaconClient = null;
|
||||
} catch (Exception e) {
|
||||
beaconClient = null;
|
||||
throw new BeaconException("Could not close beacon connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void withOutputStream(FailableConsumer<OutputStream, IOException> ex) {
|
||||
try {
|
||||
ex.accept(getOutputStream());
|
||||
} catch (IOException e) {
|
||||
throw new BeaconException("Could not write to beacon output stream", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void withInputStream(FailableConsumer<InputStream, IOException> ex) {
|
||||
try {
|
||||
ex.accept(getInputStream());
|
||||
} catch (IOException e) {
|
||||
throw new BeaconException("Could not read from beacon output stream", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkClosed() {
|
||||
if (beaconClient == null) {
|
||||
throw new BeaconException("Socket is closed");
|
||||
throw new IllegalStateException("Socket is closed");
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
public <REQ, RES> RES performSimpleExchange(REQ req) throws Exception {
|
||||
checkClosed();
|
||||
|
||||
if (bodyOutput == null) {
|
||||
throw new IllegalStateException("Body output has not started yet");
|
||||
}
|
||||
|
||||
return bodyOutput;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
checkClosed();
|
||||
|
||||
if (bodyInput == null) {
|
||||
throw new IllegalStateException("Body input has not started yet");
|
||||
}
|
||||
|
||||
return bodyInput;
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> void performInputExchange(
|
||||
REQ req, FailableBiConsumer<RES, InputStream, Exception> responseConsumer) {
|
||||
checkClosed();
|
||||
|
||||
performInputOutputExchange(req, null, responseConsumer);
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> void performInputOutputExchange(
|
||||
REQ req,
|
||||
FailableConsumer<OutputStream, IOException> reqWriter,
|
||||
FailableBiConsumer<RES, InputStream, Exception> responseConsumer) {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
beaconClient.sendRequest(req);
|
||||
if (reqWriter != null) {
|
||||
try (var out = sendBody()) {
|
||||
reqWriter.accept(out);
|
||||
}
|
||||
}
|
||||
RES res = beaconClient.receiveResponse();
|
||||
try (var in = receiveBody()) {
|
||||
responseConsumer.accept(res, in);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage> void sendRequest(REQ req) {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
beaconClient.sendRequest(req);
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <RES extends ResponseMessage> RES receiveResponse() {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
return beaconClient.receiveResponse();
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream sendBody() {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
bodyOutput = beaconClient.sendBody();
|
||||
return bodyOutput;
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream receiveBody() {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
bodyInput = beaconClient.receiveBody();
|
||||
return bodyInput;
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> RES performOutputExchange(
|
||||
REQ req, FailableConsumer<OutputStream, Exception> reqWriter) {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
beaconClient.sendRequest(req);
|
||||
try (var out = sendBody()) {
|
||||
reqWriter.accept(out);
|
||||
}
|
||||
return beaconClient.receiveResponse();
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> RES performSimpleExchange(REQ req) {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
beaconClient.sendRequest(req);
|
||||
return beaconClient.receiveResponse();
|
||||
} catch (Exception e) {
|
||||
throw unwrapException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private BeaconException unwrapException(Exception exception) {
|
||||
if (exception instanceof ServerException s) {
|
||||
return new BeaconException("An internal server error occurred", s);
|
||||
}
|
||||
|
||||
if (exception instanceof ClientException s) {
|
||||
return new BeaconException("A client error occurred", s);
|
||||
}
|
||||
|
||||
if (exception instanceof ConnectorException s) {
|
||||
return new BeaconException("A beacon connection error occurred", s);
|
||||
}
|
||||
|
||||
return new BeaconException("An unexpected error occurred", exception);
|
||||
return beaconClient.performRequest(req);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* Indicates that a connection error occurred.
|
||||
*/
|
||||
public class BeaconConnectorException extends Exception {
|
||||
|
||||
public BeaconConnectorException() {}
|
||||
|
||||
public BeaconConnectorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BeaconConnectorException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BeaconConnectorException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BeaconConnectorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* An unchecked exception that will be thrown in any case of an underlying exception.
|
||||
*/
|
||||
public class BeaconException extends RuntimeException {
|
||||
|
||||
public BeaconException() {}
|
||||
|
||||
public BeaconException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BeaconException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BeaconException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BeaconException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@UtilityClass
|
||||
public class BeaconFormat {
|
||||
|
||||
private static final int SEGMENT_SIZE = 65536;
|
||||
|
||||
public static OutputStream writeBlocks(OutputStream out) {
|
||||
return new OutputStream() {
|
||||
private final byte[] currentBytes = new byte[SEGMENT_SIZE];
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
if (isClosed()) {
|
||||
throw new IllegalStateException("Output is closed");
|
||||
}
|
||||
|
||||
if (index == currentBytes.length) {
|
||||
finishBlock();
|
||||
}
|
||||
|
||||
currentBytes[index] = (byte) b;
|
||||
index++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
finishBlock();
|
||||
out.flush();
|
||||
index = -1;
|
||||
}
|
||||
|
||||
private boolean isClosed() {
|
||||
return index == -1;
|
||||
}
|
||||
|
||||
private void finishBlock() throws IOException {
|
||||
if (isClosed()) {
|
||||
throw new IllegalStateException("Output is closed");
|
||||
}
|
||||
|
||||
if (BeaconConfig.printMessages()) {
|
||||
System.out.println("Sending data block of length " + index);
|
||||
}
|
||||
|
||||
int length = index;
|
||||
var lengthBuffer = ByteBuffer.allocate(4).putInt(length);
|
||||
out.write(lengthBuffer.array());
|
||||
out.write(currentBytes, 0, length);
|
||||
index = 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static InputStream readBlocks(InputStream in) {
|
||||
return new InputStream() {
|
||||
|
||||
private byte[] currentBytes;
|
||||
private int index;
|
||||
private boolean lastBlock;
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if ((currentBytes == null || index == currentBytes.length) && !lastBlock) {
|
||||
if (!readBlock()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentBytes != null && index == currentBytes.length && lastBlock) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int out = currentBytes[index] & 0xff;
|
||||
index++;
|
||||
return out;
|
||||
}
|
||||
|
||||
private boolean readBlock() throws IOException {
|
||||
var length = in.readNBytes(4);
|
||||
if (length.length < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var lengthInt = ByteBuffer.wrap(length).getInt();
|
||||
|
||||
if (BeaconConfig.printMessages()) {
|
||||
System.out.println("Receiving data block of length " + lengthInt);
|
||||
}
|
||||
|
||||
currentBytes = in.readNBytes(lengthInt);
|
||||
index = 0;
|
||||
if (lengthInt < SEGMENT_SIZE) {
|
||||
lastBlock = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.core.util.FailableRunnable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An exchange handler responsible for properly handling a request and sending a response.
|
||||
*/
|
||||
public interface BeaconHandler {
|
||||
|
||||
/**
|
||||
* Execute a Runnable after the initial response has been sent.
|
||||
*
|
||||
* @param r the runnable to execute
|
||||
*/
|
||||
void postResponse(FailableRunnable<Exception> r);
|
||||
|
||||
/**
|
||||
* Prepares to attach a body to a response.
|
||||
*
|
||||
* @return the output stream that can be used to write the body payload
|
||||
*/
|
||||
OutputStream sendBody() throws IOException;
|
||||
|
||||
/**
|
||||
* Prepares to read an attached body of a request.
|
||||
*
|
||||
* @return the input stream that can be used to read the body payload
|
||||
*/
|
||||
InputStream receiveBody() throws IOException;
|
||||
}
|
74
beacon/src/main/java/io/xpipe/beacon/BeaconInterface.java
Normal file
74
beacon/src/main/java/io/xpipe/beacon/BeaconInterface.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import io.xpipe.core.util.ModuleLayerLoader;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class BeaconInterface<T> {
|
||||
|
||||
private static List<BeaconInterface<?>> ALL;
|
||||
|
||||
public static List<BeaconInterface<?>> getAll() {
|
||||
return ALL;
|
||||
}
|
||||
|
||||
public static Optional<BeaconInterface<?>> byPath(String path) {
|
||||
return ALL.stream()
|
||||
.filter(d -> d.getPath().equals(path))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
|
||||
public static <RQ> Optional<BeaconInterface<?>> byRequest(RQ req) {
|
||||
return ALL.stream()
|
||||
.filter(d -> d.getRequestClass().equals(req.getClass()))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public static class Loader implements ModuleLayerLoader {
|
||||
|
||||
@Override
|
||||
public void init(ModuleLayer layer) {
|
||||
var services = layer != null ? ServiceLoader.load(layer, BeaconInterface.class) : ServiceLoader.load(BeaconInterface.class);
|
||||
ALL = services.stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.map(beaconInterface -> (BeaconInterface<?>) beaconInterface)
|
||||
.collect(Collectors.toList());
|
||||
// Remove parent classes
|
||||
ALL.removeIf(beaconInterface -> ALL.stream().anyMatch(other ->
|
||||
!other.equals(beaconInterface) && beaconInterface.getClass().isAssignableFrom(other.getClass())));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public Class<T> getRequestClass() {
|
||||
var c = getClass().getSuperclass();
|
||||
var name = (c.getSuperclass().equals(BeaconInterface.class) ? c : getClass()).getName() + "$Request";
|
||||
return (Class<T>) Class.forName(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public Class<T> getResponseClass() {
|
||||
var c = getClass().getSuperclass();
|
||||
var name = (c.getSuperclass().equals(BeaconInterface.class) ? c : getClass()).getName() + "$Response";
|
||||
return (Class<T>) Class.forName(name);
|
||||
}
|
||||
|
||||
public boolean requiresAuthentication() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract String getPath();
|
||||
|
||||
public Object handle(HttpExchange exchange, T body) throws IOException, BeaconClientException, BeaconServerException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -8,8 +8,8 @@ public class BeaconJacksonModule extends SimpleModule {
|
|||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.registerSubtypes(
|
||||
new NamedType(BeaconClient.ApiClientInformation.class),
|
||||
new NamedType(BeaconClient.CliClientInformation.class),
|
||||
new NamedType(BeaconClient.DaemonInformation.class));
|
||||
new NamedType(BeaconClientInformation.ApiClientInformation.class),
|
||||
new NamedType(BeaconClientInformation.CliClientInformation.class),
|
||||
new NamedType(BeaconClientInformation.DaemonInformation.class));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.beacon.exchange.StopExchange;
|
||||
import io.xpipe.beacon.api.StopExchange;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
|
@ -108,8 +108,7 @@ public class BeaconServer {
|
|||
}
|
||||
|
||||
public static boolean tryStop(BeaconClient client) throws Exception {
|
||||
client.sendRequest(StopExchange.Request.builder().build());
|
||||
StopExchange.Response res = client.receiveResponse();
|
||||
StopExchange.Response res = client.performRequest(StopExchange.Request.builder().build());
|
||||
return res.isSuccess();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
@Value
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@AllArgsConstructor
|
||||
public class BeaconServerErrorResponse {
|
||||
|
||||
Throwable error;
|
||||
|
||||
public void throwError() throws BeaconServerException {
|
||||
throw new BeaconServerException(error.getMessage(), error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* Indicates that an internal server error occurred.
|
||||
*/
|
||||
public class BeaconServerException extends Exception {
|
||||
|
||||
public BeaconServerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BeaconServerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BeaconServerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BeaconServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* Indicates that a client request caused an issue.
|
||||
*/
|
||||
public class ClientException extends Exception {
|
||||
|
||||
public ClientException() {}
|
||||
|
||||
public ClientException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ClientException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ClientException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* Indicates that a connection error occurred.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
public interface RequestMessage {}
|
|
@ -1,3 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
public interface ResponseMessage {}
|
|
@ -1,25 +0,0 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
/**
|
||||
* Indicates that an internal server error occurred.
|
||||
*/
|
||||
public class ServerException extends Exception {
|
||||
|
||||
public ServerException() {}
|
||||
|
||||
public ServerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ServerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
@ -11,17 +9,17 @@ import lombok.extern.jackson.Jacksonized;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
public class AskpassExchange implements MessageExchange {
|
||||
public class AskpassExchange extends BeaconInterface<AskpassExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "askpass";
|
||||
public String getPath() {
|
||||
return "/askpass";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
public static class Request {
|
||||
UUID secretId;
|
||||
|
||||
@NonNull
|
||||
|
@ -33,7 +31,7 @@ public class AskpassExchange implements MessageExchange {
|
|||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
public static class Response {
|
||||
SecretValue value;
|
||||
}
|
||||
}
|
29
beacon/src/main/java/io/xpipe/beacon/api/FocusExchange.java
Normal file
29
beacon/src/main/java/io/xpipe/beacon/api/FocusExchange.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class FocusExchange extends BeaconInterface<FocusExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/focus";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
@NonNull
|
||||
XPipeDaemonMode mode;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconClientInformation;
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class HandshakeExchange extends BeaconInterface<HandshakeExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/handshake";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresAuthentication() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
BeaconClientInformation client;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {
|
||||
String token;
|
||||
}
|
||||
}
|
32
beacon/src/main/java/io/xpipe/beacon/api/ModeExchange.java
Normal file
32
beacon/src/main/java/io/xpipe/beacon/api/ModeExchange.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class ModeExchange extends BeaconInterface<ModeExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/mode";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
@NonNull
|
||||
XPipeDaemonMode mode;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {
|
||||
@NonNull
|
||||
XPipeDaemonMode usedMode;
|
||||
}
|
||||
}
|
30
beacon/src/main/java/io/xpipe/beacon/api/OpenExchange.java
Normal file
30
beacon/src/main/java/io/xpipe/beacon/api/OpenExchange.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OpenExchange extends BeaconInterface<OpenExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/open";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
@NonNull
|
||||
List<String> arguments;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {}
|
||||
}
|
27
beacon/src/main/java/io/xpipe/beacon/api/StatusExchange.java
Normal file
27
beacon/src/main/java/io/xpipe/beacon/api/StatusExchange.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class StatusExchange extends BeaconInterface<StatusExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/status";
|
||||
}
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public static class Request {
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {
|
||||
String mode;
|
||||
}
|
||||
}
|
30
beacon/src/main/java/io/xpipe/beacon/api/StopExchange.java
Normal file
30
beacon/src/main/java/io/xpipe/beacon/api/StopExchange.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
/**
|
||||
* Requests the daemon to stop.
|
||||
*/
|
||||
public class StopExchange extends BeaconInterface<StopExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/stop";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {
|
||||
boolean success;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
@ -11,17 +9,17 @@ import lombok.extern.jackson.Jacksonized;
|
|||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TerminalLaunchExchange implements MessageExchange {
|
||||
public class TerminalLaunchExchange extends BeaconInterface<TerminalLaunchExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "terminalLaunch";
|
||||
public String getPath() {
|
||||
return "/terminalLaunch";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
public static class Request {
|
||||
@NonNull
|
||||
UUID request;
|
||||
}
|
||||
|
@ -29,7 +27,7 @@ public class TerminalLaunchExchange implements MessageExchange {
|
|||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
public static class Response {
|
||||
@NonNull
|
||||
Path targetFile;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class TerminalWaitExchange extends BeaconInterface<TerminalWaitExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/terminalWait";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
@NonNull
|
||||
UUID request;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.beacon.api;
|
||||
|
||||
import io.xpipe.beacon.BeaconInterface;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class VersionExchange extends BeaconInterface<VersionExchange.Request> {
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/version";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request {
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response {
|
||||
|
||||
String version;
|
||||
String buildVersion;
|
||||
String jvmVersion;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.core.store.DataStoreId;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class DrainExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "drain";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
DataStoreId source;
|
||||
|
||||
@NonNull
|
||||
String path;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class FocusExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "focus";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
XPipeDaemonMode mode;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.core.store.DataStoreId;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LaunchExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "launch";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
DataStoreId id;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
List<String> command;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* A message exchange scheme that implements a certain functionality.
|
||||
*/
|
||||
public interface MessageExchange {
|
||||
|
||||
/**
|
||||
* The unique id of this exchange that will be included in the messages.
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Returns the request class, needed for serialization.
|
||||
*/
|
||||
@SneakyThrows
|
||||
default Class<?> getRequestClass() {
|
||||
var c = getClass().getSuperclass();
|
||||
var name = (MessageExchange.class.isAssignableFrom(c) ? c : getClass()).getName() + "$Request";
|
||||
return Class.forName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response class, needed for serialization.
|
||||
*/
|
||||
@SneakyThrows
|
||||
default Class<?> getResponseClass() {
|
||||
var c = getClass().getSuperclass();
|
||||
var name = (MessageExchange.class.isAssignableFrom(c) ? c : getClass()).getName() + "$Response";
|
||||
return Class.forName(name);
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MessageExchanges {
|
||||
|
||||
private static List<MessageExchange> ALL;
|
||||
|
||||
public static void loadAll() {
|
||||
if (ALL == null) {
|
||||
ALL = ServiceLoader.load(MessageExchange.class).stream()
|
||||
.map(s -> {
|
||||
return s.get();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<MessageExchange> byId(String name) {
|
||||
loadAll();
|
||||
return ALL.stream().filter(d -> d.getId().equals(name)).findAny();
|
||||
}
|
||||
|
||||
public static <RQ extends RequestMessage> Optional<MessageExchange> byRequest(RQ req) {
|
||||
loadAll();
|
||||
return ALL.stream()
|
||||
.filter(d -> d.getRequestClass().equals(req.getClass()))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public static <RP extends ResponseMessage> Optional<MessageExchange> byResponse(RP rep) {
|
||||
loadAll();
|
||||
return ALL.stream()
|
||||
.filter(d -> d.getResponseClass().equals(rep.getClass()))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
public static List<MessageExchange> getAll() {
|
||||
loadAll();
|
||||
return ALL;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OpenExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "open";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
List<String> arguments;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Queries general information about a data source.
|
||||
*/
|
||||
public class QueryStoreExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "queryStore";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String name;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
String name;
|
||||
|
||||
String information;
|
||||
|
||||
String summary;
|
||||
|
||||
@NonNull
|
||||
String provider;
|
||||
|
||||
@NonNull
|
||||
LinkedHashMap<String, String> config;
|
||||
|
||||
DataStore internalStore;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.core.store.DataStoreId;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class SinkExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "sink";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
DataStoreId source;
|
||||
|
||||
@NonNull
|
||||
String path;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
/**
|
||||
* Requests the daemon to stop.
|
||||
*/
|
||||
public class StopExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "stop";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
boolean success;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class TerminalWaitExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "terminalWait";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
UUID request;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.core.dialog.DialogElement;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DialogExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "dialog";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<DialogExchange.Request> getRequestClass() {
|
||||
return DialogExchange.Request.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<DialogExchange.Response> getResponseClass() {
|
||||
return DialogExchange.Response.class;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
UUID dialogKey;
|
||||
|
||||
String value;
|
||||
boolean cancel;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
DialogElement element;
|
||||
String errorMsg;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.core.dialog.DialogReference;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class EditStoreExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "editEntry";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String name;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
DialogReference dialog;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.beacon.exchange.data.CollectionListEntry;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ListCollectionsExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "listCollections";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
List<CollectionListEntry> entries;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.beacon.exchange.data.EntryListEntry;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ListEntriesExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "listEntries";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
String collection;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
List<EntryListEntry> entries;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.beacon.exchange.data.StoreListEntry;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ListStoresExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "listStores";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
List<StoreListEntry> entries;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.core.util.XPipeDaemonMode;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class ModeExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "mode";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
XPipeDaemonMode mode;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
XPipeDaemonMode usedMode;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class ReadDrainExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "readDrain";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String name;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class RemoveCollectionExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "removeCollection";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String collectionName;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class RemoveStoreExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "removeStore";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String storeName;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class RenameCollectionExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "renameCollection";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String collectionName;
|
||||
|
||||
@NonNull
|
||||
String newName;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class RenameStoreExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "renameStore";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
String storeName;
|
||||
|
||||
@NonNull
|
||||
String newName;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class StatusExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "status";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
String mode;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.core.dialog.DialogReference;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class StoreAddExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "storeAdd";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
DataStore storeInput;
|
||||
|
||||
String type;
|
||||
String name;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
DialogReference config;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.beacon.exchange.data.ProviderEntry;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class StoreProviderListExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "storeProviderList";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
@NonNull
|
||||
Map<String, List<ProviderEntry>> entries;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class VersionExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "version";
|
||||
}
|
||||
|
||||
@lombok.extern.jackson.Jacksonized
|
||||
@lombok.Builder
|
||||
@lombok.Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
|
||||
String version;
|
||||
String buildVersion;
|
||||
String jvmVersion;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.data;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public class CollectionListEntry {
|
||||
|
||||
String name;
|
||||
int size;
|
||||
Instant lastUsed;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.data;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public class EntryListEntry {
|
||||
String name;
|
||||
String type;
|
||||
String description;
|
||||
Instant lastUsed;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package io.xpipe.beacon.exchange.data;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public class ProviderEntry {
|
||||
String id;
|
||||
String description;
|
||||
boolean hidden;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue