More beacon additions and small fixes

This commit is contained in:
Christopher Schnick 2022-05-28 23:52:08 +02:00
parent 628e322ca3
commit 948dcf33ef
25 changed files with 449 additions and 74 deletions

View file

@ -168,6 +168,10 @@ public class BeaconClient implements AutoCloseable {
System.out.println(read.toPrettyString()); System.out.println(read.toPrettyString());
} }
if (read.isMissingNode()) {
throw new ConnectorException("Received unexpected EOF");
}
var se = parseServerError(read); var se = parseServerError(read);
if (se.isPresent()) { if (se.isPresent()) {
se.get().throwError(); se.get().throwError();

View file

@ -75,12 +75,17 @@ public class BeaconServer {
public static boolean tryStart() throws Exception { public static boolean tryStart() throws Exception {
var custom = BeaconConfig.getCustomExecCommand(); var custom = BeaconConfig.getCustomExecCommand();
if (custom != null) { if (custom != null) {
System.out.println("Starting fork: " + custom);
startFork(custom); startFork(custom);
return true; return true;
} }
var daemonExecutable = getDaemonExecutable(); var daemonExecutable = getDaemonExecutable();
if (daemonExecutable.isPresent()) { if (daemonExecutable.isPresent()) {
if (BeaconConfig.debugEnabled()) {
System.out.println("Starting daemon executable: " + daemonExecutable.get());
}
// Tell daemon that we launched from an external tool // Tell daemon that we launched from an external tool
new ProcessBuilder(daemonExecutable.get().toString(), "--external") new ProcessBuilder(daemonExecutable.get().toString(), "--external")
.redirectErrorStream(true) .redirectErrorStream(true)

View file

@ -3,6 +3,7 @@ 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.DataSourceConfigInstance; import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference; import io.xpipe.core.source.DataSourceReference;
import lombok.Builder; import lombok.Builder;
import lombok.NonNull; import lombok.NonNull;
@ -19,21 +20,13 @@ public class EditExecuteExchange implements MessageExchange<EditExecuteExchange.
return "editExecute"; return "editExecute";
} }
@Override
public Class<EditExecuteExchange.Request> getRequestClass() {
return EditExecuteExchange.Request.class;
}
@Override
public Class<EditExecuteExchange.Response> getResponseClass() {
return EditExecuteExchange.Response.class;
}
@Jacksonized @Jacksonized
@Builder @Builder
@Value @Value
public static class Request implements RequestMessage { public static class Request implements RequestMessage {
@NonNull DataSourceReference ref; @NonNull
DataSourceReference ref;
@NonNull @NonNull
DataSourceConfigInstance config; DataSourceConfigInstance config;
} }
@ -42,5 +35,6 @@ public class EditExecuteExchange implements MessageExchange<EditExecuteExchange.
@Builder @Builder
@Value @Value
public static class Response implements ResponseMessage { public static class Response implements ResponseMessage {
DataSourceId id;
} }
} }

View file

@ -20,12 +20,19 @@ public interface MessageExchange<RQ extends RequestMessage, RP extends ResponseM
@SneakyThrows @SneakyThrows
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
default Class<RQ> getRequestClass() { default Class<RQ> getRequestClass() {
var name = getClass().getName() + "$Request"; var c = getClass().getSuperclass();
var name = (MessageExchange.class.isAssignableFrom(c) ? c : getClass()).getName() + "$Request";
return (Class<RQ>) Class.forName(name); return (Class<RQ>) Class.forName(name);
} }
/** /**
* Returns the response class, needed for serialization. * Returns the response class, needed for serialization.
*/ */
Class<RP> getResponseClass(); @SneakyThrows
@SuppressWarnings("unchecked")
default Class<RP> getResponseClass() {
var c = getClass().getSuperclass();
var name = (MessageExchange.class.isAssignableFrom(c) ? c : getClass()).getName() + "$Response";
return (Class<RP>) Class.forName(name);
}
} }

View file

@ -0,0 +1,30 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class RemoveCollectionExchange implements MessageExchange<RemoveCollectionExchange.Request, RemoveCollectionExchange.Response> {
@Override
public String getId() {
return "removeCollection";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
@NonNull
String collectionName;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}

View file

@ -0,0 +1,34 @@
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.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class RemoveEntryExchange implements MessageExchange<RemoveEntryExchange.Request, RemoveEntryExchange.Response> {
@Override
public String getId() {
return "removeEntry";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
@NonNull
DataSourceReference ref;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
@NonNull
DataSourceId id;
}
}

View file

@ -0,0 +1,32 @@
package io.xpipe.beacon.exchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class RenameCollectionExchange implements MessageExchange<RenameCollectionExchange.Request, RenameCollectionExchange.Response> {
@Override
public String getId() {
return "renameCollection";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
@NonNull
String collectionName;
@NonNull
String newName;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}

View file

@ -0,0 +1,36 @@
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.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class RenameEntryExchange implements MessageExchange<RenameEntryExchange.Request, RenameEntryExchange.Response> {
@Override
public String getId() {
return "renameEntry";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
@NonNull
DataSourceReference ref;
@NonNull
String newName;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
@NonNull DataSourceId newId;
}
}

View file

@ -0,0 +1,34 @@
package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class QueryRawDataExchange implements MessageExchange<QueryRawDataExchange.Request, QueryRawDataExchange.Response> {
@Override
public String getId() {
return "queryRawData";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
@NonNull
DataSourceReference ref;
int maxBytes;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}

View file

@ -16,16 +16,6 @@ public class QueryTextDataExchange implements MessageExchange<QueryTextDataExcha
return "queryTextData"; return "queryTextData";
} }
@Override
public Class<QueryTextDataExchange.Request> getRequestClass() {
return QueryTextDataExchange.Request.class;
}
@Override
public Class<QueryTextDataExchange.Response> getResponseClass() {
return QueryTextDataExchange.Response.class;
}
@Jacksonized @Jacksonized
@Builder @Builder
@Value @Value

View file

@ -6,6 +6,7 @@ import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance; import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.core.source.DataSourceId; import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference; import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.source.DataSourceType;
import lombok.Builder; import lombok.Builder;
import lombok.NonNull; import lombok.NonNull;
import lombok.Value; import lombok.Value;
@ -18,16 +19,6 @@ public class ConvertExchange implements MessageExchange<ConvertExchange.Request,
return "convert"; return "convert";
} }
@Override
public Class<ConvertExchange.Request> getRequestClass() {
return ConvertExchange.Request.class;
}
@Override
public Class<ConvertExchange.Response> getResponseClass() {
return ConvertExchange.Response.class;
}
@Jacksonized @Jacksonized
@Builder @Builder
@Value @Value
@ -35,15 +26,18 @@ public class ConvertExchange implements MessageExchange<ConvertExchange.Request,
@NonNull @NonNull
DataSourceReference ref; DataSourceReference ref;
@NonNull DataSourceId copyId; String newProvider;
@NonNull DataSourceType newType;
DataSourceConfigInstance config;
DataSourceId copyId;
} }
@Jacksonized @Jacksonized
@Builder @Builder
@Value @Value
public static class Response implements ResponseMessage { public static class Response implements ResponseMessage {
@NonNull
DataSourceConfigInstance config;
} }
} }

