Rework beacon connection and implement various improvements

This commit is contained in:
Christopher Schnick
2022-03-07 22:59:48 +01:00
parent 46e83ae757
commit f5cccd5687
29 changed files with 386 additions and 117 deletions
@@ -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
-1
View File
@@ -26,7 +26,6 @@ module io.xpipe.beacon {
ModeExchange,
StatusExchange,
StopExchange,
StoreResourceExchange,
WritePreparationExchange,
WriteExecuteExchange,
SelectExchange,