Add dynamic tunnels

This commit is contained in:
crschnick 2023-07-18 11:53:46 +00:00
parent a218f9ac35
commit c8cf6aa3fb
28 changed files with 124 additions and 178 deletions

View file

@ -55,7 +55,15 @@ public class StoreCreationBarComp extends SimpleComp {
.shortcut(new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN)) .shortcut(new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addDatabase")); .apply(new FancyTooltipAugment<>("addDatabase"));
var box = new VerticalComp(List.of(newHostStore, newShellStore, newStreamStore, newDbStore)) var newTunnelStore = new ButtonComp(AppI18n.observable("addTunnel"), new FontIcon("mdi2v-vector-polyline-plus"), () -> {
GuiDsStoreCreator.showCreation(
v -> v.getDisplayCategory().equals(DataStoreProvider.DisplayCategory.TUNNEL));
})
.styleClass(Styles.FLAT)
.shortcut(new KeyCodeCombination(KeyCode.T, KeyCombination.SHORTCUT_DOWN))
.apply(new FancyTooltipAugment<>("addTunnel"));
var box = new VerticalComp(List.of(newHostStore, newShellStore, newStreamStore, newDbStore, newTunnelStore))
.apply(struc -> struc.get().setFillWidth(true)); .apply(struc -> struc.get().setFillWidth(true));
box.apply(s -> AppFont.medium(s.get())); box.apply(s -> AppFont.medium(s.get()));
var bar = box.createRegion(); var bar = box.createRegion();

View file

@ -197,6 +197,7 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
found.createAction(entry.getStore().asNeeded()).execute(); found.createAction(entry.getStore().asNeeded()).execute();
} else if (getEntry().getStore() instanceof FixedHierarchyStore) { } else if (getEntry().getStore() instanceof FixedHierarchyStore) {
refreshWithChildrenAsync(); refreshWithChildrenAsync();
} else {
} }
} }

View file