View file

@ -0,0 +1,35 @@
package io.xpipe.beacon.exchange.cli;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.exchange.data.ProviderEntry;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceType;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.util.List;
import java.util.Map;
public class ProviderListExchange implements MessageExchange<ProviderListExchange.Request, ProviderListExchange.Response> {
@Override
public String getId() {
return "providerList";
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
@NonNull Map<DataSourceType, List<ProviderEntry>> entries;
}
}

View file

@ -5,7 +5,6 @@ import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage; import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance; import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.core.source.DataSourceReference; import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.store.DataStore;
import lombok.Builder; import lombok.Builder;
import lombok.NonNull; import lombok.NonNull;
import lombok.Value; import lombok.Value;
@ -21,16 +20,6 @@ public class WriteExecuteExchange implements MessageExchange<WriteExecuteExchang
return "writeExecute"; return "writeExecute";
} }
@Override
public Class<WriteExecuteExchange.Request> getRequestClass() {
return WriteExecuteExchange.Request.class;
}
@Override
public Class<WriteExecuteExchange.Response> getResponseClass() {
return WriteExecuteExchange.Response.class;
}
@Jacksonized @Jacksonized
@Builder @Builder
@Value @Value
@ -38,7 +27,6 @@ public class WriteExecuteExchange implements MessageExchange<WriteExecuteExchang
@NonNull @NonNull
DataSourceReference ref; DataSourceReference ref;
DataStore dataStore;
@NonNull @NonNull
DataSourceConfigInstance config; DataSourceConfigInstance config;
} }

View file

