mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Refactor various components
This commit is contained in:
parent
b54d8ad362
commit
d63882c5ff
45 changed files with 771 additions and 190 deletions
70
api/src/main/java/io/xpipe/api/DataSource.java
Normal file
70
api/src/main/java/io/xpipe/api/DataSource.java
Normal file
|
@ -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
|
||||
|
|
47
api/src/main/java/io/xpipe/api/impl/DataSourceImpl.java
Normal file
47
api/src/main/java/io/xpipe/api/impl/DataSourceImpl.java
Normal file
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
19
extension/src/main/java/io/xpipe/extension/I18n.java
Normal file
19
extension/src/main/java/io/xpipe/extension/I18n.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
0
samples/sample_extension/build.gradle
Normal file
0
samples/sample_extension/build.gradle
Normal file
18
samples/sample_program/build.gradle
Normal file
18
samples/sample_program/build.gradle
Normal 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();
|
||||
}
|
||||
}
|
3
samples/sample_program/src/main/java/module-info.java
Normal file
3
samples/sample_program/src/main/java/module-info.java
Normal file
|
@ -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
|
||||
|
Can't render this file because it has a wrong number of fields in line 52.
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue