Cleanup, some documentation, and refactoring

This commit is contained in:
Christopher Schnick 2022-03-08 23:30:13 +01:00
parent f5cccd5687
commit f7b37412b4
49 changed files with 234 additions and 270 deletions

View file

@ -24,11 +24,6 @@ dependencies {
}
test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
showStandardStreams = true
}
workingDir = rootDir
// Daemon properties
@ -38,10 +33,11 @@ test {
" -Dio.xpipe.app.dataDir=$projectDir/local/" +
" -Dio.xpipe.storage.persist=false" +
" -Dio.xpipe.app.writeSysOut=true" +
" -Dio.xpipe.beacon.debugOutput=true" +
// " -Dio.xpipe.beacon.debugOutput=true" +
" -Dio.xpipe.app.logLevel=trace"
// API properties
// systemProperty 'io.xpipe.beacon.debugOutput', "true"
systemProperty 'io.xpipe.beacon.debugExecOutput', "true"
systemProperty "io.xpipe.beacon.port", "21722"
}

View file

@ -4,8 +4,8 @@ publishing {
from components.java
pom {
name = 'X-Pipe API'
description = 'Contains everything necessary to use and interact with X-Pipe.'
name = 'X-Pipe Java API'
description = 'Contains everything necessary to interact with X-Pipe from Java applications.'
url = 'https://github.com/xpipe-io/xpipe_java/api'
licenses {
license {
@ -25,10 +25,6 @@ publishing {
developerConnection = 'scm:git:ssh://github.com/xpipe-io/xpipe_java.git'
url = 'https://github.com/xpipe-io/xpipe_java'
}
withXml {
//asNode().appendNode('dependencies').appendNode('dependency').appendNode('my-property', 'my-value')
}
}
}
}

View file

@ -3,7 +3,6 @@ package io.xpipe.api;
import io.xpipe.api.impl.DataTableAccumulatorImpl;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.source.DataSourceId;
@ -11,7 +10,7 @@ import io.xpipe.core.source.DataSourceId;
* An accumulator for table data.
*
* This class can be used to construct new table data sources by
* accumulating the rows using {@link #add(TupleNode)} or {@link #acceptor()} and then calling
* accumulating the rows using {@link #add(DataStructureNode)} or {@link #acceptor()} and then calling
* {@link #finish(DataSourceId)} to complete the construction process and create a new data source.
*/
public interface DataTableAccumulator {

View file

@ -2,7 +2,7 @@ package io.xpipe.api.impl;
import io.xpipe.api.DataSource;
import io.xpipe.api.connector.XPipeConnection;
import io.xpipe.beacon.exchange.PreStoreExchange;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.beacon.exchange.QueryDataSourceExchange;
import io.xpipe.beacon.exchange.ReadExecuteExchange;
import io.xpipe.beacon.exchange.ReadPreparationExchange;
@ -43,8 +43,8 @@ public abstract class DataSourceImpl implements DataSource {
public static DataSource create(DataSourceId id, String type, Map<String,String> config, InputStream in) {
var res = XPipeConnection.execute(con -> {
var req = PreStoreExchange.Request.builder().build();
PreStoreExchange.Response r = con.performOutputExchange(req, out -> in.transferTo(out));
var req = StoreStreamExchange.Request.builder().build();
StoreStreamExchange.Response r = con.performOutputExchange(req, out -> in.transferTo(out));
return r;
});

View file

@ -5,7 +5,7 @@ import io.xpipe.api.DataTable;
import io.xpipe.api.DataTableAccumulator;
import io.xpipe.api.connector.XPipeConnection;
import io.xpipe.api.util.TypeDescriptor;
import io.xpipe.beacon.exchange.PreStoreExchange;
import io.xpipe.beacon.exchange.StoreStreamExchange;
import io.xpipe.beacon.exchange.ReadExecuteExchange;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
@ -29,14 +29,14 @@ public class DataTableAccumulatorImpl implements DataTableAccumulator {
public DataTableAccumulatorImpl(TupleType type) {
this.type = type;
connection = XPipeConnection.open();
connection.sendRequest(PreStoreExchange.Request.builder().build());
connection.sendRequest(StoreStreamExchange.Request.builder().build());
connection.sendBody();
}
@Override
public synchronized DataTable finish(DataSourceId id) {
connection.withOutputStream(OutputStream::close);
PreStoreExchange.Response res = connection.receiveResponse();
StoreStreamExchange.Response res = connection.receiveResponse();
connection.close();
var req = ReadExecuteExchange.Request.builder()

View file

@ -1,7 +1,6 @@
module io.xpipe.api {
exports io.xpipe.api;
requires transitive io.xpipe.core;
requires io.xpipe.beacon;
exports io.xpipe.api;
exports io.xpipe.api.connector;
}

View file

@ -1,4 +0,0 @@
cd ..\app\
SET "dir=%~dp0test_env"
CALL ..\gradlew.bat run -Dio.xpipe.storage.dir=%dir% -Dio.xpipe.beacon.port=21722 -Dio.xpipe.daemon.mode=gui
pause

36
beacon/README.md Normal file
View file

@ -0,0 +1,36 @@
## X-Pipe Beacon
The X-Pipe beacon component is responsible for handling all communications between the X-Pipe daemon
and the various programming language APIs and the CLI. It provides an API that supports all kinds
of different operations.
The underlying inter-process communication is realized through TCP sockets on port `21721`.
The data structures and exchange protocols are specified in the `io.xpipe.beacon.exchange` package.
Every exchange is initiated from the outside by sending a request message to the daemon.
The daemon then always sends a response message.
The header information of a message is formatted in the json format.
As a result, all data structures exchanged must be serializable/deserializable with jackson.
Both the requests and responses can optionally include content in a body.
A body is initiated with two new lines (`\n`).
The body is split into segments of max length `65536`.
Each segment is preceded by four bytes that specify the length of the next segment.
In case the next segment has a length of less than `65536` bytes, we know that the end of the body has been reached.
This way the socket communication can handle payloads of unknown length.
### Configuration
The default port used by the beacon implementation of the X-Pipe daemon and APIs is `21721`.
It can be changed by passing the property `io.xpipe.beacon.port=<port>` to both the daemon and APIs.
The beacon API also supports launching the daemon automatically in case it is not started yet.
By default, it launches the daemon of the local X-Pipe installation.
It is possible to pass a custom launch command with the property `io.xpipe.beacon.exec=<cmd>`.
This allows for a custom launch behaviour in a testing/development environment.
By passing the property `io.xpipe.beacon.debugOutput=true`, it is possible to print debug information
about the underlying communications.
In case the `io.xpipe.beacon.exec` property is set, the output of the custom exec command can also be
printed by passing the property `io.xpipe.beacon.debugExecOutput=true`.

View file

@ -4,8 +4,8 @@ publishing {
from components.java
pom {
name = 'X-Pipe beacon'
description = 'The socket-based implementation used for the communication with X-Pipe.'
name = 'X-Pipe Beacon'
description = 'The socket-based implementation used for the communication with the X-Pipe daemon.'
url = 'https://github.com/xpipe-io/xpipe_java/beacon'
licenses {
license {

View file

@ -68,10 +68,6 @@ public class BeaconClient implements AutoCloseable {
out = socket.getOutputStream();
}
public boolean isClosed() {
return socket.isClosed();
}
public void close() throws ConnectorException {
try {
socket.close();
@ -132,14 +128,14 @@ public class BeaconClient implements AutoCloseable {
throw new ClientException("Unknown request class " + req.getClass());
}
json.set("type", new TextNode(prov.get().getId()));
json.set("phase", new TextNode("request"));
json.set("messageType", new TextNode(prov.get().getId()));
json.set("messagePhase", new TextNode("request"));
//json.set("id", new TextNode(UUID.randomUUID().toString()));
var msg = JsonNodeFactory.instance.objectNode();
msg.set("xPipeMessage", json);
if (BeaconConfig.debugEnabled()) {
System.out.println("Sending request to server of type " + req.getClass().getSimpleName());
System.out.println("Sending request to server of type " + req.getClass().getName());
}
try {
@ -163,7 +159,7 @@ public class BeaconClient implements AutoCloseable {
}
if (BeaconConfig.debugEnabled()) {
System.out.println("Recieved response:");
System.out.println("Received response:");
System.out.println(read.toPrettyString());
}
@ -211,14 +207,14 @@ public class BeaconClient implements AutoCloseable {
private <T extends ResponseMessage> T parseResponse(JsonNode header) throws ConnectorException {
ObjectNode content = (ObjectNode) header.required("xPipeMessage");
var type = content.required("type").textValue();
var phase = content.required("phase").textValue();
var type = content.required("messageType").textValue();
var phase = content.required("messagePhase").textValue();
//var requestId = UUID.fromString(content.required("id").textValue());
if (!phase.equals("response")) {
throw new IllegalArgumentException();
}
content.remove("type");
content.remove("phase");
content.remove("messageType");
content.remove("messagePhase");
//content.remove("id");
var prov = MessageExchanges.byId(type);

View file

@ -1,7 +1,10 @@
package io.xpipe.beacon;
import lombok.experimental.UtilityClass;
import java.nio.charset.StandardCharsets;
@UtilityClass
public class BeaconConfig {
public static final byte[] BODY_SEPARATOR = "\n\n".getBytes(StandardCharsets.UTF_8);
@ -15,6 +18,16 @@ public class BeaconConfig {
}
private static final String EXEC_DEBUG_PROP = "io.xpipe.beacon.debugExecOutput";
public static boolean execDebugEnabled() {
if (System.getProperty(EXEC_DEBUG_PROP) != null) {
return Boolean.parseBoolean(System.getProperty(EXEC_DEBUG_PROP));
}
return false;
}
public static final String BEACON_PORT_PROP = "io.xpipe.beacon.port";
public static final int DEFAULT_PORT = 21721;

View file

@ -1,5 +1,8 @@
package io.xpipe.beacon;
/**
* An unchecked exception that will be thrown in any case of an underlying exception.
*/
public class BeaconException extends RuntimeException {
public BeaconException() {

View file

@ -1,18 +1,22 @@
package io.xpipe.beacon;
import lombok.experimental.UtilityClass;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
@UtilityClass
public class BeaconFormat {
private static final int SEGMENT_SIZE = 65536;
public static OutputStream writeBlocks(Socket socket) throws IOException {
int size = 65536 - 4;
var out = socket.getOutputStream();
return new OutputStream() {
private final byte[] currentBytes = new byte[size];
private final byte[] currentBytes = new byte[SEGMENT_SIZE];
private int index;
@Override
@ -43,21 +47,9 @@ public class BeaconFormat {
index = 0;
}
};
// while (true) {
// var bytes = in.readNBytes(size);
// int length = bytes.length;
// var lengthBuffer = ByteBuffer.allocate(4).putInt(length);
// socket.getOutputStream().write(lengthBuffer.array());
// socket.getOutputStream().write(bytes);
//
// if (length == 0) {
// return;
// }
// }
}
public static InputStream readBlocks(Socket socket) throws IOException {
int size = 65536 - 4;
var in = socket.getInputStream();
return new InputStream() {
@ -90,7 +82,7 @@ public class BeaconFormat {
currentBytes = in.readNBytes(lengthInt);
index = 0;
if (lengthInt < size) {
if (lengthInt < SEGMENT_SIZE) {
finished = true;
}
}

View file

@ -4,11 +4,29 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* An exchange handler responsible for properly handling a request and sending a response.
*/
public interface BeaconHandler {
/**
* Execute a Runnable after the initial response has been sent.
*
* @param r the runnable to execute
*/
void postResponse(BeaconClient.FailableRunnable<Exception> r);
/**
* Prepares to attach a body to a response.
*
* @return the output stream that can be used to write the body payload
*/
OutputStream sendBody() throws IOException;
/**
* Prepares to read an attached body of a request.
*
* @return the input stream that can be used to read the body payload
*/
InputStream receiveBody() throws IOException;
}

View file

@ -1,6 +1,7 @@
package io.xpipe.beacon;
import io.xpipe.beacon.exchange.StopExchange;
import lombok.experimental.UtilityClass;
import java.io.BufferedReader;
import java.io.IOException;
@ -11,10 +12,14 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
/**
* Contains basic functionality to start, communicate, and stop a beacon server.
*/
@UtilityClass
public class BeaconServer {
private static boolean isPortAvailable(int port) {
try (var ss = new ServerSocket(port); var ds = new DatagramSocket(port)) {
try (var ignored = new ServerSocket(port); var ignored1 = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
return false;
@ -27,15 +32,18 @@ public class BeaconServer {
}
private static void startFork(String custom) throws IOException {
boolean print = true;
boolean print = BeaconConfig.execDebugEnabled();
var proc = Runtime.getRuntime().exec(custom);
new Thread(null, () -> {
try {
InputStreamReader isr = new InputStreamReader(proc.getInputStream());
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
System.out.println("[xpiped] " + line);
String line;
while ((line = br.readLine()) != null) {
if (print) {
System.out.println("[xpiped] " + line);
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
@ -70,7 +78,7 @@ public class BeaconServer {
private static Optional<Path> getPortableLauncherExecutable() {
var env = System.getenv("XPIPE_HOME");
Path file = null;
Path file;
// Prepare for invalid XPIPE_HOME path value
try {
@ -92,7 +100,7 @@ public class BeaconServer {
}
try {
Path file = null;
Path file;
if (System.getProperty("os.name").startsWith("Windows")) {
file = Path.of(System.getenv("LOCALAPPDATA"), "X-Pipe", "xpipe_launcher.exe");
} else {
@ -106,7 +114,7 @@ public class BeaconServer {
public static Optional<Path> getDaemonExecutable() {
try {
Path file = null;
Path file;
if (System.getProperty("os.name").startsWith("Windows")) {
file = Path.of(System.getenv("LOCALAPPDATA"), "X-Pipe", "app", "xpipe.exe");
} else {

View file

@ -1,5 +1,8 @@
package io.xpipe.beacon;
/**
* Indicates that a client request caused an issue.
*/
public class ClientException extends Exception {
public ClientException() {

View file

@ -1,5 +1,8 @@
package io.xpipe.beacon;
/**
* Indicates that a connection error occurred.
*/
public class ConnectorException extends Exception {
public ConnectorException() {

View file

@ -1,5 +1,11 @@
package io.xpipe.beacon;
import lombok.experimental.StandardException;
/**
* Indicates that an internal server error occurred.
*/
@StandardException
public class ServerException extends Exception {
public ServerException() {

View file

@ -9,6 +9,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Performs an edit for a data source.
*/
public class EditExecuteExchange implements MessageExchange<EditExecuteExchange.Request, EditExecuteExchange.Response> {
@Override

View file

@ -9,6 +9,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Requests to edit a data source.
*/
public class EditPreparationExchange implements MessageExchange<EditPreparationExchange.Request, EditPreparationExchange.Response> {
@Override

View file

@ -2,12 +2,30 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.SneakyThrows;
/**
* A message exchange scheme that implements a certain functionality.
*/
public interface MessageExchange<RQ extends RequestMessage, RP extends ResponseMessage> {
/**
* The unique id of this exchange that will be included in the messages.
*/
String getId();
Class<RQ> getRequestClass();
/**
* Returns the request class, needed for serialization.
*/
@SneakyThrows
@SuppressWarnings("unchecked")
default Class<RQ> getRequestClass() {
var name = getClass().getName() + "$Request";
return (Class<RQ>) Class.forName(name);
}
/**
* Returns the response class, needed for serialization.
*/
Class<RP> getResponseClass();
}

View file

@ -1,39 +0,0 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.store.StreamDataStore;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class PreStoreExchange implements MessageExchange<PreStoreExchange.Request, PreStoreExchange.Response> {
@Override
public String getId() {
return "preStore";
}
@Override
public Class<PreStoreExchange.Request> getRequestClass() {
return PreStoreExchange.Request.class;
}
@Override
public Class<PreStoreExchange.Response> getResponseClass() {
return PreStoreExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
StreamDataStore store;
}
}

View file

@ -9,6 +9,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Queries general information about a data source.
*/
public class QueryDataSourceExchange implements MessageExchange<QueryDataSourceExchange.Request, QueryDataSourceExchange.Response> {
@Override

View file

@ -10,6 +10,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Sends stream-based data to a daemon.
*/
public class ReadExecuteExchange implements MessageExchange<ReadExecuteExchange.Request, ReadExecuteExchange.Response> {
@Override

View file

@ -9,6 +9,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Prepares a client to send stream-based data to a daemon.
*/
public class ReadPreparationExchange implements MessageExchange<ReadPreparationExchange.Request, ReadPreparationExchange.Response> {
@Override

View file

@ -6,6 +6,9 @@ import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Requests the daemon to stop.
*/
public class StopExchange implements MessageExchange<StopExchange.Request, StopExchange.Response> {
@Override

View file

@ -1,41 +0,0 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigOptions;
import io.xpipe.core.source.DataSourceId;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class StoreEditExchange implements MessageExchange<StoreEditExchange.Request, StoreEditExchange.Response> {
@Override
public String getId() {
return "storeEdit";
}
@Override
public Class<StoreEditExchange.Request> getRequestClass() {
return StoreEditExchange.Request.class;
}
@Override
public Class<StoreEditExchange.Response> getResponseClass() {
return StoreEditExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
DataSourceId sourceId;
DataSourceConfigOptions config;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}

View file

@ -2,13 +2,14 @@ package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.source.DataSourceConfigOptions;
import io.xpipe.core.store.StreamDataStore;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Stores a stream of data in a storage.
*/
public class StoreStreamExchange implements MessageExchange<StoreStreamExchange.Request, StoreStreamExchange.Response> {
@Override
@ -30,16 +31,12 @@ public class StoreStreamExchange implements MessageExchange<StoreStreamExchange.
@Builder
@Value
public static class Request implements RequestMessage {
String type;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
DataSourceId sourceId;
DataSourceType sourceType;
DataSourceConfigOptions config;
Object data;
StreamDataStore store;
}
}

View file

@ -9,6 +9,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Queries data of a table data source in the xpbt format.
*/
public class QueryTableDataExchange implements MessageExchange<QueryTableDataExchange.Request, QueryTableDataExchange.Response> {
@Override

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.CollectionListEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.EntryListEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance;
@ -10,6 +11,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Output the data source contents.
*/
public class WriteExecuteExchange implements MessageExchange<WriteExecuteExchange.Request, WriteExecuteExchange.Response> {
@Override

View file

@ -1,5 +1,6 @@
package io.xpipe.beacon.exchange;
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance;
@ -10,6 +11,9 @@ import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
/**
* Prepares a client to output the data source contents.
*/
public class WritePreparationExchange implements MessageExchange<WritePreparationExchange.Request, WritePreparationExchange.Response> {
@Override
@ -31,7 +35,7 @@ public class WritePreparationExchange implements MessageExchange<WritePreparatio
@Builder
@Value
public static class Request implements RequestMessage {
String providerType;
String type;
String output;
@NonNull
DataSourceReference ref;
@ -41,7 +45,7 @@ public class WritePreparationExchange implements MessageExchange<WritePreparatio
@Builder
@Value
public static class Response implements ResponseMessage {
DataStore dataStore;
DataStore store;
@NonNull
DataSourceConfigInstance config;

View file

@ -1,5 +1,6 @@
import io.xpipe.beacon.exchange.*;
import io.xpipe.beacon.exchange.api.QueryTableDataExchange;
import io.xpipe.beacon.exchange.api.*;
import io.xpipe.beacon.exchange.cli.*;
module io.xpipe.beacon {
exports io.xpipe.beacon;
@ -7,12 +8,14 @@ module io.xpipe.beacon {
exports io.xpipe.beacon.message;
exports io.xpipe.beacon.exchange.api;
exports io.xpipe.beacon.exchange.data;
exports io.xpipe.beacon.exchange.cli;
opens io.xpipe.beacon;
opens io.xpipe.beacon.exchange;
opens io.xpipe.beacon.exchange.api;
opens io.xpipe.beacon.message;
opens io.xpipe.beacon.exchange.data;
opens io.xpipe.beacon.exchange.cli;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
@ -33,7 +36,7 @@ module io.xpipe.beacon {
ReadExecuteExchange,
DialogExchange,
QueryDataSourceExchange,
PreStoreExchange,
StoreStreamExchange,
EditPreparationExchange,
EditExecuteExchange,
QueryTableDataExchange,

View file

@ -4,8 +4,8 @@ publishing {
from components.java
pom {
name = 'X-Pipe core'
description = 'Core classes ued by all X-Pipe components.'
name = 'X-Pipe Core'
description = 'Core classes used by all X-Pipe components.'
url = 'https://github.com/xpipe-io/xpipe_java/core'
licenses {
license {

View file

@ -11,12 +11,12 @@ public abstract class TupleNode extends DataStructureNode {
return new Builder();
}
public static TupleNode of(List<? extends DataStructureNode> nodes) {
public static TupleNode of(List<DataStructureNode> nodes) {
if (nodes == null) {
throw new IllegalArgumentException("Nodes must be not null");
}
return new NoKeyTupleNode(true, (List<DataStructureNode>) nodes);
return new NoKeyTupleNode(true, nodes);
}
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {

View file

@ -1,6 +1,5 @@
package io.xpipe.core.store;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -19,27 +18,7 @@ public class OutputStreamStore implements StreamDataStore {
@Override
public OutputStream openOutput() throws Exception {
return new OutputStream() {
@Override
public void write(int b) throws IOException {
out.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
}
@Override
public void write(byte[] b) throws IOException {
out.write(b);
}
@Override
public void flush() throws IOException {
out.flush();
}
};
return out;
}
@Override

View file

@ -1,5 +1,7 @@
package io.xpipe.core.util;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
@ -103,6 +105,9 @@ public class CoreJacksonModule extends SimpleModule {
@JsonSerialize(as = Throwable.class)
public abstract static class ThrowableTypeMixIn {
@JsonIdentityInfo(generator= ObjectIdGenerators.StringIdGenerator.class, property="$id")
private Throwable cause;
}
@JsonSerialize(as = DataSourceReference.class)

View file

@ -1,27 +1,26 @@
import io.xpipe.core.util.CoreJacksonModule;
module io.xpipe.core {
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires static lombok;
exports io.xpipe.core.store;
exports io.xpipe.core.source;
exports io.xpipe.core.data.generic;
exports io.xpipe.core.data.type;
exports io.xpipe.core.util;
exports io.xpipe.core.data.node;
exports io.xpipe.core.data.typed;
opens io.xpipe.core.store;
opens io.xpipe.core.source;
opens io.xpipe.core.data.type;
opens io.xpipe.core.data.generic;
exports io.xpipe.core.util;
opens io.xpipe.core.util;
exports io.xpipe.core.data.node;
opens io.xpipe.core.data.node;
exports io.xpipe.core.data.typed;
opens io.xpipe.core.data.typed;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires static lombok;
uses com.fasterxml.jackson.databind.Module;
provides com.fasterxml.jackson.databind.Module with CoreJacksonModule;
}

View file

@ -27,5 +27,4 @@ repositories {
dependencies {
implementation project(':core')
implementation project(':fxcomps')
implementation group: 'com.dlsc.preferencesfx', name: 'preferencesfx-core', version: '11.8.0'
}

View file

@ -4,7 +4,7 @@ publishing {
from components.java
pom {
name = 'X-Pipe extension base'
name = 'X-Pipe Extension Base'
description = 'Classes required to create X-Pipe extensions.'
url = 'https://github.com/xpipe-io/xpipe_java/extension'
licenses {

View file

@ -1,51 +0,0 @@
package io.xpipe.extension.prefs;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.List;
public class PrefsChoiceValueModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
addSerializer(PrefsChoiceValue.class, new PrefsChoiceValueSerializer());
addDeserializer(PrefsChoiceValue.class, new PrefsChoiceValueDeserializer());
context.addSerializers(_serializers);
context.addDeserializers(_deserializers);
}
public static class PrefsChoiceValueSerializer extends JsonSerializer<PrefsChoiceValue> {
@Override
public void serialize(PrefsChoiceValue value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeString(value.getId());
}
}
public static class PrefsChoiceValueDeserializer extends JsonDeserializer<PrefsChoiceValue> {
@Override
public PrefsChoiceValue deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
var id = p.getValueAsString();
Class<? extends PrefsChoiceValue> clazz = (Class<? extends PrefsChoiceValue>) ctxt.getContextualType().getRawClass();
try {
var list = (List<? extends PrefsChoiceValue>) clazz.getDeclaredField("SUPPORTED").get(null);
return list.stream().filter(v -> v.getId().equals(id)).findAny().orElse(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return null;
}
}
}

View file

@ -1,9 +1,12 @@
import com.fasterxml.jackson.databind.Module;
import io.xpipe.extension.DataSourceProvider;
import io.xpipe.extension.SupportedApplicationProvider;
import io.xpipe.extension.prefs.PrefsChoiceValueModule;
module io.xpipe.extension {
exports io.xpipe.extension;
exports io.xpipe.extension.comp;
exports io.xpipe.extension.event;
exports io.xpipe.extension.prefs;
requires io.xpipe.core;
requires javafx.base;
requires javafx.graphics;
@ -11,20 +14,6 @@ module io.xpipe.extension {
requires io.xpipe.fxcomps;
requires org.apache.commons.collections4;
requires static lombok;
exports io.xpipe.extension;
exports io.xpipe.extension.comp;
exports io.xpipe.extension.event;
exports io.xpipe.extension.prefs;
uses DataSourceProvider;
uses SupportedApplicationProvider;
uses io.xpipe.extension.I18n;
uses io.xpipe.extension.event.EventHandler;
uses io.xpipe.extension.prefs.PrefsProvider;
provides Module with PrefsChoiceValueModule;
requires com.dlsc.preferencesfx;
requires com.dlsc.formsfx;
requires java.desktop;
@ -35,4 +24,10 @@ module io.xpipe.extension {
requires org.reactfx;
requires org.kordamp.ikonli.javafx;
requires com.fasterxml.jackson.databind;
uses DataSourceProvider;
uses SupportedApplicationProvider;
uses io.xpipe.extension.I18n;
uses io.xpipe.extension.event.EventHandler;
uses io.xpipe.extension.prefs.PrefsProvider;
}

View file

@ -1 +1 @@
0.1
0.1-SNAPSHOT