mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-09-27 19:05:46 +00:00
Rework beacon connection and implement various improvements
This commit is contained in:
@@ -104,31 +104,27 @@ public class BeaconClient implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveBody() throws ConnectorException {
|
||||
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(socket);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void startBody() throws ConnectorException {
|
||||
public OutputStream sendBody() throws ConnectorException {
|
||||
try {
|
||||
out.write(BODY_SEPARATOR);
|
||||
return BeaconFormat.writeBlocks(socket);
|
||||
} catch (IOException ex) {
|
||||
throw new ConnectorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> RES simpleExchange(REQ req)
|
||||
throws ServerException, ConnectorException, ClientException {
|
||||
sendRequest(req);
|
||||
return this.receiveResponse();
|
||||
}
|
||||
|
||||
public <T extends RequestMessage> void sendRequest(T req) throws ClientException, ConnectorException {
|
||||
ObjectNode json = JacksonHelper.newMapper().valueToTree(req);
|
||||
var prov = MessageExchanges.byRequest(req);
|
||||
@@ -245,4 +241,8 @@ public class BeaconClient implements AutoCloseable {
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ public abstract class BeaconConnection implements AutoCloseable {
|
||||
|
||||
protected BeaconClient socket;
|
||||
|
||||
private InputStream bodyInput;
|
||||
private OutputStream bodyOutput;
|
||||
|
||||
protected abstract void constructSocket();
|
||||
|
||||
@Override
|
||||
@@ -26,14 +29,6 @@ public abstract class BeaconConnection implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public void closeOutput() {
|
||||
try {
|
||||
socket.getOutputStream().close();
|
||||
} catch (Exception e) {
|
||||
throw new BeaconException("Could not close beacon output stream", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void withOutputStream(BeaconClient.FailableConsumer<OutputStream, IOException> ex) {
|
||||
try {
|
||||
ex.accept(getOutputStream());
|
||||
@@ -59,13 +54,21 @@ public abstract class BeaconConnection implements AutoCloseable {
|
||||
public OutputStream getOutputStream() {
|
||||
checkClosed();
|
||||
|
||||
return socket.getOutputStream();
|
||||
if (bodyOutput == null) {
|
||||
throw new IllegalStateException("Body output has not started yet");
|
||||
}
|
||||
|
||||
return bodyOutput;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
checkClosed();
|
||||
|
||||
return socket.getInputStream();
|
||||
if (bodyInput == null) {
|
||||
throw new IllegalStateException("Body input has not started yet");
|
||||
}
|
||||
|
||||
return bodyInput;
|
||||
}
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> void performInputExchange(
|
||||
@@ -83,7 +86,16 @@ public abstract class BeaconConnection implements AutoCloseable {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
socket.exchange(req, reqWriter, responseConsumer);
|
||||
socket.sendRequest(req);
|
||||
if (reqWriter != null) {
|
||||
try (var out = socket.sendBody()) {
|
||||
reqWriter.accept(out);
|
||||
}
|
||||
}
|
||||
RES res = socket.receiveResponse();
|
||||
try (var in = socket.receiveBody()) {
|
||||
responseConsumer.accept(res, in);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BeaconException("Could not communicate with beacon", e);
|
||||
}
|
||||
@@ -110,21 +122,23 @@ public abstract class BeaconConnection implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendBodyStart() {
|
||||
public OutputStream sendBody() {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
socket.startBody();
|
||||
bodyOutput = socket.sendBody();
|
||||
return bodyOutput;
|
||||
} catch (Exception e) {
|
||||
throw new BeaconException("Could not communicate with beacon", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void receiveBody() {
|
||||
public InputStream receiveBody() {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
socket.receiveBody();
|
||||
bodyInput = socket.receiveBody();
|
||||
return bodyInput;
|
||||
} catch (Exception e) {
|
||||
throw new BeaconException("Could not communicate with beacon", e);
|
||||
}
|
||||
@@ -137,25 +151,22 @@ public abstract class BeaconConnection implements AutoCloseable {
|
||||
|
||||
try {
|
||||
socket.sendRequest(req);
|
||||
socket.startBody();
|
||||
reqWriter.accept(socket.getOutputStream());
|
||||
try (var out = socket.sendBody()) {
|
||||
reqWriter.accept(out);
|
||||
}
|
||||
return socket.receiveResponse();
|
||||
} catch (Exception e) {
|
||||
throw new BeaconException("Could not communicate with beacon", e);
|
||||
}
|
||||
}
|
||||
|
||||
// public void writeLength(int bytes) throws IOException {
|
||||
// checkClosed();
|
||||
// socket.getOutputStream().write(ByteBuffer.allocate(4).putInt(bytes).array());
|
||||
// }
|
||||
|
||||
public <REQ extends RequestMessage, RES extends ResponseMessage> RES performSimpleExchange(
|
||||
REQ req) {
|
||||
checkClosed();
|
||||
|
||||
try {
|
||||
return socket.simpleExchange(req);
|
||||
socket.sendRequest(req);
|
||||
return socket.receiveResponse();
|
||||
} catch (Exception e) {
|
||||
throw new BeaconException("Could not communicate with beacon", e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package io.xpipe.beacon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class BeaconFormat {
|
||||
|
||||
public static OutputStream writeBlocks(Socket socket) throws IOException {
|
||||
int size = 65536 - 4;
|
||||
var out = socket.getOutputStream();
|
||||
return new OutputStream() {
|
||||
private final byte[] currentBytes = new byte[size];
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
finishBlock();
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
if (index == currentBytes.length) {
|
||||
finishBlock();
|
||||
}
|
||||
|
||||
currentBytes[index] = (byte) b;
|
||||
index++;
|
||||
}
|
||||
|
||||
private void finishBlock() throws IOException {
|
||||
if (BeaconConfig.debugEnabled()) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
// while (true) {
|
||||
// var bytes = in.readNBytes(size);
|
||||
// int length = bytes.length;
|
||||
// var lengthBuffer = ByteBuffer.allocate(4).putInt(length);
|
||||
// socket.getOutputStream().write(lengthBuffer.array());
|
||||
// socket.getOutputStream().write(bytes);
|
||||
//
|
||||
// if (length == 0) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public static InputStream readBlocks(Socket socket) throws IOException {
|
||||
int size = 65536 - 4;
|
||||
var in = socket.getInputStream();
|
||||
return new InputStream() {
|
||||
|
||||
private byte[] currentBytes;
|
||||
private int index;
|
||||
private boolean finished;
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if ((currentBytes == null || index == currentBytes.length) && !finished) {
|
||||
readBlock();
|
||||
}
|
||||
|
||||
if (currentBytes != null && index == currentBytes.length && finished) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int out = currentBytes[index];
|
||||
index++;
|
||||
return out;
|
||||
}
|
||||
|
||||
private void readBlock() throws IOException {
|
||||
var length = in.readNBytes(4);
|
||||
var lengthInt = ByteBuffer.wrap(length).getInt();
|
||||
|
||||
if (BeaconConfig.debugEnabled()) {
|
||||
System.out.println("Receiving data block of length " + lengthInt);
|
||||
}
|
||||
|
||||
currentBytes = in.readNBytes(lengthInt);
|
||||
index = 0;
|
||||
if (lengthInt < size) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,7 @@ public interface BeaconHandler {
|
||||
|
||||
void postResponse(BeaconClient.FailableRunnable<Exception> r);
|
||||
|
||||
void prepareBody() throws IOException;
|
||||
OutputStream sendBody() throws IOException;
|
||||
|
||||
InputStream startBodyRead() throws IOException;
|
||||
|
||||
OutputStream getOutputStream() throws Exception;
|
||||
InputStream receiveBody() throws IOException;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.beacon.exchange.StopExchange;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.file.Files;
|
||||
@@ -24,10 +26,26 @@ public class BeaconServer {
|
||||
return !isPortAvailable(port);
|
||||
}
|
||||
|
||||
private static void startFork(String custom) throws IOException {
|
||||
boolean print = true;
|
||||
var proc = Runtime.getRuntime().exec(custom);
|
||||
new Thread(null, () -> {
|
||||
try {
|
||||
InputStreamReader isr = new InputStreamReader(proc.getInputStream());
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null)
|
||||
System.out.println("[xpiped] " + line);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}, "daemon fork").start();
|
||||
}
|
||||
|
||||
public static boolean tryStart() throws Exception {
|
||||
var custom = BeaconConfig.getCustomExecCommand();
|
||||
if (custom != null) {
|
||||
Runtime.getRuntime().exec(custom);
|
||||
startFork(custom);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -45,7 +63,8 @@ public class BeaconServer {
|
||||
}
|
||||
|
||||
public static boolean tryStop(BeaconClient client) throws Exception {
|
||||
StopExchange.Response res = client.simpleExchange(StopExchange.Request.builder().build());
|
||||
client.sendRequest(StopExchange.Request.builder().build());
|
||||
StopExchange.Response res =client.receiveResponse();
|
||||
return res.isSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.message.RequestMessage;
|
||||
import io.xpipe.beacon.message.ResponseMessage;
|
||||
import io.xpipe.core.source.DataSourceConfigOptions;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceInfo;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
public class StoreResourceExchange implements MessageExchange<StoreResourceExchange.Request, StoreResourceExchange.Response> {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "storeResource";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<StoreResourceExchange.Request> getRequestClass() {
|
||||
return StoreResourceExchange.Request.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<StoreResourceExchange.Response> getResponseClass() {
|
||||
return StoreResourceExchange.Response.class;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
URL url;
|
||||
String providerId;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
DataSourceId sourceId;
|
||||
DataSourceConfigOptions config;
|
||||
DataSourceInfo info;
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,7 @@ public class QueryTableDataExchange implements MessageExchange<QueryTableDataExc
|
||||
@NonNull
|
||||
DataSourceId id;
|
||||
|
||||
@Builder.Default
|
||||
int maxRows = -1;
|
||||
int maxRows;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
|
||||
@@ -26,7 +26,6 @@ module io.xpipe.beacon {
|
||||
ModeExchange,
|
||||
StatusExchange,
|
||||
StopExchange,
|
||||
StoreResourceExchange,
|
||||
WritePreparationExchange,
|
||||
WriteExecuteExchange,
|
||||
SelectExchange,
|
||||
|
||||
Reference in New Issue
Block a user