@ -5,7 +5,7 @@ import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage; import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceConfigInstance; import io.xpipe.core.source.DataSourceConfigInstance;
import io.xpipe.core.source.DataSourceReference; import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.StreamDataStore;
import lombok.Builder; import lombok.Builder;
import lombok.NonNull; import lombok.NonNull;
import lombok.Value; import lombok.Value;
@ -21,22 +21,13 @@ public class WritePreparationExchange implements MessageExchange<WritePreparatio
return "writePreparation"; return "writePreparation";
} }
@Override
public Class<WritePreparationExchange.Request> getRequestClass() {
return WritePreparationExchange.Request.class;
}
@Override
public Class<WritePreparationExchange.Response> getResponseClass() {
return WritePreparationExchange.Response.class;
}
@Jacksonized @Jacksonized
@Builder @Builder
@Value @Value
public static class Request implements RequestMessage { public static class Request implements RequestMessage {
String type; String type;
String output; @NonNull
StreamDataStore output;
@NonNull @NonNull
DataSourceReference ref; DataSourceReference ref;
} }
@ -45,8 +36,6 @@ public class WritePreparationExchange implements MessageExchange<WritePreparatio
@Builder @Builder
@Value @Value
public static class Response implements ResponseMessage { public static class Response implements ResponseMessage {
DataStore store;
@NonNull @NonNull
DataSourceConfigInstance config; DataSourceConfigInstance config;
} }

View file

@ -0,0 +1,14 @@
package io.xpipe.beacon.exchange.data;
import lombok.Builder;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
@Value
@Jacksonized
@Builder
public class ProviderEntry {
String id;
String description;
boolean hidden;
}

View file

@ -40,7 +40,13 @@ module io.xpipe.beacon {
StoreStreamExchange, StoreStreamExchange,
EditPreparationExchange, EditPreparationExchange,
EditExecuteExchange, EditExecuteExchange,
RemoveEntryExchange,
RemoveCollectionExchange,
RenameCollectionExchange,
RenameEntryExchange,
ProviderListExchange,
ConvertExchange, ConvertExchange,
QueryRawDataExchange,
QueryTableDataExchange, QueryTableDataExchange,
VersionExchange; VersionExchange;
} }

View file

