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;
|
package io.xpipe.api;
|
||||||
|
|
||||||
import io.xpipe.api.impl.DataTableImpl;
|
|
||||||
import io.xpipe.core.data.node.ArrayNode;
|
import io.xpipe.core.data.node.ArrayNode;
|
||||||
import io.xpipe.core.data.node.TupleNode;
|
import io.xpipe.core.data.node.TupleNode;
|
||||||
import io.xpipe.core.data.type.DataType;
|
import io.xpipe.core.data.type.DataType;
|
||||||
import io.xpipe.core.source.DataSourceId;
|
|
||||||
|
|
||||||
import java.util.OptionalInt;
|
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) {
|
Stream<TupleNode> stream();
|
||||||
return DataTableImpl.get(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataSourceId getId();
|
|
||||||
|
|
||||||
int getRowCount();
|
int getRowCount();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package io.xpipe.api;
|
package io.xpipe.api;
|
||||||
|
|
||||||
import io.xpipe.beacon.*;
|
import io.xpipe.beacon.*;
|
||||||
import io.xpipe.beacon.BeaconClient;
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public abstract class XPipeApiConnector extends BeaconConnector {
|
public abstract class XPipeApiConnector extends BeaconConnector {
|
||||||
|
|
||||||
|
@ -23,12 +24,48 @@ public abstract class XPipeApiConnector extends BeaconConnector {
|
||||||
protected abstract void handle(BeaconClient sc) throws Exception;
|
protected abstract void handle(BeaconClient sc) throws Exception;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void waitForStartup() {
|
protected BeaconClient constructSocket() throws ConnectorException {
|
||||||
try {
|
if (!BeaconServer.isRunning()) {
|
||||||
Thread.sleep(5000);
|
try {
|
||||||
} catch (InterruptedException e) {
|
start();
|
||||||
e.printStackTrace();
|
} 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
|
@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.ConnectorException;
|
||||||
import io.xpipe.beacon.ServerException;
|
import io.xpipe.beacon.ServerException;
|
||||||
import io.xpipe.beacon.exchange.ReadTableDataExchange;
|
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.ArrayNode;
|
||||||
|
import io.xpipe.core.data.node.DataStructureNode;
|
||||||
import io.xpipe.core.data.node.TupleNode;
|
import io.xpipe.core.data.node.TupleNode;
|
||||||
import io.xpipe.core.data.type.DataType;
|
import io.xpipe.core.data.type.DataType;
|
||||||
import io.xpipe.core.data.typed.TypedAbstractReader;
|
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.TypedDataStructureNodeReader;
|
||||||
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
|
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
|
||||||
import io.xpipe.core.source.DataSourceId;
|
import io.xpipe.core.source.DataSourceId;
|
||||||
|
import io.xpipe.core.source.DataSourceType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -25,30 +25,14 @@ import java.util.*;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
public class DataTableImpl implements DataTable {
|
public class DataTableImpl extends DataSourceImpl 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DataSourceId id;
|
private final DataSourceId id;
|
||||||
private final int size;
|
private final int size;
|
||||||
private final DataType dataType;
|
private final DataType dataType;
|
||||||
|
|
||||||
public DataTableImpl(DataSourceId id, int size, DataType dataType) {
|
public DataTableImpl(DataSourceId id, int size, DataType dataType) {
|
||||||
|
super(id);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
|
@ -64,6 +48,11 @@ public class DataTableImpl implements DataTable {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataSourceType getType() {
|
||||||
|
return DataSourceType.TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRowCount() {
|
public int getRowCount() {
|
||||||
if (size == -1) {
|
if (size == -1) {
|
||||||
|
@ -96,11 +85,12 @@ public class DataTableImpl implements DataTable {
|
||||||
new XPipeApiConnector() {
|
new XPipeApiConnector() {
|
||||||
@Override
|
@Override
|
||||||
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
|
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
|
||||||
var req = new ReadTableDataExchange.Request(id, maxToRead);
|
var req = ReadTableDataExchange.Request.builder()
|
||||||
performExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
|
.sourceId(id).maxRows(maxToRead).build();
|
||||||
|
performInputExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
|
||||||
var r = new TypedDataStreamParser(dataType);
|
var r = new TypedDataStreamParser(dataType);
|
||||||
r.parseStructures(in, TypedDataStructureNodeReader.immutable(dataType), nodes::add);
|
r.parseStructures(in, TypedDataStructureNodeReader.immutable(dataType), nodes::add);
|
||||||
}, false);
|
});
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
return ArrayNode.of(nodes);
|
return ArrayNode.of(nodes);
|
||||||
|
@ -120,9 +110,10 @@ public class DataTableImpl implements DataTable {
|
||||||
new XPipeApiConnector() {
|
new XPipeApiConnector() {
|
||||||
@Override
|
@Override
|
||||||
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
|
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
|
||||||
var req = new ReadTableDataExchange.Request(id, Integer.MAX_VALUE);
|
var req = ReadTableDataExchange.Request.builder()
|
||||||
performExchange(sc, req,
|
.sourceId(id).maxRows(Integer.MAX_VALUE).build();
|
||||||
(ReadTableDataExchange.Response res, InputStream in) -> input = in, false);
|
performInputExchange(sc, req,
|
||||||
|
(ReadTableDataExchange.Response res, InputStream in) -> input = in);
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
module io.xpipe.api {
|
module io.xpipe.api {
|
||||||
requires io.xpipe.core;
|
requires transitive io.xpipe.core;
|
||||||
requires io.xpipe.beacon;
|
requires transitive io.xpipe.beacon;
|
||||||
requires org.apache.commons.lang;
|
|
||||||
|
|
||||||
exports io.xpipe.api;
|
exports io.xpipe.api;
|
||||||
}
|
}
|
|
@ -14,12 +14,14 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/deps/slf4j.gradle"
|
apply from: "$rootDir/deps/slf4j.gradle"
|
||||||
apply from: "$rootDir/deps/websocket.gradle"
|
|
||||||
apply from: "$rootDir/deps/jackson.gradle"
|
apply from: "$rootDir/deps/jackson.gradle"
|
||||||
apply from: "$rootDir/deps/commons.gradle"
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
|
implementation project(':extension')
|
||||||
|
|
||||||
|
compileOnly 'org.projectlombok:lombok:1.18.22'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok:1.18.22'
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
|
@ -12,9 +12,6 @@ import io.xpipe.beacon.message.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
import io.xpipe.beacon.message.ResponseMessage;
|
||||||
import io.xpipe.beacon.message.ServerErrorMessage;
|
import io.xpipe.beacon.message.ServerErrorMessage;
|
||||||
import io.xpipe.core.util.JacksonHelper;
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -23,13 +20,34 @@ import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static io.xpipe.beacon.BeaconConfig.BODY_SEPARATOR;
|
import static io.xpipe.beacon.BeaconConfig.BODY_SEPARATOR;
|
||||||
|
|
||||||
public class BeaconClient {
|
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 Socket socket;
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
|
@ -51,29 +69,27 @@ public class BeaconClient {
|
||||||
|
|
||||||
public <REQ extends RequestMessage, RES extends ResponseMessage> void exchange(
|
public <REQ extends RequestMessage, RES extends ResponseMessage> void exchange(
|
||||||
REQ req,
|
REQ req,
|
||||||
Consumer<OutputStream> output,
|
FailableConsumer<OutputStream, IOException> reqWriter,
|
||||||
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
|
FailableBiConsumer<RES, InputStream, IOException> resReader)
|
||||||
boolean keepOpen) throws ConnectorException, ClientException, ServerException {
|
throws ConnectorException, ClientException, ServerException {
|
||||||
try {
|
try {
|
||||||
sendRequest(req);
|
sendRequest(req);
|
||||||
if (output != null) {
|
if (reqWriter != null) {
|
||||||
out.write(BODY_SEPARATOR);
|
out.write(BODY_SEPARATOR);
|
||||||
output.accept(out);
|
reqWriter.accept(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = this.<RES>receiveResponse();
|
var res = this.<RES>receiveResponse();
|
||||||
var sep = in.readNBytes(BODY_SEPARATOR.length);
|
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");
|
throw new ConnectorException("Invalid body separator");
|
||||||
}
|
}
|
||||||
|
|
||||||
responseConsumer.accept(res, in);
|
resReader.accept(res, in);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new ConnectorException("Couldn't communicate with socket", ex);
|
throw new ConnectorException("Couldn't communicate with socket", ex);
|
||||||
} finally {
|
} finally {
|
||||||
if (!keepOpen) {
|
close();
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,52 +2,42 @@ package io.xpipe.beacon;
|
||||||
|
|
||||||
import io.xpipe.beacon.message.RequestMessage;
|
import io.xpipe.beacon.message.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
import io.xpipe.beacon.message.ResponseMessage;
|
||||||
import org.apache.commons.lang3.function.FailableBiConsumer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.function.Consumer;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
public abstract class BeaconConnector {
|
public abstract class BeaconConnector {
|
||||||
|
|
||||||
protected abstract void waitForStartup();
|
protected abstract BeaconClient constructSocket() throws ConnectorException;
|
||||||
|
|
||||||
protected BeaconClient constructSocket() throws ConnectorException {
|
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performInputExchange(
|
||||||
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(
|
|
||||||
BeaconClient socket,
|
BeaconClient socket,
|
||||||
REQ req,
|
REQ req,
|
||||||
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
|
BeaconClient.FailableBiConsumer<RES, InputStream, IOException> responseConsumer) throws ServerException, ConnectorException, ClientException {
|
||||||
boolean keepOpen) throws ServerException, ConnectorException, ClientException {
|
performInputOutputExchange(socket, req, null, responseConsumer);
|
||||||
performExchange(socket, req, null, responseConsumer, keepOpen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performExchange(
|
protected <REQ extends RequestMessage, RES extends ResponseMessage> void performInputOutputExchange(
|
||||||
BeaconClient socket,
|
BeaconClient socket,
|
||||||
REQ req,
|
REQ req,
|
||||||
Consumer<OutputStream> output,
|
BeaconClient.FailableConsumer<OutputStream, IOException> reqWriter,
|
||||||
FailableBiConsumer<RES, InputStream, IOException> responseConsumer,
|
BeaconClient.FailableBiConsumer<RES, InputStream, IOException> responseConsumer)
|
||||||
boolean keepOpen) throws ServerException, ConnectorException, ClientException {
|
throws ServerException, ConnectorException, ClientException {
|
||||||
socket.exchange(req, output, responseConsumer, keepOpen);
|
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(
|
protected <REQ extends RequestMessage, RES extends ResponseMessage> RES performSimpleExchange(
|
||||||
|
|
|
@ -10,6 +10,8 @@ public interface BeaconHandler {
|
||||||
|
|
||||||
void prepareBody() throws IOException;
|
void prepareBody() throws IOException;
|
||||||
|
|
||||||
|
void startBodyRead() throws IOException;
|
||||||
|
|
||||||
public <T extends ResponseMessage> void sendResponse(T obj) throws Exception;
|
public <T extends ResponseMessage> void sendResponse(T obj) throws Exception;
|
||||||
|
|
||||||
public void sendClientErrorResponse(String message) throws Exception;
|
public void sendClientErrorResponse(String message) throws Exception;
|
||||||
|
|
|
@ -20,19 +20,19 @@ public class BeaconServer {
|
||||||
return !isPortAvailable(port);
|
return !isPortAvailable(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void start() throws Exception {
|
public static boolean tryStart() throws Exception {
|
||||||
if (BeaconConfig.shouldStartInProcess()) {
|
if (BeaconConfig.shouldStartInProcess()) {
|
||||||
startInProcess();
|
startInProcess();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var custom = BeaconConfig.getCustomExecCommand();
|
var custom = BeaconConfig.getCustomExecCommand();
|
||||||
if (custom != null) {
|
if (custom != null) {
|
||||||
Runtime.getRuntime().exec(System.getenv(custom));
|
var proc = new ProcessBuilder("cmd", "/c", "CALL", "C:\\Projects\\xpipe\\xpipe\\gradlew.bat", ":app:run").inheritIO().start();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException("Unable to start xpipe daemon");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void startInProcess() throws Exception {
|
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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
import io.xpipe.beacon.message.ResponseMessage;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class ListCollectionsExchange implements MessageExchange<ListCollectionsExchange.Request, ListCollectionsExchange.Response> {
|
public class ListCollectionsExchange implements MessageExchange<ListCollectionsExchange.Request, ListCollectionsExchange.Response> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -23,7 +26,10 @@ public abstract class ListCollectionsExchange implements MessageExchange<ListCol
|
||||||
return Response.class;
|
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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
import io.xpipe.beacon.message.ResponseMessage;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.jackson.Jacksonized;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class ListEntriesExchange implements MessageExchange<ListEntriesExchange.Request, ListEntriesExchange.Response> {
|
public class ListEntriesExchange implements MessageExchange<ListEntriesExchange.Request, ListEntriesExchange.Response> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -23,15 +26,21 @@ public abstract class ListEntriesExchange implements MessageExchange<ListEntries
|
||||||
return Response.class;
|
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 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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
import io.xpipe.beacon.message.ResponseMessage;
|
||||||
import io.xpipe.beacon.BeaconHandler;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface MessageExchange<RQ extends RequestMessage, RP extends ResponseMessage> {
|
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<RQ> getRequestClass();
|
||||||
|
|
||||||
Class<RP> getResponseClass();
|
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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
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
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -20,11 +23,17 @@ public abstract class ModeExchange implements MessageExchange<ModeExchange.Reque
|
||||||
return ModeExchange.Response.class;
|
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.beacon.message.ResponseMessage;
|
||||||
import io.xpipe.core.source.DataSourceId;
|
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
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
|
|
@ -3,8 +3,11 @@ package io.xpipe.beacon.exchange;
|
||||||
import io.xpipe.beacon.message.RequestMessage;
|
import io.xpipe.beacon.message.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
import io.xpipe.beacon.message.ResponseMessage;
|
||||||
import io.xpipe.core.source.DataSourceId;
|
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
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -21,11 +24,18 @@ public abstract class ReadTableDataExchange implements MessageExchange<ReadTable
|
||||||
return ReadTableDataExchange.Response.class;
|
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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
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
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -20,11 +23,16 @@ public abstract class StatusExchange implements MessageExchange<StatusExchange.R
|
||||||
return Response.class;
|
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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
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
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -20,11 +23,17 @@ public abstract class StopExchange implements MessageExchange<StopExchange.Reque
|
||||||
return StopExchange.Response.class;
|
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.RequestMessage;
|
||||||
import io.xpipe.beacon.message.ResponseMessage;
|
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
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -20,18 +23,20 @@ public abstract class VersionExchange implements MessageExchange<VersionExchange
|
||||||
return VersionExchange.Response.class;
|
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 static class Response implements ResponseMessage {
|
||||||
|
|
||||||
public final String version;
|
String version;
|
||||||
public final String jvmVersion;
|
String buildVersion;
|
||||||
|
String jvmVersion;
|
||||||
public Response(String version, String jvmVersion) {
|
|
||||||
this.version = version;
|
|
||||||
this.jvmVersion = jvmVersion;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,22 +1,30 @@
|
||||||
import io.xpipe.beacon.exchange.MessageExchange;
|
import io.xpipe.beacon.exchange.*;
|
||||||
|
|
||||||
module io.xpipe.beacon {
|
module io.xpipe.beacon {
|
||||||
exports io.xpipe.beacon;
|
exports io.xpipe.beacon;
|
||||||
exports io.xpipe.beacon.exchange;
|
exports io.xpipe.beacon.exchange;
|
||||||
exports io.xpipe.beacon.message;
|
exports io.xpipe.beacon.message;
|
||||||
requires org.slf4j;
|
requires org.slf4j;
|
||||||
requires org.slf4j.simple;
|
|
||||||
|
|
||||||
requires com.fasterxml.jackson.core;
|
requires com.fasterxml.jackson.core;
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
requires com.fasterxml.jackson.module.paramnames;
|
requires com.fasterxml.jackson.module.paramnames;
|
||||||
requires io.xpipe.core;
|
requires io.xpipe.core;
|
||||||
|
requires io.xpipe.extension;
|
||||||
|
|
||||||
opens io.xpipe.beacon;
|
opens io.xpipe.beacon;
|
||||||
opens io.xpipe.beacon.exchange;
|
opens io.xpipe.beacon.exchange;
|
||||||
opens io.xpipe.beacon.message;
|
opens io.xpipe.beacon.message;
|
||||||
|
|
||||||
requires org.apache.commons.lang;
|
requires static lombok;
|
||||||
|
|
||||||
uses MessageExchange;
|
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();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TupleNode of(List<DataStructureNode> nodes) {
|
public static TupleNode of(List<? extends DataStructureNode> nodes) {
|
||||||
if (nodes == null) {
|
if (nodes == null) {
|
||||||
throw new IllegalArgumentException("Nodes must be not 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) {
|
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {
|
||||||
|
|
|
@ -3,6 +3,4 @@ package io.xpipe.core.source;
|
||||||
public interface DataSourceConnection extends AutoCloseable {
|
public interface DataSourceConnection extends AutoCloseable {
|
||||||
|
|
||||||
void init() throws Exception;
|
void init() throws Exception;
|
||||||
|
|
||||||
void close() throws Exception;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ import java.io.OutputStream;
|
||||||
|
|
||||||
public interface TableDataReadConnection extends DataSourceConnection {
|
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;
|
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 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
|
@Override
|
||||||
public DataSourceType getType() {
|
public DataSourceType getType() {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package io.xpipe.core.store;
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public abstract class InputStreamDataStore implements StreamDataStore {
|
public class InputStreamDataStore implements StreamDataStore {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
|
private BufferedInputStream bufferedInputStream;
|
||||||
|
private boolean opened = false;
|
||||||
|
|
||||||
public InputStreamDataStore(InputStream in) {
|
public InputStreamDataStore(InputStream in) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
|
@ -13,11 +16,17 @@ public abstract class InputStreamDataStore implements StreamDataStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInput() throws Exception {
|
public InputStream openInput() throws Exception {
|
||||||
return in;
|
if (opened) {
|
||||||
|
return bufferedInputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
opened = true;
|
||||||
|
bufferedInputStream = new BufferedInputStream(in);
|
||||||
|
return bufferedInputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openOutput() throws Exception {
|
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 com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -65,7 +66,7 @@ public class LocalFileDataStore extends FileDataStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInput() throws Exception {
|
public InputStream openInput() throws Exception {
|
||||||
return Files.newInputStream(file);
|
return new BufferedInputStream(Files.newInputStream(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.extension;
|
||||||
import io.xpipe.core.source.DataSourceDescriptor;
|
import io.xpipe.core.source.DataSourceDescriptor;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -24,13 +23,15 @@ public interface DataSourceGuiProvider {
|
||||||
|
|
||||||
String getDisplayName();
|
String getDisplayName();
|
||||||
|
|
||||||
Image getImage();
|
String getDisplayImage();
|
||||||
|
|
||||||
Supplier<String> getFileName();
|
String getFileName();
|
||||||
|
|
||||||
Map<Supplier<String>, String> getFileExtensions();
|
Map<Supplier<String>, String> getFileExtensions();
|
||||||
|
|
||||||
String getDataSourceDescription(DataSourceDescriptor<?> source);
|
String getDataSourceShortDescription(DataSourceDescriptor<?> source);
|
||||||
|
|
||||||
|
String getDataSourceLongDescription(DataSourceDescriptor<?> source);
|
||||||
|
|
||||||
Class<? extends DataSourceDescriptor<?>> getType();
|
Class<? extends DataSourceDescriptor<?>> getType();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package io.xpipe.extension;
|
package io.xpipe.extension;
|
||||||
|
|
||||||
import io.xpipe.core.source.DataSourceDescriptor;
|
import io.xpipe.core.source.DataSourceDescriptor;
|
||||||
|
import io.xpipe.core.store.DataStore;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public interface DataSourceProvider {
|
public interface DataSourceProvider {
|
||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
|
boolean supportsFile(Path file);
|
||||||
|
|
||||||
|
DataSourceDescriptor<?> createDefaultDataSource(DataStore input) throws Exception;
|
||||||
|
|
||||||
Class<? extends DataSourceDescriptor<?>> getType();
|
Class<? extends DataSourceDescriptor<?>> getType();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package io.xpipe.extension;
|
package io.xpipe.extension;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -32,6 +33,14 @@ public class DataSourceProviders {
|
||||||
return ALL.stream().filter(d -> d.getId().equals(name)).findAny();
|
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() {
|
public static Set<DataSourceProvider> getAll() {
|
||||||
return ALL;
|
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;
|
requires javafx.graphics;
|
||||||
|
|
||||||
exports io.xpipe.extension;
|
exports io.xpipe.extension;
|
||||||
|
exports io.xpipe.extension.cli;
|
||||||
|
|
||||||
uses DataSourceProvider;
|
uses DataSourceProvider;
|
||||||
uses DataSourceGuiProvider;
|
uses DataSourceGuiProvider;
|
||||||
uses SupportedApplicationProvider;
|
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 'api'
|
||||||
include 'extension'
|
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