@ -22,7 +22,7 @@ import lombok.experimental.FieldDefaults;
@AllArgsConstructor @AllArgsConstructor
public class DataStoreSelectorComp extends Comp<CompStructure<Button>> { public class DataStoreSelectorComp extends Comp<CompStructure<Button>> {
DataStoreProvider.DataCategory category; DataStoreProvider.DisplayCategory category;
Property<DataStore> chosenStore; Property<DataStore> chosenStore;
@Override @Override
@ -34,7 +34,7 @@ public class DataStoreSelectorComp extends Comp<CompStructure<Button>> {
"inProgress", "inProgress",
null, null,
null, null,
v -> v.getCategory().equals(category), v -> v.getDisplayCategory().equals(category),
entry -> { entry -> {
chosenStore.setValue(entry.getStore()); chosenStore.setValue(entry.getStore());
}, },

View file

@ -1,52 +0,0 @@
package io.xpipe.app.comp.store;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataSourceProvider;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.TabPaneComp;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.store.DataStore;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Region;
import java.util.List;
import java.util.function.Predicate;
public class DsDbStoreChooserComp extends SimpleComp {
private final Property<DataStore> input;
private final ObservableValue<DataSourceProvider<?>> provider;
public DsDbStoreChooserComp(Property<DataStore> input, ObservableValue<DataSourceProvider<?>> provider) {
this.input = input;
this.provider = provider;
}
@Override
protected Region createSimple() {
var filter = Bindings.createObjectBinding(
() -> (Predicate<DataStoreEntry>) e -> {
if (provider.getValue() == null) {
return e.getProvider().getCategory() == DataStoreProvider.DataCategory.DATABASE;
}
return provider.getValue().couldSupportStore(e.getStore());
},
provider);
var connections = new TabPaneComp.Entry(
AppI18n.observable("savedConnections"),
"mdi2m-monitor",
NamedStoreChoiceComp.create(filter, input, DataStoreProvider.DataCategory.DATABASE)
.styleClass("store-local-file-chooser"));
var pane = new TabPaneComp(new SimpleObjectProperty<>(connections), List.of(connections));
pane.apply(s -> AppFont.normal(s.get()));
return pane.createRegion();
}
}

View file

@ -121,7 +121,7 @@ public class DsStreamStoreChoiceComp extends SimpleComp implements Validatable {
var other = new TabPaneComp.Entry( var other = new TabPaneComp.Entry(
AppI18n.observable("other"), AppI18n.observable("other"),
"mdrmz-web_asset", "mdrmz-web_asset",
new DataStoreSelectorComp(DataStoreProvider.DataCategory.STREAM, otherStore)); new DataStoreSelectorComp(DataStoreProvider.DisplayCategory.HOST, otherStore));
var selectedTab = new SimpleObjectProperty<TabPaneComp.Entry>(); var selectedTab = new SimpleObjectProperty<TabPaneComp.Entry>();
if (localStore.get() != null) { if (localStore.get() != null) {

View file

@ -105,11 +105,11 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
v -> true, v -> true,
newE -> { newE -> {
ThreadHelper.runAsync(() -> { ThreadHelper.runAsync(() -> {
e.applyChanges(newE);
if (!DataStorage.get().getStoreEntries().contains(e)) { if (!DataStorage.get().getStoreEntries().contains(e)) {
DataStorage.get().addStoreEntry(e); DataStorage.get().addStoreEntry(e);
} else {
DataStorage.get().updateEntry(e, newE);
} }
DataStorage.get().refresh();
}); });
}, },
true); true);

View file

@ -146,7 +146,7 @@ public class NamedStoreChoiceComp extends SimpleComp implements Validatable {
var text = new LabelComp(AppI18n.observable("noMatchingStoreFound")) var text = new LabelComp(AppI18n.observable("noMatchingStoreFound"))
.apply(struc -> VBox.setVgrow(struc.get(), Priority.ALWAYS)); .apply(struc -> VBox.setVgrow(struc.get(), Priority.ALWAYS));
var addButton = new ButtonComp(AppI18n.observable("addStore"), null, () -> { var addButton = new ButtonComp(AppI18n.observable("addStore"), null, () -> {
GuiDsStoreCreator.showCreation(v -> v.getCategory().equals(category)); // GuiDsStoreCreator.showCreation(v -> v.getCategory().equals(category));
}); });
var notice = new VerticalComp(List.of(text, addButton)) var notice = new VerticalComp(List.of(text, addButton))
.apply(struc -> { .apply(struc -> {

View file

@ -195,6 +195,10 @@ public class AppI18n {
} }
public String getMarkdownDocumentation(String name) { public String getMarkdownDocumentation(String name) {
if (!markdownDocumentations.containsKey(name)) {
TrackEvent.withWarn("Markdown documentation for key " + name + " not found").handle();
}
return markdownDocumentations.getOrDefault(name, ""); return markdownDocumentations.getOrDefault(name, "");
} }

View file

@ -1,6 +1,7 @@
package io.xpipe.app.core.mode; package io.xpipe.app.core.mode;
import io.xpipe.app.core.*; import io.xpipe.app.core.*;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.ErrorHandler; import io.xpipe.app.issue.ErrorHandler;
import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.issue.TrackEvent;
@ -105,6 +106,11 @@ public abstract class OperationMode {
setup(args); setup(args);
LauncherCommand.runLauncher(usedArgs); LauncherCommand.runLauncher(usedArgs);
inStartup = false; inStartup = false;
postInit(args);
}
public static void postInit(String[] args) {
DataStoreProviders.postInit(AppExtensionManager.getInstance().getExtendedLayer());
} }
public static boolean isInStartup() { public static boolean isInStartup() {

View file

@ -15,12 +15,12 @@ public class StoreProviderListExchangeImpl extends StoreProviderListExchange
@Override @Override
public Response handleRequest(BeaconHandler handler, Request msg) { public Response handleRequest(BeaconHandler handler, Request msg) {
var categories = DataStoreProvider.DataCategory.values(); var categories = DataStoreProvider.DisplayCategory.values();
var all = DataStoreProviders.getAll(); var all = DataStoreProviders.getAll();
var map = Arrays.stream(categories) var map = Arrays.stream(categories)
.collect(Collectors.toMap(category -> getName(category), category -> all.stream() .collect(Collectors.toMap(category -> getName(category), category -> all.stream()
.filter(dataStoreProvider -> .filter(dataStoreProvider ->
dataStoreProvider.getCategory().equals(category)) dataStoreProvider.getDisplayCategory().equals(category))
.map(p -> ProviderEntry.builder() .map(p -> ProviderEntry.builder()
.id(p.getId()) .id(p.getId())
.description(p.getDisplayDescription()) .description(p.getDisplayDescription())
@ -31,7 +31,7 @@ public class StoreProviderListExchangeImpl extends StoreProviderListExchange
return Response.builder().entries(map).build(); return Response.builder().entries(map).build();
} }
private String getName(DataStoreProvider.DataCategory category) { private String getName(DataStoreProvider.DisplayCategory category) {
return category.name().substring(0, 1).toUpperCase() return category.name().substring(0, 1).toUpperCase()
+ category.name().substring(1).toLowerCase(); + category.name().substring(1).toLowerCase();
} }

View file

@ -25,7 +25,6 @@ public interface DataStoreProvider {
} }
default void validate() { default void validate() {
getCategory();
for (Class<?> storeClass : getStoreClasses()) { for (Class<?> storeClass : getStoreClasses()) {
if (!JacksonizedValue.class.isAssignableFrom(storeClass)) { if (!JacksonizedValue.class.isAssignableFrom(storeClass)) {
throw new ExtensionException( throw new ExtensionException(
@ -34,6 +33,8 @@ public interface DataStoreProvider {
} }
} }
default void preAdd(DataStore store) {}
default Comp<?> customDisplay(StoreSection s) { default Comp<?> customDisplay(StoreSection s) {
return new StandardStoreEntryComp(s.getWrapper(), null); return new StandardStoreEntryComp(s.getWrapper(), null);
} }
@ -90,27 +91,7 @@ public interface DataStoreProvider {
return null; return null;
} }
default DataCategory getCategory() {
var c = getStoreClasses().get(0);
if (StreamDataStore.class.isAssignableFrom(c)) {
return DataCategory.STREAM;
}
if (FileSystem.class.isAssignableFrom(c) || ShellStore.class.isAssignableFrom(c)) {
return DataCategory.SHELL;
}
throw new ExtensionException("Provider " + getId() + " has no set category");
}
default DisplayCategory getDisplayCategory() { default DisplayCategory getDisplayCategory() {
var category = getCategory();
if (category.equals(DataCategory.SHELL)) {
return DisplayCategory.HOST;
}
if (category.equals(DataCategory.DATABASE)) {
return DisplayCategory.DATABASE;
}
return DisplayCategory.OTHER; return DisplayCategory.OTHER;
} }
@ -130,19 +111,26 @@ public interface DataStoreProvider {
return true; return true;
} }
default void postInit(){
}
default void storageInit() throws Exception {} default void storageInit() throws Exception {}
default boolean isShareable() { default boolean isShareable() {
return false; return false;
} }
String queryInformationString(DataStore store, int length) throws Exception; default String queryInformationString(DataStore store, int length) throws Exception {
return null;
}
default String queryInvalidInformationString(DataStore store, int length) throws Exception { default String queryInvalidInformationString(DataStore store, int length) throws Exception {
return null; return null;
} }
String toSummaryString(DataStore store, int length); default String toSummaryString(DataStore store, int length) {
return null;
}
default String i18n(String key) { default String i18n(String key) {
return AppI18n.get(getId() + "." + key); return AppI18n.get(getId() + "." + key);
@ -205,6 +193,7 @@ public interface DataStoreProvider {
DATABASE, DATABASE,
SHELL, SHELL,
COMMAND, COMMAND,
TUNNEL,
OTHER OTHER
} }
} }

View file

@ -17,7 +17,6 @@ public class DataStoreProviders {
if (ALL == null) { if (ALL == null) {
ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream() ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream()
.map(ServiceLoader.Provider::get) .map(ServiceLoader.Provider::get)
.sorted(Comparator.comparing(DataStoreProvider::getId))
.collect(Collectors.toList()); .collect(Collectors.toList());
ALL.removeIf(p -> { ALL.removeIf(p -> {
try { try {
@ -35,6 +34,20 @@ public class DataStoreProviders {
} }
} }
public static void postInit(ModuleLayer layer) {
ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream()
.map(ServiceLoader.Provider::get)
.sorted(Comparator.comparing(DataStoreProvider::getId))
.collect(Collectors.toList());
ALL.forEach(p -> {
try {
p.postInit();
} catch (Throwable e) {
ErrorEvent.fromThrowable(e).handle();
}
});
}
public static Optional<DataStoreProvider> byName(String name) { public static Optional<DataStoreProvider> byName(String name) {
if (ALL == null) { if (ALL == null) {
throw new IllegalStateException("Not initialized"); throw new IllegalStateException("Not initialized");

View file

@ -42,6 +42,11 @@ public class DataStateProviderImpl extends DataStateProvider {
return c.cast(result); return c.cast(result);
} }
public boolean isInStorage(DataStore store) {
var entry = DataStorage.get().getStoreEntryIfPresent(store);
return entry.isPresent();
}
@Override @Override
public Path getInternalStreamStore(UUID id) { public Path getInternalStreamStore(UUID id) {
return DataStorage.get().getInternalStreamPath(id); return DataStorage.get().getInternalStreamPath(id);

View file

@ -92,6 +92,7 @@ public abstract class DataStorage {
Collections.reverse(ordered); Collections.reverse(ordered);
synchronized (this) { synchronized (this) {
ordered.forEach(entry -> entry.finalizeEntry());
this.storeEntries.removeAll(ordered); this.storeEntries.removeAll(ordered);
this.listeners.forEach(l -> l.onStoreRemove(ordered.toArray(DataStoreEntry[]::new))); this.listeners.forEach(l -> l.onStoreRemove(ordered.toArray(DataStoreEntry[]::new)));
} }
@ -213,6 +214,12 @@ public abstract class DataStorage {
} }
} }
public void updateEntry(DataStoreEntry entry, DataStoreEntry newEntry) {
newEntry.finalizeEntry();
entry.applyChanges(newEntry);
entry.initializeEntry();
}
public void refreshAsync(DataStoreEntry element, boolean deep) { public void refreshAsync(DataStoreEntry element, boolean deep) {
ThreadHelper.runAsync(() -> { ThreadHelper.runAsync(() -> {
try { try {
@ -233,6 +240,8 @@ public abstract class DataStorage {
} }
public void addStoreEntry(@NonNull DataStoreEntry e) { public void addStoreEntry(@NonNull DataStoreEntry e) {
e.getProvider().preAdd(e.getStore());
synchronized (this) { synchronized (this) {
e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
this.storeEntries.add(e); this.storeEntries.add(e);
@ -241,16 +250,22 @@ public abstract class DataStorage {
save(); save();
this.listeners.forEach(l -> l.onStoreAdd(e)); this.listeners.forEach(l -> l.onStoreAdd(e));
e.initializeEntry();
} }
public void addStoreEntries(@NonNull DataStoreEntry... es) { public void addStoreEntries(@NonNull DataStoreEntry... es) {
synchronized (this) { synchronized (this) {
for (DataStoreEntry e : es) { for (DataStoreEntry e : es) {
e.getProvider().preAdd(e.getStore());
e.setDirectory(getStoresDir().resolve(e.getUuid().toString())); e.setDirectory(getStoresDir().resolve(e.getUuid().toString()));
this.storeEntries.add(e); this.storeEntries.add(e);
propagateUpdate(e); propagateUpdate(e);
} }
this.listeners.forEach(l -> l.onStoreAdd(es)); this.listeners.forEach(l -> l.onStoreAdd(es));
for (DataStoreEntry e : es) {
e.initializeEntry();
}
} }
save(); save();
} }
@ -284,6 +299,7 @@ public abstract class DataStorage {
} }
public void deleteStoreEntry(@NonNull DataStoreEntry store) { public void deleteStoreEntry(@NonNull DataStoreEntry store) {
store.finalizeEntry();
synchronized (this) { synchronized (this) {
this.storeEntries.remove(store); this.storeEntries.remove(store);
} }
@ -298,13 +314,6 @@ public abstract class DataStorage {
public abstract void load(); public abstract void load();
public void refresh() {
getStoreEntries().forEach(entry -> {
entry.simpleRefresh();
});
save();
}
public abstract void save(); public abstract void save();
public synchronized Optional<DataStoreEntry> getStoreEntry(UUID id) { public synchronized Optional<DataStoreEntry> getStoreEntry(UUID id) {

View file

@ -288,6 +288,36 @@ public class DataStoreEntry extends StorageElement {
} }
} }
@SneakyThrows
public void initializeEntry() {
try {
state = State.VALIDATING;
listeners.forEach(l -> l.onUpdate());
store.initializeValidate();
state = State.COMPLETE_AND_VALID;
} catch (Exception e) {
state = State.COMPLETE_BUT_INVALID;
ErrorEvent.fromThrowable(e).handle();
} finally {
propagateUpdate();
}
}
@SneakyThrows
public void finalizeEntry() {
try {
state = State.VALIDATING;
listeners.forEach(l -> l.onUpdate());
store.finalizeValidate();
state = State.COMPLETE_AND_VALID;
} catch (Exception e) {
state = State.COMPLETE_BUT_INVALID;
ErrorEvent.fromThrowable(e).handle();
} finally {
propagateUpdate();
}
}
@Override @Override
protected boolean shouldSave() { protected boolean shouldSave() {
return getStore() == null || getStore().shouldSave(); return getStore() == null || getStore().shouldSave();

View file

@ -42,6 +42,12 @@ public class OptionsBuilder {
entries.add(entry); entries.add(entry);
} }
public OptionsBuilder sub(OptionsBuilder builder) {
props.addAll(builder.props);
pushComp(builder.buildComp());
return this;
}
public OptionsBuilder addTitle(String titleKey) { public OptionsBuilder addTitle(String titleKey) {
finishCurrent(); finishCurrent();
entries.add(new OptionsComp.Entry( entries.add(new OptionsComp.Entry(

View file

@ -42,6 +42,7 @@ clean=Clean
refresh=Refresh refresh=Refresh
remove=Remove remove=Remove
addDatabase=Add Database ... addDatabase=Add Database ...
addTunnel=Add Tunnel ...
addHost=Add Remote Host ... addHost=Add Remote Host ...
addShell=Add Environment ... addShell=Add Environment ...
addCommand=Add Command ... addCommand=Add Command ...

View file

@ -116,7 +116,6 @@ noMatchingSourceFound=No matching source found
addSource=Add Source addSource=Add Source
edit=Edit edit=Edit
addStream=Add File addStream=Add File
addDatabase=Add Database
pipeStream=Pipe File pipeStream=Pipe File
pipeDatabase=Pipe Database pipeDatabase=Pipe Database
transfer=Transfer transfer=Transfer

View file

@ -9,13 +9,10 @@ import lombok.Getter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized; import lombok.extern.jackson.Jacksonized;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@JsonTypeName("internalStream") @JsonTypeName("internalStream")
@ -35,20 +32,10 @@ public class InternalStreamStore extends JacksonizedValue implements StreamDataS
return DataFlow.INPUT_OUTPUT; return DataFlow.INPUT_OUTPUT;
} }
@Override
public Optional<String> determineDefaultName() {
return Optional.of(uuid.toString());
}
private Path getFile() { private Path getFile() {
return DataStateProvider.get().getInternalStreamStore(uuid); return DataStateProvider.get().getInternalStreamStore(uuid);
} }
@Override
public Optional<Instant> determineLastModified() throws IOException {
return Optional.of(Files.getLastModifiedTime(getFile()).toInstant());
}
@Override @Override
public InputStream openInput() throws Exception { public InputStream openInput() throws Exception {
return Files.newInputStream(getFile()); return Files.newInputStream(getFile());

View file

@ -5,11 +5,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant;
import java.util.Optional;
@JsonTypeName("localDir") @JsonTypeName("localDir")
@EqualsAndHashCode @EqualsAndHashCode
@ -22,21 +18,6 @@ public class LocalDirectoryDataStore implements DataStore {
this.file = file; this.file = file;
} }
@Override
public Optional<String> determineDefaultName() {
return Optional.of(file.getFileName().toString());
}
@Override
public Optional<Instant> determineLastModified() {
try {
var l = Files.getLastModifiedTime(file);
return Optional.of(l.toInstant());
} catch (IOException e) {
return Optional.empty();
}
}
public Path getPath() { public Path getPath() {
return file; return file;
} }

View file

@ -6,9 +6,6 @@ import lombok.Getter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized; import lombok.extern.jackson.Jacksonized;
import java.time.Instant;
import java.util.Optional;
/** /**
* A store that refers to another store in the XPipe storage. * A store that refers to another store in the XPipe storage.
* The referenced store has to be resolved by the caller manually, as this class does not act as a resolver. * The referenced store has to be resolved by the caller manually, as this class does not act as a resolver.
@ -36,13 +33,4 @@ public final class NamedStore implements DataStore {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public Optional<String> determineDefaultName() {
throw new UnsupportedOperationException();
}
@Override
public Optional<Instant> determineLastModified() {
throw new UnsupportedOperationException();
}
} }

View file

@ -5,10 +5,6 @@ import io.xpipe.core.impl.StdinDataStore;
import io.xpipe.core.impl.StdoutDataStore; import io.xpipe.core.impl.StdoutDataStore;
import io.xpipe.core.source.DataSource; import io.xpipe.core.source.DataSource;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
/** /**
* A data store represents some form of a location where data is stored, e.g. a file or a database. * A data store represents some form of a location where data is stored, e.g. a file or a database.
* It does not contain any information on what data is stored, * It does not contain any information on what data is stored,
@ -77,6 +73,10 @@ public interface DataStore {
*/ */
default void validate() throws Exception {} default void validate() throws Exception {}
default void initializeValidate() throws Exception {}
default void finalizeValidate() throws Exception {}
default void checkComplete() throws Exception {} default void checkComplete() throws Exception {}
default boolean delete() { default boolean delete() {
@ -90,19 +90,4 @@ public interface DataStore {
default <DS extends DataStore> DS asNeeded() { default <DS extends DataStore> DS asNeeded() {
return (DS) this; return (DS) this;
} }
/**
* Determines on optional default name for this data store that is
* used when determining a suitable default name for a data source.
*/
default Optional<String> determineDefaultName() {
return Optional.empty();
}
/**
* Determines the last modified of this data store if this data store supports it.
*/
default Optional<Instant> determineLastModified() throws IOException {
return Optional.empty();
}
} }

View file

@ -1,20 +1,11 @@
package io.xpipe.core.store; package io.xpipe.core.store;
import java.util.Optional;
/** /**
* Represents a store that has a filename. * Represents a store that has a filename.
* Note that this does not only apply to file stores but any other store as well that has some kind of file name. * Note that this does not only apply to file stores but any other store as well that has some kind of file name.
*/ */
public interface FilenameStore extends DataStore { public interface FilenameStore extends DataStore {
@Override
default Optional<String> determineDefaultName() {
var n = getFileName();
var i = n.lastIndexOf('.');
return Optional.of(i != -1 ? n.substring(0, i) : n);
}
default String getFileExtension() { default String getFileExtension() {
var split = getFileName().split("[\\\\.]"); var split = getFileName().split("[\\\\.]");
if (split.length == 0) { if (split.length == 0) {

View file

@ -6,6 +6,10 @@ import java.util.function.Supplier;
public interface StatefulDataStore extends DataStore { public interface StatefulDataStore extends DataStore {
default boolean isInStorage() {
return DataStateProvider.get().isInStorage(this);
}
default <T> T getState(String key, Class<T> c, T def) { default <T> T getState(String key, Class<T> c, T def) {
return DataStateProvider.get().getState(this, key, c, () -> def); return DataStateProvider.get().getState(this, key, c, () -> def);
} }

View file

@ -25,5 +25,7 @@ public abstract class DataStateProvider {
public abstract <T> T getState(DataStore store, String key, Class<T> c, Supplier<T> def); public abstract <T> T getState(DataStore store, String key, Class<T> c, Supplier<T> def);
public abstract boolean isInStorage(DataStore store);
public abstract Path getInternalStreamStore(UUID id); public abstract Path getInternalStreamStore(UUID id);
} }

View file

@ -25,7 +25,6 @@ import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.IllegalCharsetNameException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Optional;
@JsonTypeName("http") @JsonTypeName("http")
@SuperBuilder @SuperBuilder
@ -101,11 +100,6 @@ public class HttpStore extends JacksonizedValue implements StreamDataStore, Stat
Validators.nonNull(headers, "Headers"); Validators.nonNull(headers, "Headers");
} }
@Override
public Optional<String> determineDefaultName() {
return Optional.ofNullable(getURL().getHost());
}
@Override @Override
public void validate() throws Exception { public void validate() throws Exception {
var client = createClient(); var client = createClient();

View file

@ -70,11 +70,6 @@ public class InMemoryStoreProvider implements DataStoreProvider {
}); });
} }
@Override
public DataCategory getCategory() {
return DataCategory.STREAM;
}
@Override @Override
public DataStore defaultStore() { public DataStore defaultStore() {
return new InMemoryStore(new byte[0]); return new InMemoryStore(new byte[0]);

View file

@ -1 +1 @@
1.3.2 1.4.0