Refactor various components

This commit is contained in:
Christopher Schnick
2022-01-01 20:11:13 +01:00
parent b54d8ad362
commit d63882c5ff
45 changed files with 771 additions and 190 deletions
@@ -0,0 +1,70 @@
package io.xpipe.api;
import io.xpipe.api.impl.DataSourceImpl;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceType;
import java.net.URL;
import java.util.Map;
/**
* Represents a reference to an XPipe data source.
*
* The actual data is only queried when required and is not cached.
* Therefore, the queried data is always up-to-date at the point of calling a method that queries the data.
*/
public interface DataSource {
/**
* Wrapper for {@link #get(DataSourceId)}.
*
* @throws IllegalArgumentException if {@code id} is not a valid data source id
*/
static DataSource get(String id) {
return get(DataSourceId.fromString(id));
}
/**
* Retrieves a reference to the given data source.
*
* @param id the data source id
* @return a reference to the data source that can be used to access the underlying data source
*/
static DataSource get(DataSourceId id) {
return DataSourceImpl.get(id);
}
/**
* Retrieves a reference to the given local data source that is specified by a URL.
*
* This wrapped data source is only available temporarily and locally,
* i.e. it is not added to the XPipe data source storage.
*
* @param url the url that points to the data
* @param configOptions additional configuration options for the specific data source type
* @return a reference to the data source that can be used to access the underlying data source
*/
static DataSource wrap(URL url, Map<String, String> configOptions) {
return null;
}
/**
* Wrapper for {@link #wrap(URL, Map)} that passes no configuration options.
*/
static DataSource wrap(URL url) {
return wrap(url, Map.of());
}
DataSourceId getId();
DataSourceType getType();
/**
* Attemps to cast this object to a {@link DataTable}.
*
* @throws UnsupportedOperationException if the data source is not a table
*/
default DataTable asTable() {
throw new UnsupportedOperationException("Data source is not a table");
}
}
@@ -1,20 +1,15 @@
package io.xpipe.api;
import io.xpipe.api.impl.DataTableImpl;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.source.DataSourceId;
import java.util.OptionalInt;
import java.util.stream.Stream;
public interface DataTable extends Iterable<TupleNode> {
public interface DataTable extends Iterable<TupleNode>, DataSource {
static DataTable get(String s) {
return DataTableImpl.get(s);
}
DataSourceId getId();
Stream<TupleNode> stream();
int getRowCount();
@@ -1,7 +1,8 @@
package io.xpipe.api;
import io.xpipe.beacon.*;
import io.xpipe.beacon.BeaconClient;
import java.util.Optional;
public abstract class XPipeApiConnector extends BeaconConnector {
@@ -23,12 +24,48 @@ public abstract class XPipeApiConnector extends BeaconConnector {
protected abstract void handle(BeaconClient sc) throws Exception;
@Override
protected void waitForStartup() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
protected BeaconClient constructSocket() throws ConnectorException {
if (!BeaconServer.isRunning()) {
try {
start();
} catch (Exception ex) {
throw new ConnectorException("Unable to start xpipe daemon", ex);
}
var r = waitForStartup();
if (r.isEmpty()) {
throw new ConnectorException("Wait for xpipe daemon timed out");
} else {
return r.get();
}
}
try {
return new BeaconClient();
} catch (Exception ex) {
throw new ConnectorException("Unable to connect to running xpipe daemon", ex);
}
}
private void start() throws Exception {
if (!BeaconServer.tryStart()) {
throw new UnsupportedOperationException("Unable to determine xpipe daemon launch command");
};
}
private Optional<BeaconClient> waitForStartup() {
for (int i = 0; i < 40; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
var s = BeaconClient.tryConnect();
if (s.isPresent()) {
return s;
}
}
return Optional.empty();
}
@FunctionalInterface
@@ -0,0 +1,47 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSource;
import io.xpipe.api.DataTable;
import io.xpipe.api.XPipeApiConnector;
import io.xpipe.beacon.BeaconClient;
import io.xpipe.beacon.ClientException;
import io.xpipe.beacon.ConnectorException;
import io.xpipe.beacon.ServerException;
import io.xpipe.beacon.exchange.ReadInfoExchange;
import io.xpipe.core.source.DataSourceId;
public abstract class DataSourceImpl implements DataSource {
public static DataSource get(DataSourceId ds) {
final DataSource[] source = new DataSource[1];
new XPipeApiConnector() {
@Override
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
var req = ReadInfoExchange.Request.builder().sourceId(ds).build();
ReadInfoExchange.Response res = performSimpleExchange(sc, req);
switch (res.getType()) {
case TABLE -> {
var data = res.getTableData();
source[0] = new DataTableImpl(res.getSourceId(), data.getRowCount(), data.getDataType());
}
case STRUCTURE -> {
}
case RAW -> {
}
}
}
}.execute();
return source[0];
}
private final DataSourceId sourceId;
public DataSourceImpl(DataSourceId sourceId) {
this.sourceId = sourceId;
}
@Override
public DataSourceId getId() {
return sourceId;
}
}
@@ -7,9 +7,8 @@ import io.xpipe.beacon.ClientException;
import io.xpipe.beacon.ConnectorException;
import io.xpipe.beacon.ServerException;
import io.xpipe.beacon.exchange.ReadTableDataExchange;
import io.xpipe.beacon.exchange.ReadTableInfoExchange;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.typed.TypedAbstractReader;
@@ -17,6 +16,7 @@ import io.xpipe.core.data.typed.TypedDataStreamParser;
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceType;
import java.io.IOException;
import java.io.InputStream;
@@ -25,30 +25,14 @@ import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class DataTableImpl implements DataTable {
public static DataTable get(String s) {
return get(DataSourceId.fromString(s));
}
public static DataTable get(DataSourceId ds) {
final DataTable[] table = {null};
new XPipeApiConnector() {
@Override
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
var req = new ReadTableInfoExchange.Request(ds);
ReadTableInfoExchange.Response res = performSimpleExchange(sc, req);
table[0] = new DataTableImpl(res.sourceId(), res.rowCount(), res.dataType());
}
}.execute();
return table[0];
}
public class DataTableImpl extends DataSourceImpl implements DataTable {
private final DataSourceId id;
private final int size;
private final DataType dataType;
public DataTableImpl(DataSourceId id, int size, DataType dataType) {
super(id);
this.id = id;
this.size = size;
this.dataType = dataType;
@@ -64,6 +48,11 @@ public class DataTableImpl implements DataTable {
return id;
}
@Override
public DataSourceType getType() {
return DataSourceType.TABLE;
}
@Override
public int getRowCount() {
if (size == -1) {
@@ -96,11 +85,12 @@ public class DataTableImpl implements DataTable {
new XPipeApiConnector() {
@Override
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
var req = new ReadTableDataExchange.Request(id, maxToRead);
performExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
var req = ReadTableDataExchange.Request.builder()
.sourceId(id).maxRows(maxToRead).build();
performInputExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
var r = new TypedDataStreamParser(dataType);
r.parseStructures(in, TypedDataStructureNodeReader.immutable(dataType), nodes::add);
}, false);
});
}
}.execute();
return ArrayNode.of(nodes);
@@ -120,9 +110,10 @@ public class DataTableImpl implements DataTable {
new XPipeApiConnector() {
@Override
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
var req = new ReadTableDataExchange.Request(id, Integer.MAX_VALUE);
performExchange(sc, req,
(ReadTableDataExchange.Response res, InputStream in) -> input = in, false);
var req = ReadTableDataExchange.Request.builder()
.sourceId(id).maxRows(Integer.MAX_VALUE).build();
performInputExchange(sc, req,
(ReadTableDataExchange.Response res, InputStream in) -> input = in);
}
}.execute();
+2 -3
View File
@@ -1,7 +1,6 @@
module io.xpipe.api {
requires io.xpipe.core;
requires io.xpipe.beacon;
requires org.apache.commons.lang;
requires transitive io.xpipe.core;
requires transitive io.xpipe.beacon;
exports io.xpipe.api;
}
+4 -2
View File
@@ -14,12 +14,14 @@ repositories {
}
apply from: "$rootDir/deps/slf4j.gradle"
apply from: "$rootDir/deps/websocket.gradle"
apply from: "$rootDir/deps/jackson.gradle"
apply from: "$rootDir/deps/commons.gradle"
dependencies {
implementation project(':core')
implementation project(':extension')
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
}
test {
@@ -12,9 +12,6 @@ import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.message.ServerErrorMessage;
import io.xpipe.core.util.JacksonHelper;
import org.apache.commons.lang3.function.FailableBiConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -23,13 +20,34 @@ import java.net.InetAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Consumer;
import static io.xpipe.beacon.BeaconConfig.BODY_SEPARATOR;
public class BeaconClient {
private static final Logger logger = LoggerFactory.getLogger(BeaconClient.class);
@FunctionalInterface
public interface FailableBiConsumer<T, U, E extends Throwable> {
void accept(T var1, U var2) throws E;
}
@FunctionalInterface
public interface FailableConsumer<T, E extends Throwable> {
void accept(T var1) throws E;
}
public static Optional<BeaconClient> tryConnect() {
if (BeaconConfig.debugEnabled()) {
System.out.println("Attempting connection to server at port " + BeaconConfig.getUsedPort());
}
try {
return Optional.of(new BeaconClient());
} catch (IOException ex) {
return Optional.empty();
}
}
private final Socket socket;
private final InputStream in;
@@ -51,29 +69,27 @@ public class BeaconClient {
public <REQ extends RequestMessage, RES extends ResponseMessage> void exchange(
REQ req,
Consumer<OutputStream> output,
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
boolean keepOpen) throws ConnectorException, ClientException, ServerException {
FailableConsumer<OutputStream, IOException> reqWriter,
FailableBiConsumer<RES, InputStream, IOException> resReader)
throws ConnectorException, ClientException, ServerException {
try {
sendRequest(req);
if (output != null) {
if (reqWriter != null) {
out.write(BODY_SEPARATOR);
output.accept(out);
reqWriter.accept(out);
}
var res = this.<RES>receiveResponse();
var sep = in.readNBytes(BODY_SEPARATOR.length);
if (!Arrays.equals(BODY_SEPARATOR, sep)) {
if (sep.length != 0 && !Arrays.equals(BODY_SEPARATOR, sep)) {
throw new ConnectorException("Invalid body separator");
}
responseConsumer.accept(res, in);
resReader.accept(res, in);
} catch (IOException ex) {
throw new ConnectorException("Couldn't communicate with socket", ex);
} finally {
if (!keepOpen) {
close();
}
close();
}
}
@@ -2,52 +2,42 @@ package io.xpipe.beacon;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import org.apache.commons.lang3.function.FailableBiConsumer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.function.Consumer;
import java.util.concurrent.atomic.AtomicReference;
public abstract class BeaconConnector {
protected abstract void waitForStartup();
protected abstract BeaconClient constructSocket() throws ConnectorException;
protected BeaconClient constructSocket() throws ConnectorException {
if (!BeaconServer.isRunning()) {
try {
BeaconServer.start();
waitForStartup();
if (!BeaconServer.isRunning()) {
throw new ConnectorException("Unable to start xpipe daemon");
}
} catch (Exception ex) {
throw new ConnectorException("Unable to start xpipe daemon: " + ex.getMessage());
}
}
try {
return new BeaconClient();
} catch (Exception ex) {
throw new ConnectorException("Unable to connect to running xpipe daemon: " + ex.getMessage());
}
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performExchange(
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performInputExchange(
BeaconClient socket,
REQ req,
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
boolean keepOpen) throws ServerException, ConnectorException, ClientException {
performExchange(socket, req, null, responseConsumer, keepOpen);
BeaconClient.FailableBiConsumer<RES, InputStream, IOException> responseConsumer) throws ServerException, ConnectorException, ClientException {
performInputOutputExchange(socket, req, null, responseConsumer);
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performExchange(
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performInputOutputExchange(
BeaconClient socket,
REQ req,
Consumer<OutputStream> output,
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
boolean keepOpen) throws ServerException, ConnectorException, ClientException {
socket.exchange(req, output, responseConsumer, keepOpen);
BeaconClient.FailableConsumer<OutputStream, IOException> reqWriter,
BeaconClient.FailableBiConsumer<RES, InputStream, IOException> responseConsumer)
throws ServerException, ConnectorException, ClientException {
socket.exchange(req, reqWriter, responseConsumer);
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> RES performOutputExchange(
BeaconClient socket,
REQ req,
BeaconClient.FailableConsumer<OutputStream, IOException> reqWriter)
throws ServerException, ConnectorException, ClientException {
AtomicReference<RES> response = new AtomicReference<>();
socket.exchange(req, reqWriter, (RES res, InputStream in) -> {
response.set(res);
});
return response.get();
}
protected <REQ extends RequestMessage, RES extends ResponseMessage> RES performSimpleExchange(
@@ -10,6 +10,8 @@ public interface BeaconHandler {
void prepareBody() throws IOException;
void startBodyRead() throws IOException;
public <T extends ResponseMessage> void sendResponse(T obj) throws Exception;
public void sendClientErrorResponse(String message) throws Exception;
@@ -20,19 +20,19 @@ public class BeaconServer {
return !isPortAvailable(port);
}
public static void start() throws Exception {
public static boolean tryStart() throws Exception {
if (BeaconConfig.shouldStartInProcess()) {
startInProcess();
return;
return true;
}
var custom = BeaconConfig.getCustomExecCommand();
if (custom != null) {
Runtime.getRuntime().exec(System.getenv(custom));
return;
var proc = new ProcessBuilder("cmd", "/c", "CALL", "C:\\Projects\\xpipe\\xpipe\\gradlew.bat", ":app:run").inheritIO().start();
return true;
}
throw new IllegalArgumentException("Unable to start xpipe daemon");
return false;
}
private static void startInProcess() throws Exception {
@@ -0,0 +1,45 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.source.DataSourceId;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class CliOptionPageExchange implements MessageExchange<CliOptionPageExchange.Request, CliOptionPageExchange.Response> {
@Override
public String getId() {
return "cliOptionPage";
}
@Override
public Class<CliOptionPageExchange.Request> getRequestClass() {
return CliOptionPageExchange.Request.class;
}
@Override
public Class<CliOptionPageExchange.Response> getResponseClass() {
return CliOptionPageExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
DataSourceId newSourceId;
String type;
boolean hasInputStream;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
DataSourceId sourceId;
DataType dataType;
int rowCount;
}
}
@@ -2,11 +2,14 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.time.Instant;
import java.util.List;
public abstract class ListCollectionsExchange implements MessageExchange<ListCollectionsExchange.Request, ListCollectionsExchange.Response> {
public class ListCollectionsExchange implements MessageExchange<ListCollectionsExchange.Request, ListCollectionsExchange.Response> {
@Override
public String getId() {
@@ -23,7 +26,10 @@ public abstract class ListCollectionsExchange implements MessageExchange<ListCol
return Response.class;
}
public static record Request() implements RequestMessage {
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
}
@@ -31,7 +37,10 @@ public abstract class ListCollectionsExchange implements MessageExchange<ListCol
}
public static record Response(List<Entry> entries) implements ResponseMessage {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
List<Entry> entries;
}
}
@@ -2,11 +2,14 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.time.Instant;
import java.util.List;
public abstract class ListEntriesExchange implements MessageExchange<ListEntriesExchange.Request, ListEntriesExchange.Response> {
public class ListEntriesExchange implements MessageExchange<ListEntriesExchange.Request, ListEntriesExchange.Response> {
@Override
public String getId() {
@@ -23,15 +26,21 @@ public abstract class ListEntriesExchange implements MessageExchange<ListEntries
return Response.class;
}
public static record Request(String collection) implements RequestMessage {
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
String collection;
}
public static record Entry(String name, String type, String description, Instant lastUsed) {
}
public static record Response(List<Entry> entries) implements ResponseMessage {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
List<Entry> entries;
}
}
@@ -2,9 +2,6 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.beacon.BeaconHandler;
import java.io.InputStream;
public interface MessageExchange<RQ extends RequestMessage, RP extends ResponseMessage> {
@@ -13,6 +10,4 @@ public interface MessageExchange<RQ extends RequestMessage, RP extends ResponseM
Class<RQ> getRequestClass();
Class<RP> getResponseClass();
void handleRequest(BeaconHandler handler, RQ msg, InputStream body) throws Exception;
}
@@ -2,8 +2,11 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public abstract class ModeExchange implements MessageExchange<ModeExchange.Request, ModeExchange.Response> {
public class ModeExchange implements MessageExchange<ModeExchange.Request, ModeExchange.Response> {
@Override
public String getId() {
@@ -20,11 +23,17 @@ public abstract class ModeExchange implements MessageExchange<ModeExchange.Reque
return ModeExchange.Response.class;
}
public static record Request(String modeId) implements RequestMessage {
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
String modeId;
}
public static record Response() implements ResponseMessage {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}
@@ -0,0 +1,56 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceType;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class ReadInfoExchange implements MessageExchange<ReadInfoExchange.Request, ReadInfoExchange.Response> {
@Override
public String getId() {
return "readTableInfo";
}
@Override
public Class<ReadInfoExchange.Request> getRequestClass() {
return ReadInfoExchange.Request.class;
}
@Override
public Class<ReadInfoExchange.Response> getResponseClass() {
return ReadInfoExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
DataSourceId sourceId;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
DataSourceId sourceId;
DataSourceType type;
Object data;
public TableData getTableData() {
return (TableData) data;
}
}
@Jacksonized
@Builder
@Value
public static class TableData {
DataType dataType;
int rowCount;
}
}
@@ -4,7 +4,7 @@ import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
public abstract class ReadStructureExchange implements MessageExchange<ReadStructureExchange.Request, ReadStructureExchange.Response> {
public class ReadStructureExchange implements MessageExchange<ReadStructureExchange.Request, ReadStructureExchange.Response> {
@Override
public String getId() {
@@ -3,8 +3,11 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public abstract class ReadTableDataExchange implements MessageExchange<ReadTableDataExchange.Request, ReadTableDataExchange.Response> {
public class ReadTableDataExchange implements MessageExchange<ReadTableDataExchange.Request, ReadTableDataExchange.Response> {
@Override
public String getId() {
@@ -21,11 +24,18 @@ public abstract class ReadTableDataExchange implements MessageExchange<ReadTable
return ReadTableDataExchange.Response.class;
}
public static record Request(DataSourceId sourceId, int maxRows) implements RequestMessage {
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
DataSourceId sourceId;
int maxRows;
}
public static record Response() implements ResponseMessage {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}
@@ -1,32 +0,0 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.source.DataSourceId;
public abstract class ReadTableInfoExchange implements MessageExchange<ReadTableInfoExchange.Request, ReadTableInfoExchange.Response> {
@Override
public String getId() {
return "readTableInfo";
}
@Override
public Class<ReadTableInfoExchange.Request> getRequestClass() {
return ReadTableInfoExchange.Request.class;
}
@Override
public Class<ReadTableInfoExchange.Response> getResponseClass() {
return ReadTableInfoExchange.Response.class;
}
public static record Request(DataSourceId sourceId) implements RequestMessage {
}
public static record Response(DataSourceId sourceId, DataType dataType, int rowCount) implements ResponseMessage {
}
}
@@ -2,8 +2,11 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public abstract class StatusExchange implements MessageExchange<StatusExchange.Request, StatusExchange.Response> {
public class StatusExchange implements MessageExchange<StatusExchange.Request, StatusExchange.Response> {
@Override
public String getId() {
@@ -20,11 +23,16 @@ public abstract class StatusExchange implements MessageExchange<StatusExchange.R
return Response.class;
}
public static record Request() implements RequestMessage {
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
}
public static record Response(String mode) implements ResponseMessage {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
String mode;
}
}
@@ -2,8 +2,11 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public abstract class StopExchange implements MessageExchange<StopExchange.Request, StopExchange.Response> {
public class StopExchange implements MessageExchange<StopExchange.Request, StopExchange.Response> {
@Override
public String getId() {
@@ -20,11 +23,17 @@ public abstract class StopExchange implements MessageExchange<StopExchange.Reque
return StopExchange.Response.class;
}
public static record Request(boolean force) implements RequestMessage {
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
boolean force;
}
public static record Response(boolean success) implements ResponseMessage {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
boolean success;
}
}
@@ -0,0 +1,44 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.util.Map;
import java.util.UUID;
public class StoreEndExchange implements MessageExchange<StoreEndExchange.Request, StoreEndExchange.Response> {
@Override
public String getId() {
return "storeEnd";
}
@Override
public Class<StoreEndExchange.Request> getRequestClass() {
return StoreEndExchange.Request.class;
}
@Override
public Class<StoreEndExchange.Response> getResponseClass() {
return StoreEndExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
UUID entryId;
Map<String, String> values;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
DataSourceId sourceId;
}
}
@@ -0,0 +1,41 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.extension.cli.CliOptionPage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class StoreStartExchange implements MessageExchange<StoreStartExchange.Request, StoreStartExchange.Response> {
@Override
public String getId() {
return "storeStart";
}
@Override
public Class<StoreStartExchange.Request> getRequestClass() {
return StoreStartExchange.Request.class;
}
@Override
public Class<StoreStartExchange.Response> getResponseClass() {
return StoreStartExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
String type;
boolean hasInputStream;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
CliOptionPage page;
}
}
@@ -2,8 +2,11 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public abstract class VersionExchange implements MessageExchange<VersionExchange.Request, VersionExchange.Response> {
public class VersionExchange implements MessageExchange<VersionExchange.Request, VersionExchange.Response> {
@Override
public String getId() {
@@ -20,18 +23,20 @@ public abstract class VersionExchange implements MessageExchange<VersionExchange
return VersionExchange.Response.class;
}
public static record Request() implements RequestMessage {
@lombok.extern.jackson.Jacksonized
@lombok.Builder
@lombok.Value
public static class Request implements RequestMessage {
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
public final String version;
public final String jvmVersion;
public Response(String version, String jvmVersion) {
this.version = version;
this.jvmVersion = jvmVersion;
}
String version;
String buildVersion;
String jvmVersion;
}
}
+11 -3
View File
@@ -1,22 +1,30 @@
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.*;
module io.xpipe.beacon {
exports io.xpipe.beacon;
exports io.xpipe.beacon.exchange;
exports io.xpipe.beacon.message;
requires org.slf4j;
requires org.slf4j.simple;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.module.paramnames;
requires io.xpipe.core;
requires io.xpipe.extension;
opens io.xpipe.beacon;
opens io.xpipe.beacon.exchange;
opens io.xpipe.beacon.message;
requires org.apache.commons.lang;
requires static lombok;
uses MessageExchange;
provides io.xpipe.beacon.exchange.MessageExchange with
ListCollectionsExchange,
ListEntriesExchange,
ReadTableDataExchange,
ReadInfoExchange,
StatusExchange,
StopExchange,
VersionExchange;
}
@@ -11,12 +11,12 @@ public abstract class TupleNode extends DataStructureNode {
return new Builder();
}
public static TupleNode of(List<DataStructureNode> nodes) {
public static TupleNode of(List<? extends DataStructureNode> nodes) {
if (nodes == null) {
throw new IllegalArgumentException("Nodes must be not null");
}
return new NoKeyTupleNode(true, nodes);
return new NoKeyTupleNode(true, (List<DataStructureNode>) nodes);
}
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {
@@ -3,6 +3,4 @@ package io.xpipe.core.source;
public interface DataSourceConnection extends AutoCloseable {
void init() throws Exception;
void close() throws Exception;
}
@@ -10,9 +10,9 @@ import java.io.OutputStream;
public interface TableDataReadConnection extends DataSourceConnection {
TupleType determineDataType() throws Exception;
TupleType getDataType() throws Exception;
int determineRowCount() throws Exception;
int getRowCount() throws Exception;
void withLines(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception;
@@ -4,9 +4,21 @@ import io.xpipe.core.store.DataStore;
public abstract class TableDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
public abstract TableDataWriteConnection newWriteConnection(DS store);
public final TableDataReadConnection openReadConnection(DS store) throws Exception {
var con = newReadConnection(store);
con.init();
return con;
}
public abstract TableDataReadConnection newReadConnection(DS store);
public final TableDataWriteConnection openWriteConnection(DS store) throws Exception {
var con = newWriteConnection(store);
con.init();
return con;
}
protected abstract TableDataWriteConnection newWriteConnection(DS store);
protected abstract TableDataReadConnection newReadConnection(DS store);
@Override
public DataSourceType getType() {
@@ -1,11 +1,14 @@
package io.xpipe.core.store;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
public abstract class InputStreamDataStore implements StreamDataStore {
public class InputStreamDataStore implements StreamDataStore {
private final InputStream in;
private BufferedInputStream bufferedInputStream;
private boolean opened = false;
public InputStreamDataStore(InputStream in) {
this.in = in;
@@ -13,11 +16,17 @@ public abstract class InputStreamDataStore implements StreamDataStore {
@Override
public InputStream openInput() throws Exception {
return in;
if (opened) {
return bufferedInputStream;
}
opened = true;
bufferedInputStream = new BufferedInputStream(in);
return bufferedInputStream;
}
@Override
public OutputStream openOutput() throws Exception {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException("No output available");
}
}
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonTypeName;
import org.apache.commons.io.FilenameUtils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -65,7 +66,7 @@ public class LocalFileDataStore extends FileDataStore {
@Override
public InputStream openInput() throws Exception {
return Files.newInputStream(file);
return new BufferedInputStream(Files.newInputStream(file));
}
@Override
@@ -3,7 +3,6 @@ package io.xpipe.extension;
import io.xpipe.core.source.DataSourceDescriptor;
import io.xpipe.core.store.DataStore;
import javafx.beans.property.Property;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import java.nio.file.Path;
@@ -24,13 +23,15 @@ public interface DataSourceGuiProvider {
String getDisplayName();
Image getImage();
String getDisplayImage();
Supplier<String> getFileName();
String getFileName();
Map<Supplier<String>, String> getFileExtensions();
String getDataSourceDescription(DataSourceDescriptor<?> source);
String getDataSourceShortDescription(DataSourceDescriptor<?> source);
String getDataSourceLongDescription(DataSourceDescriptor<?> source);
Class<? extends DataSourceDescriptor<?>> getType();
}
@@ -1,10 +1,17 @@
package io.xpipe.extension;
import io.xpipe.core.source.DataSourceDescriptor;
import io.xpipe.core.store.DataStore;
import java.nio.file.Path;
public interface DataSourceProvider {
String getId();
boolean supportsFile(Path file);
DataSourceDescriptor<?> createDefaultDataSource(DataStore input) throws Exception;
Class<? extends DataSourceDescriptor<?>> getType();
}
@@ -1,5 +1,6 @@
package io.xpipe.extension;
import java.nio.file.Path;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
@@ -32,6 +33,14 @@ public class DataSourceProviders {
return ALL.stream().filter(d -> d.getId().equals(name)).findAny();
}
public static Optional<DataSourceProvider> byFile(Path file) {
if (ALL == null) {
throw new IllegalStateException("Not initialized");
}
return ALL.stream().filter(d -> d.supportsFile(file)).findAny();
}
public static Set<DataSourceProvider> getAll() {
return ALL;
}
@@ -0,0 +1,19 @@
package io.xpipe.extension;
import java.util.ServiceLoader;
import java.util.function.Supplier;
public interface I18n {
I18n INSTANCE = ServiceLoader.load(I18n.class).findFirst().orElseThrow();
public static Supplier<String> resolver(String s, Object... vars) {
return () -> get(s, vars);
}
public static String get(String s, Object... vars) {
return INSTANCE.getLocalised(s, vars);
}
String getLocalised(String s, Object... vars);
}
@@ -0,0 +1,27 @@
package io.xpipe.extension.cli;
public abstract class CliOption<T> {
private final String name;
protected T value;
public CliOption(String name) {
this.name = name;
this.value = null;
}
public CliOption(String name, T value) {
this.name = name;
this.value = value;
}
protected abstract String enterValue(String val);
public String getName() {
return name;
}
public T getValue() {
return value;
}
}
@@ -0,0 +1,22 @@
package io.xpipe.extension.cli;
import java.util.List;
public class CliOptionPage {
private String description;
private List<CliOption<?>> options;
public CliOptionPage(String description, List<CliOption<?>> options) {
this.description = description;
this.options = options;
}
public String getDescription() {
return description;
}
public List<CliOption<?>> getOptions() {
return options;
}
}
+2
View File
@@ -8,8 +8,10 @@ module io.xpipe.extension {
requires javafx.graphics;
exports io.xpipe.extension;
exports io.xpipe.extension.cli;
uses DataSourceProvider;
uses DataSourceGuiProvider;
uses SupportedApplicationProvider;
uses io.xpipe.extension.I18n;
}
+18
View File
@@ -0,0 +1,18 @@
plugins {
id 'application'
}
java {
modularity.inferModulePath = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
implementation project(':api')
implementation project(':core')
}
@@ -0,0 +1,30 @@
package io.xpipe.sample;
import io.xpipe.api.DataSource;
import io.xpipe.api.DataTable;
import io.xpipe.core.data.node.TupleNode;
import java.util.Comparator;
public class HomePricesSample {
private static DataTable homePricesTable;
public static void main(String[] args) {
var resource = HomePricesSample.class.getResource("homes.csv");
// Creates a wrapped data source using the url.
// Note that while this is possible, it is not recommended as
// all queries are routed through the XPipe client anyway.
// It allows us however to bundle the data with this sample program.
homePricesTable = DataSource.wrap(resource).asTable();
System.out.println("The highest selling house entry is: " + getHighestSellingHouse());
}
private static TupleNode getHighestSellingHouse() {
return homePricesTable.stream()
.min(Comparator.comparingInt(t -> t.forKey("Sell").asInt()))
.get();
}
}
@@ -0,0 +1,3 @@
module io.xpipe.sample {
requires io.xpipe.api;
}
@@ -0,0 +1,52 @@
"Sell", "List", "Living", "Rooms", "Beds", "Baths", "Age", "Acres", "Taxes"
142, 160, 28, 10, 5, 3, 60, 0.28, 3167
175, 180, 18, 8, 4, 1, 12, 0.43, 4033
129, 132, 13, 6, 3, 1, 41, 0.33, 1471
138, 140, 17, 7, 3, 1, 22, 0.46, 3204
232, 240, 25, 8, 4, 3, 5, 2.05, 3613
135, 140, 18, 7, 4, 3, 9, 0.57, 3028
150, 160, 20, 8, 4, 3, 18, 4.00, 3131
207, 225, 22, 8, 4, 2, 16, 2.22, 5158
271, 285, 30, 10, 5, 2, 30, 0.53, 5702
89, 90, 10, 5, 3, 1, 43, 0.30, 2054
153, 157, 22, 8, 3, 3, 18, 0.38, 4127
87, 90, 16, 7, 3, 1, 50, 0.65, 1445
234, 238, 25, 8, 4, 2, 2, 1.61, 2087
106, 116, 20, 8, 4, 1, 13, 0.22, 2818
175, 180, 22, 8, 4, 2, 15, 2.06, 3917
165, 170, 17, 8, 4, 2, 33, 0.46, 2220
166, 170, 23, 9, 4, 2, 37, 0.27, 3498
136, 140, 19, 7, 3, 1, 22, 0.63, 3607
148, 160, 17, 7, 3, 2, 13, 0.36, 3648
151, 153, 19, 8, 4, 2, 24, 0.34, 3561
180, 190, 24, 9, 4, 2, 10, 1.55, 4681
293, 305, 26, 8, 4, 3, 6, 0.46, 7088
167, 170, 20, 9, 4, 2, 46, 0.46, 3482
190, 193, 22, 9, 5, 2, 37, 0.48, 3920
184, 190, 21, 9, 5, 2, 27, 1.30, 4162
157, 165, 20, 8, 4, 2, 7, 0.30, 3785
110, 115, 16, 8, 4, 1, 26, 0.29, 3103
135, 145, 18, 7, 4, 1, 35, 0.43, 3363
567, 625, 64, 11, 4, 4, 4, 0.85, 12192
180, 185, 20, 8, 4, 2, 11, 1.00, 3831
183, 188, 17, 7, 3, 2, 16, 3.00, 3564
185, 193, 20, 9, 3, 2, 56, 6.49, 3765
152, 155, 17, 8, 4, 1, 33, 0.70, 3361
148, 153, 13, 6, 3, 2, 22, 0.39, 3950
152, 159, 15, 7, 3, 1, 25, 0.59, 3055
146, 150, 16, 7, 3, 1, 31, 0.36, 2950
170, 190, 24, 10, 3, 2, 33, 0.57, 3346
127, 130, 20, 8, 4, 1, 65, 0.40, 3334
265, 270, 36, 10, 6, 3, 33, 1.20, 5853
157, 163, 18, 8, 4, 2, 12, 1.13, 3982
128, 135, 17, 9, 4, 1, 25, 0.52, 3374
110, 120, 15, 8, 4, 2, 11, 0.59, 3119
123, 130, 18, 8, 4, 2, 43, 0.39, 3268
212, 230, 39, 12, 5, 3, 202, 4.29, 3648
145, 145, 18, 8, 4, 2, 44, 0.22, 2783
129, 135, 10, 6, 3, 1, 15, 1.00, 2438
143, 145, 21, 7, 4, 2, 10, 1.20, 3529
247, 252, 29, 9, 4, 2, 4, 1.25, 4626
111, 120, 15, 8, 3, 1, 97, 1.11, 3205
133, 145, 26, 7, 3, 1, 42, 0.36, 3059
1 Sell List Living Rooms Beds Baths Age Acres Taxes
2 142 160 28 10 5 3 60 0.28 3167
3 175 180 18 8 4 1 12 0.43 4033
4 129 132 13 6 3 1 41 0.33 1471
5 138 140 17 7 3 1 22 0.46 3204
6 232 240 25 8 4 3 5 2.05 3613
7 135 140 18 7 4 3 9 0.57 3028
8 150 160 20 8 4 3 18 4.00 3131
9 207 225 22 8 4 2 16 2.22 5158
10 271 285 30 10 5 2 30 0.53 5702
11 89 90 10 5 3 1 43 0.30 2054
12 153 157 22 8 3 3 18 0.38 4127
13 87 90 16 7 3 1 50 0.65 1445
14 234 238 25 8 4 2 2 1.61 2087
15 106 116 20 8 4 1 13 0.22 2818
16 175 180 22 8 4 2 15 2.06 3917
17 165 170 17 8 4 2 33 0.46 2220
18 166 170 23 9 4 2 37 0.27 3498
19 136 140 19 7 3 1 22 0.63 3607
20 148 160 17 7 3 2 13 0.36 3648
21 151 153 19 8 4 2 24 0.34 3561
22 180 190 24 9 4 2 10 1.55 4681
23 293 305 26 8 4 3 6 0.46 7088
24 167 170 20 9 4 2 46 0.46 3482
25 190 193 22 9 5 2 37 0.48 3920
26 184 190 21 9 5 2 27 1.30 4162
27 157 165 20 8 4 2 7 0.30 3785
28 110 115 16 8 4 1 26 0.29 3103
29 135 145 18 7 4 1 35 0.43 3363
30 567 625 64 11 4 4 4 0.85 12192
31 180 185 20 8 4 2 11 1.00 3831
32 183 188 17 7 3 2 16 3.00 3564
33 185 193 20 9 3 2 56 6.49 3765
34 152 155 17 8 4 1 33 0.70 3361
35 148 153 13 6 3 2 22 0.39 3950
36 152 159 15 7 3 1 25 0.59 3055
37 146 150 16 7 3 1 31 0.36 2950
38 170 190 24 10 3 2 33 0.57 3346
39 127 130 20 8 4 1 65 0.40 3334
40 265 270 36 10 6 3 33 1.20 5853
41 157 163 18 8 4 2 12 1.13 3982
42 128 135 17 9 4 1 25 0.52 3374
43 110 120 15 8 4 2 11 0.59 3119
44 123 130 18 8 4 2 43 0.39 3268
45 212 230 39 12 5 3 202 4.29 3648
46 145 145 18 8 4 2 44 0.22 2783
47 129 135 10 6 3 1 15 1.00 2438
48 143 145 21 7 4 2 10 1.20 3529
49 247 252 29 9 4 2 4 1.25 4626
50 111 120 15 8 3 1 97 1.11 3205
51 133 145 26 7 3 1 42 0.36 3059
52
+6
View File
@@ -5,4 +5,10 @@ include 'beacon'
include 'api'
include 'extension'
include 'sample_extension'
project(":sample_extension").projectDir = file("$projectDir/samples/sample_extension")
include 'sample_program'
project(":sample_program").projectDir = file("$projectDir/samples/sample_program")