@ -14,6 +14,11 @@ public class MutableValueNode extends ValueNode {
this.textual = textual; this.textual = textual;
} }
@Override
public String asString() {
return new String(data);
}
@Override @Override
public String toString(int indent) { public String toString(int indent) {
return (textual ? "\"" : "") + new String(data) + (textual ? "\"" : "") + " (M)"; return (textual ? "\"" : "") + new String(data) + (textual ? "\"" : "") + " (M)";

View file

@ -1,6 +1,11 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.util.JacksonHelper;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.SneakyThrows;
import java.util.Optional; import java.util.Optional;
@ -10,16 +15,25 @@ import java.util.Optional;
* *
* This instance is only valid in combination with its associated data store instance. * This instance is only valid in combination with its associated data store instance.
*/ */
@AllArgsConstructor
public abstract class DataSource<DS extends DataStore> { public abstract class DataSource<DS extends DataStore> {
@NonNull
protected DS store; protected DS store;
public DataSource(DS store) { @SneakyThrows
this.store = store; @SuppressWarnings("unchecked")
public DataSource<DS> copy() {
var mapper = JacksonHelper.newMapper();
TokenBuffer tb = new TokenBuffer(mapper, false);
mapper.writeValue(tb, this);
return mapper.readValue(tb.asParser(), getClass());
} }
public DataSource<DS> withStore(DS newStore) { public DataSource<DS> withStore(DS store) {
return null; var c = copy();
c.store = store;
return c;
} }
/** /**

View file

@ -86,10 +86,12 @@ public abstract class DataSourceInfo {
@Value @Value
@JsonTypeName("text") @JsonTypeName("text")
public static class Text extends DataSourceInfo { public static class Text extends DataSourceInfo {
int characters;
int lineCount; int lineCount;
@JsonCreator @JsonCreator
public Text(int lineCount) { public Text(int characters, int lineCount) {
this.characters = characters;
this.lineCount = lineCount; this.lineCount = lineCount;
} }

View file

@ -1,11 +1,21 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import java.io.OutputStream;
public interface RawReadConnection extends DataSourceReadConnection { public interface RawReadConnection extends DataSourceReadConnection {
byte[] readBytes(int max) throws Exception; byte[] readBytes(int max) throws Exception;
int BUFFER_SIZE = 8192; int BUFFER_SIZE = 8192;
default void forwardBytes(OutputStream out, int maxBytes) throws Exception {
if (maxBytes == 0) {
return;
}
out.write(readBytes(maxBytes));
}
default void forward(DataSourceConnection con) throws Exception { default void forward(DataSourceConnection con) throws Exception {
try (var tCon = (RawWriteConnection) con) { try (var tCon = (RawWriteConnection) con) {
tCon.init(); tCon.init();

View file

@ -2,6 +2,8 @@ package io.xpipe.core.source;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class TextDataSource<DS extends DataStore> extends DataSource<DS> { public abstract class TextDataSource<DS extends DataStore> extends DataSource<DS> {
private static final int MAX_LINE_READ = 1000; private static final int MAX_LINE_READ = 1000;
@ -13,9 +15,14 @@ public abstract class TextDataSource<DS extends DataStore> extends DataSource<DS
@Override @Override
public final DataSourceInfo determineInfo() throws Exception { public final DataSourceInfo determineInfo() throws Exception {
try (var con = openReadConnection()) { try (var con = openReadConnection()) {
int count = (int) con.lines().limit(MAX_LINE_READ).count(); AtomicInteger lineCount = new AtomicInteger();
int usedCount = count == MAX_LINE_READ ? -1 : count; AtomicInteger charCount = new AtomicInteger();
return new DataSourceInfo.Text(usedCount); con.lines().limit(MAX_LINE_READ).forEach(s -> {
lineCount.getAndIncrement();
charCount.addAndGet(s.length());
});
boolean limitHit = lineCount.get() == MAX_LINE_READ;
return new DataSourceInfo.Text(limitHit ? -1 : charCount.get(), limitHit ? -1 : lineCount.get());
} }
} }

View file

@ -0,0 +1,93 @@
package io.xpipe.core.store;
import lombok.EqualsAndHashCode;
import lombok.Value;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@EqualsAndHashCode
@Value
public class StdinDataStore implements StreamDataStore {
@Override
public InputStream openInput() throws Exception {
var in = System.in;
return new InputStream() {
@Override
public int read() throws IOException {
return in.read();
}
@Override
public int read(byte[] b) throws IOException {
return in.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return in.read(b, off, len);
}
@Override
public byte[] readAllBytes() throws IOException {
return in.readAllBytes();
}
@Override
public byte[] readNBytes(int len) throws IOException {
return in.readNBytes(len);
}
@Override
public int readNBytes(byte[] b, int off, int len) throws IOException {
return in.readNBytes(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
@Override
public void skipNBytes(long n) throws IOException {
in.skipNBytes(n);
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
}
@Override
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
in.reset();
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public long transferTo(OutputStream out) throws IOException {
return in.transferTo(out);
}
};
}
@Override
public boolean exists() {
return false;
}
}

View file

@ -0,0 +1,46 @@
package io.xpipe.core.store;
import lombok.EqualsAndHashCode;
import lombok.Value;
import java.io.IOException;
import java.io.OutputStream;
@EqualsAndHashCode
@Value
public class StdoutDataStore implements StreamDataStore {
@Override
public OutputStream openOutput() throws Exception {
return new OutputStream() {
@Override
public void write(int b) throws IOException {
System.out.write(b);
}
@Override
public void write(byte[] b) throws IOException {
System.out.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
System.out.write(b, off, len);
}
@Override
public void flush() throws IOException {
System.out.flush();
}
@Override
public void close() throws IOException {
}
};
}
@Override
public boolean exists() {
return false;
}
}

View file

@ -107,13 +107,13 @@ public class DataSourceProviders {
.anyMatch(s -> s.equalsIgnoreCase(name))).findAny(); .anyMatch(s -> s.equalsIgnoreCase(name))).findAny();
} }
public static Optional<DataSourceProvider<?>> byStore(DataStore store) { public static Optional<DataSourceProvider<?>> byPreferredStore(DataStore store) {
if (ALL == null) { if (ALL == null) {
throw new IllegalStateException("Not initialized"); throw new IllegalStateException("Not initialized");
} }
return ALL.stream().filter(d -> d.getFileProvider() != null) return ALL.stream().filter(d -> d.getFileProvider() != null)
.filter(d -> d.couldSupportStore(store)).findAny(); .filter(d -> d.prefersStore(store)).findAny();
} }
public static Set<DataSourceProvider<?>> getAll() { public static Set<DataSourceProvider<?>> getAll() {

View file

@ -18,8 +18,14 @@ public interface SimpleFileDataSourceProvider<T extends DataSource<?>> extends D
continue; continue;
} }
for (var ext : e.getValue()) {
if (ext == null) {
continue;
}
if (store instanceof FileDataStore l) { if (store instanceof FileDataStore l) {
return l.getFileName().matches("\\." + e.getValue() + "$"); return l.getFileName().endsWith("." + ext);
}
} }
} }
return false; return false;