From d879c13aa4d1ec1139fb8fd0166e1ec55502e5f0 Mon Sep 17 00:00:00 2001 From: crschnick Date: Thu, 2 Mar 2023 16:57:49 +0000 Subject: [PATCH] Improvements and refactor for shell connections --- app/build.gradle | 2 +- .../io/xpipe/app/browser/FileContextMenu.java | 6 +- .../app/browser/OpenFileSystemModel.java | 6 +- .../xpipe/app/comp/base/ListBoxViewComp.java | 11 +- .../xpipe/app/comp/base/ListSelectorComp.java | 1 + .../comp/source/store/GuiDsStoreCreator.java | 15 +- .../java/io/xpipe/app/core/AppGreetings.java | 3 +- .../main/java/io/xpipe/app/core/AppI18n.java | 2 + .../io/xpipe/app/core/AppWindowHelper.java | 2 +- .../java/io/xpipe/app/ext/ScanProvider.java | 49 ++++++ .../xpipe/app/fxcomps/impl/OptionsComp.java | 163 +++++++++++++++++ .../app/prefs/ExternalApplicationType.java | 6 +- .../xpipe/app/prefs/ExternalTerminalType.java | 12 +- .../io/xpipe/app/storage/DataStorage.java | 19 +- .../io/xpipe/app/update/AppInstaller.java | 46 ++--- .../app/update/UpdateChangelogAlert.java | 2 +- .../io/xpipe/app/util/ApplicationHelper.java | 6 +- .../io/xpipe/app/util/MacOsPermissions.java | 2 +- .../io/xpipe/app/util/OptionsBuilder.java | 166 ++++++++++++++++++ .../app/util/ProxyManagerProviderImpl.java | 6 +- .../java/io/xpipe/app/util/ScanAlert.java | 64 +++++++ .../java/io/xpipe/app/util/ScriptHelper.java | 18 +- app/src/main/java/module-info.java | 4 +- .../resources/lang/translations_en.properties | 4 + .../app/resources/style/section-comp.css | 9 + .../java/io/xpipe/core/impl/LocalStore.java | 12 +- ...rocessControl.java => CommandControl.java} | 25 +-- .../java/io/xpipe/core/process/OsType.java | 53 ++++-- .../core/process/ProcessControlProvider.java | 42 ++--- ...lProcessControl.java => ShellControl.java} | 47 +++-- .../io/xpipe/core/process/ShellDialect.java | 8 +- .../core/store/CommandExecutionStore.java | 4 +- .../core/store/ConnectionFileSystem.java | 38 ++-- .../java/io/xpipe/core/store/FileSystem.java | 4 +- .../java/io/xpipe/core/store/ShellStore.java | 8 +- .../xpipe/core/util/ProxyManagerProvider.java | 6 +- .../io/xpipe/core/util/XPipeInstallation.java | 14 +- .../xpipe/core/util/XPipeTempDirectory.java | 6 +- 38 files changed, 700 insertions(+), 191 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/ext/ScanProvider.java create mode 100644 app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java create mode 100644 app/src/main/java/io/xpipe/app/util/OptionsBuilder.java create mode 100644 app/src/main/java/io/xpipe/app/util/ScanAlert.java rename core/src/main/java/io/xpipe/core/process/{CommandProcessControl.java => CommandControl.java} (65%) rename core/src/main/java/io/xpipe/core/process/{ShellProcessControl.java => ShellControl.java} (60%) diff --git a/app/build.gradle b/app/build.gradle index 95ca73cf..ed3fc133 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -132,7 +132,7 @@ application { run { systemProperty 'io.xpipe.app.mode', 'gui' - systemProperty 'io.xpipe.app.dataDir', "$projectDir/local_stage/" + systemProperty 'io.xpipe.app.dataDir', "$projectDir/local7/" systemProperty 'io.xpipe.app.writeLogs', "true" systemProperty 'io.xpipe.app.writeSysOut', "true" systemProperty 'io.xpipe.app.developerMode', "true" diff --git a/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java b/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java index 9683bb13..d10258ab 100644 --- a/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java +++ b/app/src/main/java/io/xpipe/app/browser/FileContextMenu.java @@ -7,7 +7,7 @@ import io.xpipe.app.ext.DataSourceProvider; import io.xpipe.app.util.*; import io.xpipe.core.impl.FileStore; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import io.xpipe.core.store.FileSystem; import javafx.beans.property.Property; import javafx.scene.control.ContextMenu; @@ -73,7 +73,7 @@ final class FileContextMenu extends ContextMenu { var execute = new MenuItem("Run in terminal"); execute.setOnAction(event -> { ThreadHelper.runFailableAsync(() -> { - ShellProcessControl pc = + ShellControl pc = model.getFileSystem().getShell().orElseThrow(); pc.executeBooleanSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath())); var cmd = pc.command("\"" + entry.getPath() + "\"").prepareTerminalOpen(); @@ -86,7 +86,7 @@ final class FileContextMenu extends ContextMenu { var executeInBackground = new MenuItem("Run in background"); executeInBackground.setOnAction(event -> { ThreadHelper.runFailableAsync(() -> { - ShellProcessControl pc = + ShellControl pc = model.getFileSystem().getShell().orElseThrow(); pc.executeBooleanSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath())); var cmd = ScriptHelper.createDetachCommand(pc, "\"" + entry.getPath() + "\""); diff --git a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java index b0409507..4c11b875 100644 --- a/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java +++ b/app/src/main/java/io/xpipe/app/browser/OpenFileSystemModel.java @@ -178,9 +178,9 @@ final class OpenFileSystemModel { var current = !(fileSystem instanceof LocalStore) && fs instanceof ConnectionFileSystem connectionFileSystem ? connectionFileSystem - .getShellProcessControl() + .getShellControl() .executeStringSimpleCommand(connectionFileSystem - .getShellProcessControl() + .getShellControl() .getShellDialect() .getPrintWorkingDirectoryCommand()) : null; @@ -199,7 +199,7 @@ final class OpenFileSystemModel { ThreadHelper.runFailableAsync(() -> { BusyProperty.execute(busy, () -> { if (store.getValue() instanceof ShellStore s) { - var connection = ((ConnectionFileSystem) fileSystem).getShellProcessControl(); + var connection = ((ConnectionFileSystem) fileSystem).getShellControl(); var command = s.create() .initWith(List.of(connection.getShellDialect().getCdCommand(directory))) .prepareTerminalOpen(); diff --git a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java index c86e4455..918879fd 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java @@ -8,6 +8,7 @@ import io.xpipe.app.fxcomps.util.PlatformThread; import javafx.application.Platform; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import javafx.scene.control.ScrollPane; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; @@ -16,7 +17,7 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -public class ListBoxViewComp extends Comp> { +public class ListBoxViewComp extends Comp> { private final ObservableList shown; private final ObservableList all; @@ -29,7 +30,7 @@ public class ListBoxViewComp extends Comp> { } @Override - public CompStructure createBase() { + public CompStructure createBase() { Map cache = new HashMap<>(); VBox listView = new VBox(); @@ -46,7 +47,11 @@ public class ListBoxViewComp extends Comp> { cache.keySet().retainAll(c.getList()); }); - return new SimpleCompStructure<>(listView); + var scroll = new ScrollPane(listView); + scroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scroll.setFitToWidth(true); + + return new SimpleCompStructure<>(scroll); } private void refresh(VBox listView, List c, Map cache, boolean asynchronous) { diff --git a/app/src/main/java/io/xpipe/app/comp/base/ListSelectorComp.java b/app/src/main/java/io/xpipe/app/comp/base/ListSelectorComp.java index ba7e023f..64d001c4 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ListSelectorComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ListSelectorComp.java @@ -24,6 +24,7 @@ public class ListSelectorComp extends SimpleComp { @Override protected Region createSimple() { var vbox = new VBox(); + vbox.setSpacing(8); vbox.getStyleClass().add("content"); for (var v : values) { var cb = new CheckBox(null); diff --git a/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java b/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java index 9104036f..a99ff05a 100644 --- a/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java +++ b/app/src/main/java/io/xpipe/app/comp/source/store/GuiDsStoreCreator.java @@ -25,7 +25,6 @@ import io.xpipe.app.util.*; import io.xpipe.core.store.DataStore; import javafx.application.Platform; import javafx.beans.property.*; -import javafx.beans.value.ObservableValue; import javafx.scene.control.Alert; import javafx.scene.control.Separator; import javafx.scene.layout.BorderPane; @@ -160,10 +159,11 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { } private Region createStoreProperties(Comp comp, Validator propVal) { - return new DynamicOptionsBuilder(false) - .addComp((ObservableValue) null, comp, input) - .addTitle(AppI18n.observable("properties")) - .addString(AppI18n.observable("name"), name, false) + return new OptionsBuilder() + .addComp(comp, input) + .name("connectionName") + .description("connectionNameDescription") + .addString(name, false) .nonNull(propVal) .bind( () -> { @@ -218,6 +218,11 @@ public class GuiDsStoreCreator extends MultiStepComp.Step> { return new LoadingOverlayComp(Comp.of(() -> layout), busy).createStructure(); } + @Override + public void onContinue() { + ScanAlert.showIfNeeded(entry.getValue().getStore()); + } + @Override public boolean canContinue() { if (provider.getValue() != null) { diff --git a/app/src/main/java/io/xpipe/app/core/AppGreetings.java b/app/src/main/java/io/xpipe/app/core/AppGreetings.java index 7d72858e..322db709 100644 --- a/app/src/main/java/io/xpipe/app/core/AppGreetings.java +++ b/app/src/main/java/io/xpipe/app/core/AppGreetings.java @@ -66,7 +66,7 @@ public class AppGreetings { } public static void showIfNeeded() { - //TODO + // TODO if (!AppProperties.get().isImage() || true) { return; } @@ -113,6 +113,7 @@ public class AppGreetings { var button = alert.getDialogPane().lookupButton(buttonType); button.disableProperty().bind(accepted.not()); }, + null, r -> r.filter(b -> b.getButtonData().isDefaultButton() && accepted.get()) .ifPresentOrElse( t -> { diff --git a/app/src/main/java/io/xpipe/app/core/AppI18n.java b/app/src/main/java/io/xpipe/app/core/AppI18n.java index 331293b7..47b0750c 100644 --- a/app/src/main/java/io/xpipe/app/core/AppI18n.java +++ b/app/src/main/java/io/xpipe/app/core/AppI18n.java @@ -7,6 +7,7 @@ import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.SupportedLocale; import io.xpipe.app.util.DynamicOptionsBuilder; +import io.xpipe.app.util.OptionsBuilder; import io.xpipe.app.util.Translatable; import io.xpipe.core.util.ModuleHelper; import javafx.beans.binding.Bindings; @@ -131,6 +132,7 @@ private static AppI18n INSTANCE = new AppI18n(); || caller.equals(FancyTooltipAugment.class) || caller.equals(PrefsChoiceValue.class) || caller.equals(Translatable.class) + || caller.equals(OptionsBuilder.class) || caller.equals(DynamicOptionsBuilder.class)) { continue; } diff --git a/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java b/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java index b9efd2f8..6273a01d 100644 --- a/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java +++ b/app/src/main/java/io/xpipe/app/core/AppWindowHelper.java @@ -72,7 +72,7 @@ public class AppWindowHelper { childStage.setY(stage.getY() + stage.getHeight() / 2 - childStage.getHeight() / 2); } - public static void showAlert(Consumer c, Consumer> bt) { + public static void showAlert(Consumer c, ObservableValue loading, Consumer> bt) { ThreadHelper.runAsync(() -> { var r = showBlockingAlert(c); if (bt != null) { diff --git a/app/src/main/java/io/xpipe/app/ext/ScanProvider.java b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java new file mode 100644 index 00000000..97f0dd8e --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/ScanProvider.java @@ -0,0 +1,49 @@ +package io.xpipe.app.ext; + +import io.xpipe.core.store.DataStore; +import io.xpipe.core.util.ModuleLayerLoader; +import lombok.Value; +import org.apache.commons.lang3.function.FailableRunnable; + +import java.util.Comparator; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Collectors; + +public abstract class ScanProvider { + + @Value + public static class ScanOperation { + String nameKey; + FailableRunnable scanner; + } + + private static List ALL; + + public static class Loader implements ModuleLayerLoader { + + @Override + public void init(ModuleLayer layer) { + ALL = ServiceLoader.load(layer, ScanProvider.class).stream() + .map(ServiceLoader.Provider::get) + .sorted(Comparator.comparing(scanProvider -> scanProvider.getClass().getName())) + .collect(Collectors.toList()); + } + + @Override + public boolean requiresFullDaemon() { + return true; + } + + @Override + public boolean prioritizeLoading() { + return false; + } + } + + public static List getAll() { + return ALL; + } + + public abstract ScanOperation create(DataStore store); +} diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java new file mode 100644 index 00000000..33505638 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java @@ -0,0 +1,163 @@ +package io.xpipe.app.fxcomps.impl; + +import atlantafx.base.controls.Popover; +import atlantafx.base.controls.Spacer; +import io.xpipe.app.core.AppFont; +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.CompStructure; +import io.xpipe.app.fxcomps.SimpleCompStructure; +import io.xpipe.app.fxcomps.util.PlatformThread; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Orientation; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.layout.*; + +import java.util.ArrayList; +import java.util.List; + +public class OptionsComp extends Comp> { + + private final List entries; + + public OptionsComp(List entries) { + this.entries = entries; + } + + public OptionsComp.Entry queryEntry(String key) { + return entries.stream() + .filter(entry -> entry.key != null && entry.key.equals(key)) + .findAny() + .orElseThrow(); + } + + @Override + public CompStructure createBase() { + Pane pane; + var content = new VBox(); + content.setSpacing(7); + pane = content; + pane.getStyleClass().add("options-comp"); + + var nameRegions = new ArrayList(); + var compRegions = new ArrayList(); + + for (var entry : getEntries()) { + Region compRegion = null; + if (entry.comp() != null) { + compRegion = entry.comp().createRegion(); + } + + if (entry.name() != null && entry.description() != null) { + var line = new VBox(); + line.prefWidthProperty().bind(pane.widthProperty()); + line.setSpacing(5); + + var name = new Label(); + name.getStyleClass().add("name"); + name.textProperty().bind(entry.name()); + name.setMinWidth(Region.USE_PREF_SIZE); + name.setAlignment(Pos.CENTER_LEFT); + if (compRegion != null) { + name.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty())); + name.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty())); + } + line.getChildren().add(name); + + var description = new Label(); + description.setWrapText(true); + description.getStyleClass().add("description"); + description.textProperty().bind(entry.description()); + description.setAlignment(Pos.CENTER_LEFT); + if (compRegion != null) { + description.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty())); + description.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty())); + } + + if (entry.longDescription() != null) { + var popover = new Popover(new Label(entry.longDescription().getValue())); + popover.setCloseButtonEnabled(false); + popover.setHeaderAlwaysVisible(false); + popover.setDetachable(true); + AppFont.small(popover.getContentNode()); + + var descriptionHover = new Label("?"); + AppFont.header(descriptionHover); + descriptionHover.setOnMouseClicked(e -> popover.show(descriptionHover)); + + var descriptionBox = new HBox(description, new Spacer(Orientation.HORIZONTAL), descriptionHover); + HBox.setHgrow(descriptionBox, Priority.ALWAYS); + descriptionBox.setAlignment(Pos.CENTER_LEFT); + line.getChildren().add(descriptionBox); + }else { + line.getChildren().add(description); + } + + if (compRegion != null) { + line.getChildren().add(compRegion); + } + + pane.getChildren().add(line); + } + + else if (entry.name() != null) { + var line = new HBox(); + line.setFillHeight(true); + line.prefWidthProperty().bind(pane.widthProperty()); + line.setSpacing(8); + + var name = new Label(); + name.textProperty().bind(entry.name()); + name.prefHeightProperty().bind(line.heightProperty()); + name.setMinWidth(Region.USE_PREF_SIZE); + name.setAlignment(Pos.CENTER_LEFT); + if (compRegion != null) { + name.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty())); + name.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty())); + } + nameRegions.add(name); + line.getChildren().add(name); + + if (compRegion != null) { + compRegions.add(compRegion); + line.getChildren().add(compRegion); + HBox.setHgrow(compRegion, Priority.ALWAYS); + } + + pane.getChildren().add(line); + } else { + if (compRegion != null) { + compRegions.add(compRegion); + pane.getChildren().add(compRegion); + } + } + } + + if (entries.stream().anyMatch(entry -> entry.name() != null && entry.description() == null)) { + var nameWidthBinding = Bindings.createDoubleBinding( + () -> { + if (nameRegions.stream().anyMatch(r -> r.getWidth() == 0)) { + return Region.USE_COMPUTED_SIZE; + } + + var m = nameRegions.stream() + .map(Region::getWidth) + .max(Double::compareTo) + .orElse(0.0); + return m; + }, + nameRegions.stream().map(Region::widthProperty).toList().toArray(new Observable[0])); + nameRegions.forEach(r -> r.prefWidthProperty().bind(nameWidthBinding)); + } + + return new SimpleCompStructure<>(pane); + } + + public List getEntries() { + return entries; + } + + public record Entry(String key, ObservableValue description, ObservableValue longDescription, ObservableValue name, Comp comp) {} +} diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java index 92b34dde..34860f67 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java @@ -4,7 +4,7 @@ import io.xpipe.app.ext.PrefsChoiceValue; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import java.nio.file.Files; import java.nio.file.Path; @@ -37,7 +37,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue { } protected Optional getApplicationPath() { - try (ShellProcessControl pc = LocalStore.getShell().start()) { + try (ShellControl pc = LocalStore.getShell().start()) { try (var c = pc.command(String.format( "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister " + "-dump | grep -o \"/.*%s.app\" | grep -v -E \"Caches|TimeMachine|Temporary|/Volumes/%s\" | uniq", @@ -76,7 +76,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue { } public boolean isAvailable() { - try (ShellProcessControl pc = LocalStore.getShell()) { + try (ShellControl pc = LocalStore.getShell()) { return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable)); } catch (Exception e) { ErrorEvent.fromThrowable(e).omit().handle(); diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java index 34ced2e2..d2897dc6 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java @@ -6,7 +6,7 @@ import io.xpipe.app.util.ApplicationHelper; import io.xpipe.app.util.MacOsPermissions; import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import java.util.List; @@ -133,7 +133,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue { @Override public void launch(String name, String command) throws Exception { - try (ShellProcessControl pc = LocalStore.getShell()) { + try (ShellControl pc = LocalStore.getShell()) { var suffix = command.equals(pc.getShellDialect().getNormalOpenCommand()) ? "\"\"" : "\"" + command.replaceAll("\"", "\\\\\"") + "\""; @@ -187,7 +187,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue { @Override public void launch(String name, String command) throws Exception { - try (ShellProcessControl pc = LocalStore.getShell()) { + try (ShellControl pc = LocalStore.getShell()) { var cmd = String.format( """ osascript - "$@" < entry.simpleRefresh()); + DataStoreProviders.getAll().forEach(dataStoreProvider -> { try { dataStoreProvider.storageInit(); @@ -115,6 +117,21 @@ public abstract class DataStorage { return createUniqueSourceEntryName(col, typeName); } + private String createUniqueStoreEntryName(String base) { + if (DataStorage.get().getStoreIfPresent(base).isEmpty()) { + return base; + } + + int counter = 1; + while (true) { + var name = base + counter; + if (DataStorage.get().getStoreIfPresent(name).isEmpty()) { + return name; + } + counter++; + } + } + private String createUniqueSourceEntryName(DataSourceCollection col, String base) { base = DataSourceId.cleanString(base); var id = DataSourceId.create(col != null ? col.getName() : null, base); @@ -345,7 +362,7 @@ public abstract class DataStorage { } public DataStoreEntry addStore(@NonNull String name, DataStore store) { - var e = DataStoreEntry.createNew(UUID.randomUUID(), name, store); + var e = DataStoreEntry.createNew(UUID.randomUUID(), createUniqueStoreEntryName(name), store); addStore(e); return e; } diff --git a/app/src/main/java/io/xpipe/app/update/AppInstaller.java b/app/src/main/java/io/xpipe/app/update/AppInstaller.java index 2ae9d00c..63211905 100644 --- a/app/src/main/java/io/xpipe/app/update/AppInstaller.java +++ b/app/src/main/java/io/xpipe/app/update/AppInstaller.java @@ -6,9 +6,9 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import io.xpipe.app.util.ScriptHelper; import io.xpipe.app.util.TerminalHelper; import io.xpipe.core.impl.FileNames; -import io.xpipe.core.process.CommandProcessControl; +import io.xpipe.core.process.CommandControl; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellDialects; import io.xpipe.core.store.ShellStore; import io.xpipe.core.util.XPipeInstallation; @@ -22,7 +22,7 @@ import java.util.List; public class AppInstaller { - public static void installOnRemoteMachine(ShellProcessControl s, String version) throws Exception { + public static void installOnRemoteMachine(ShellControl s, String version) throws Exception { var asset = getSuitablePlatformAsset(s); var file = AppDownloads.downloadInstaller(asset, version); installFile(s, asset, file); @@ -32,14 +32,14 @@ public class AppInstaller { asset.installLocal(localFile.toString()); } - public static void installFile(ShellProcessControl s, InstallerAssetType asset, Path localFile) throws Exception { + public static void installFile(ShellControl s, InstallerAssetType asset, Path localFile) throws Exception { String targetFile = null; if (s.isLocal()) { targetFile = localFile.toString(); } else { targetFile = FileNames.join( s.getTemporaryDirectory(), localFile.getFileName().toString()); - try (CommandProcessControl c = s.getShellDialect().getStreamFileWriteCommand(s, targetFile) + try (CommandControl c = s.getShellDialect().getStreamFileWriteCommand(s, targetFile) .start()) { c.discardOut(); c.discardErr(); @@ -73,13 +73,13 @@ public class AppInstaller { throw new AssertionError(); } - public static InstallerAssetType getSuitablePlatformAsset(ShellProcessControl p) throws Exception { + public static InstallerAssetType getSuitablePlatformAsset(ShellControl p) throws Exception { if (p.getOsType().equals(OsType.WINDOWS)) { return new InstallerAssetType.Msi(); } if (p.getOsType().equals(OsType.LINUX)) { - try (CommandProcessControl c = p.command(p.getShellDialect().getFileExistsCommand("/etc/debian_version")) + try (CommandControl c = p.command(p.getShellDialect().getFileExistsCommand("/etc/debian_version")) .start()) { return c.discardAndCheckExit() ? new InstallerAssetType.Debian() : new InstallerAssetType.Rpm(); } @@ -102,7 +102,7 @@ public class AppInstaller { }) public abstract static class InstallerAssetType { - public abstract void installRemote(ShellProcessControl pc, String file) throws Exception; + public abstract void installRemote(ShellControl pc, String file) throws Exception; public abstract void installLocal(String file) throws Exception; @@ -121,11 +121,11 @@ public class AppInstaller { } @Override - public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception { + public void installRemote(ShellControl shellControl, String file) throws Exception { var exec = XPipeInstallation.getInstallationExecutable( - shellProcessControl, - XPipeInstallation.getDefaultInstallationBasePath(shellProcessControl, false)); - var logsDir = FileNames.join(XPipeInstallation.getDataBasePath(shellProcessControl), "logs"); + shellControl, + XPipeInstallation.getDefaultInstallationBasePath(shellControl, false)); + var logsDir = FileNames.join(XPipeInstallation.getDataBasePath(shellControl), "logs"); var cmd = new ArrayList<>(java.util.List.of( "start", "/wait", @@ -139,7 +139,7 @@ public class AppInstaller { exec // "/qf" )); - try (CommandProcessControl c = shellProcessControl.command(cmd).start()) { + try (CommandControl c = shellControl.command(cmd).start()) { c.discardOrThrow(); } } @@ -177,14 +177,14 @@ public class AppInstaller { } @Override - public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception { - try (var pc = shellProcessControl.subShell(ShellDialects.BASH).start()) { - try (CommandProcessControl c = pc.command("DEBIAN_FRONTEND=noninteractive apt-get remove -qy xpipe") + public void installRemote(ShellControl shellControl, String file) throws Exception { + try (var pc = shellControl.subShell(ShellDialects.BASH).start()) { + try (CommandControl c = pc.command("DEBIAN_FRONTEND=noninteractive apt-get remove -qy xpipe") .elevated() .start()) { c.discardOrThrow(); } - try (CommandProcessControl c = pc.command( + try (CommandControl c = pc.command( "DEBIAN_FRONTEND=noninteractive apt-get install -qy \"" + file + "\"") .elevated() .start()) { @@ -214,9 +214,9 @@ public class AppInstaller { } @Override - public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception { - try (var pc = shellProcessControl.subShell(ShellDialects.BASH).start()) { - try (CommandProcessControl c = pc.command("rpm -U -v --force \"" + file + "\"") + public void installRemote(ShellControl shellControl, String file) throws Exception { + try (var pc = shellControl.subShell(ShellDialects.BASH).start()) { + try (CommandControl c = pc.command("rpm -U -v --force \"" + file + "\"") .elevated() .start()) { c.discardOrThrow(); @@ -244,9 +244,9 @@ public class AppInstaller { } @Override - public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception { - try (var pc = shellProcessControl.subShell(ShellDialects.BASH).start()) { - try (CommandProcessControl c = pc.command( + public void installRemote(ShellControl shellControl, String file) throws Exception { + try (var pc = shellControl.subShell(ShellDialects.BASH).start()) { + try (CommandControl c = pc.command( "installer -verboseR -allowUntrusted -pkg \"" + file + "\" -target /") .elevated() .start()) { diff --git a/app/src/main/java/io/xpipe/app/update/UpdateChangelogAlert.java b/app/src/main/java/io/xpipe/app/update/UpdateChangelogAlert.java index 64403a53..2f288c9e 100644 --- a/app/src/main/java/io/xpipe/app/update/UpdateChangelogAlert.java +++ b/app/src/main/java/io/xpipe/app/update/UpdateChangelogAlert.java @@ -38,7 +38,7 @@ public class UpdateChangelogAlert { alert.getDialogPane().setContent(markdown); alert.getButtonTypes().add(new ButtonType(AppI18n.get("gotIt"), ButtonBar.ButtonData.OK_DONE)); - }, + },null, r -> r.filter(b -> b.getButtonData().isDefaultButton()).ifPresent(t -> {})); } } diff --git a/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java b/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java index da674890..18339d00 100644 --- a/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java @@ -3,7 +3,7 @@ package io.xpipe.app.util; import io.xpipe.app.issue.TrackEvent; import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.ShellDialects; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import java.io.IOException; import java.util.List; @@ -30,12 +30,12 @@ public class ApplicationHelper { } } - public static boolean isInPath(ShellProcessControl processControl, String executable) throws Exception { + public static boolean isInPath(ShellControl processControl, String executable) throws Exception { return processControl.executeBooleanSimpleCommand( processControl.getShellDialect().getWhichCommand(executable)); } - public static void checkSupport(ShellProcessControl processControl, String executable, String displayName) + public static void checkSupport(ShellControl processControl, String executable, String displayName) throws Exception { if (!isInPath(processControl, executable)) { throw new IOException(displayName + " executable " + executable + " not found in PATH"); diff --git a/app/src/main/java/io/xpipe/app/util/MacOsPermissions.java b/app/src/main/java/io/xpipe/app/util/MacOsPermissions.java index f109545d..8ca92809 100644 --- a/app/src/main/java/io/xpipe/app/util/MacOsPermissions.java +++ b/app/src/main/java/io/xpipe/app/util/MacOsPermissions.java @@ -38,7 +38,7 @@ public class MacOsPermissions { a.getDialogPane().setContent(AppWindowHelper.alertContentText(AppI18n.get("permissionsAlertTitleContent"))); a.getButtonTypes().clear(); alert.set(a); - }, buttonType -> { + }, null, buttonType -> { alert.get().close(); if (buttonType.isEmpty() || !buttonType.get().getButtonData().isDefaultButton()) { state.set(false); diff --git a/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java b/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java new file mode 100644 index 00000000..0bbb55bb --- /dev/null +++ b/app/src/main/java/io/xpipe/app/util/OptionsBuilder.java @@ -0,0 +1,166 @@ +package io.xpipe.app.util; + +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.fxcomps.Comp; +import io.xpipe.app.fxcomps.impl.*; +import io.xpipe.core.util.SecretValue; +import javafx.beans.property.Property; +import javafx.beans.value.ObservableValue; +import javafx.scene.control.Label; +import javafx.scene.layout.Region; +import net.synedra.validatorfx.Check; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class OptionsBuilder { + + private final List entries = new ArrayList<>(); + private final List> props = new ArrayList<>(); + + private ObservableValue name; + private ObservableValue description; + private ObservableValue longDescription; + private Comp comp; + + private void finishCurrent() { + if (comp == null) { + return; + } + + var entry = new OptionsComp.Entry(null, description, longDescription, name, comp); + description = null; + longDescription = null; + name = null; + comp = null; + entries.add(entry); + } + + public OptionsBuilder addTitle(String titleKey) { + finishCurrent(); + entries.add(new OptionsComp.Entry( + titleKey, null, null, null, new LabelComp(AppI18n.observable(titleKey)).styleClass("title-header"))); + return this; + } + + public OptionsBuilder addTitle(ObservableValue title) { + finishCurrent(); + entries.add(new OptionsComp.Entry( + null, null, null, null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header"))); + return this; + } + + public OptionsBuilder decorate(Check c) { + comp.apply(s -> c.decorates(s.get())); + return this; + } + + public OptionsBuilder nonNull(Validator v) { + var e = name; + var p = props.get(props.size() - 1); + return decorate(Validator.nonNull(v, e, p)); + } + + private void pushComp(Comp comp) { + finishCurrent(); + this.comp = comp; + } + + public OptionsBuilder stringArea(Property prop, boolean lazy) { + var comp = new TextAreaComp(prop, lazy); + pushComp(comp); + props.add(prop); + return this; + } + + public OptionsBuilder addInteger(Property prop) { + var comp = new IntFieldComp(prop); + pushComp(comp); + props.add(prop); + return this; + } + + public OptionsBuilder addString(Property prop) { + return addString(prop, false); + } + public OptionsBuilder addString(Property prop, boolean lazy) { + var comp = new TextFieldComp(prop, lazy); + pushComp(comp); + props.add(prop); + return this; + } + + + public OptionsBuilder name(String nameKey) { + finishCurrent(); + name = AppI18n.observable(nameKey); + return this; + } + + public OptionsBuilder description(String descriptionKey) { + finishCurrent(); + description = AppI18n.observable(descriptionKey); + return this; + } + + public OptionsBuilder longDescription(String descriptionKey) { + finishCurrent(); + longDescription = AppI18n.observable(descriptionKey); + return this; + } + + public OptionsBuilder addComp(Comp comp) { + pushComp(comp); + return this; + } + + public OptionsBuilder addComp(Comp comp, Property prop) { + pushComp(comp); + props.add(prop); + return this; + } + + public OptionsBuilder addSecret(Property prop) { + var comp = new SecretFieldComp(prop); + pushComp(comp); + props.add(prop); + return this; + } + + @SafeVarargs + public final OptionsBuilder bind(Supplier creator, Property... toSet) { + props.forEach(prop -> { + prop.addListener((c, o, n) -> { + for (Property p : toSet) { + p.setValue(creator.get()); + } + }); + }); + for (Property p : toSet) { + p.setValue(creator.get()); + } + return this; + } + + public final OptionsBuilder bindChoice( + Supplier> creator, Property toSet) { + props.forEach(prop -> { + prop.addListener((c, o, n) -> { + toSet.unbind(); + toSet.bind(creator.get()); + }); + }); + toSet.bind(creator.get()); + return this; + } + + public OptionsComp buildComp() { + finishCurrent(); + return new OptionsComp(entries); + } + + public Region build() { + return buildComp().createRegion(); + } +} diff --git a/app/src/main/java/io/xpipe/app/util/ProxyManagerProviderImpl.java b/app/src/main/java/io/xpipe/app/util/ProxyManagerProviderImpl.java index f8c787aa..573d8344 100644 --- a/app/src/main/java/io/xpipe/app/util/ProxyManagerProviderImpl.java +++ b/app/src/main/java/io/xpipe/app/util/ProxyManagerProviderImpl.java @@ -7,7 +7,7 @@ import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.update.AppDownloads; import io.xpipe.app.update.AppInstaller; import io.xpipe.core.impl.FileNames; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import io.xpipe.core.util.ModuleHelper; import io.xpipe.core.util.ProxyManagerProvider; import io.xpipe.core.util.XPipeInstallation; @@ -31,7 +31,7 @@ public class ProxyManagerProviderImpl extends ProxyManagerProvider { } @Override - public Optional checkCompatibility(ShellProcessControl s) throws Exception { + public Optional checkCompatibility(ShellControl s) throws Exception { var version = ModuleHelper.isImage() ? AppProperties.get().getVersion() : AppDownloads.getLatestVersion(); if (AppPrefs.get().developerDisableConnectorInstallationVersionCheck().get()) { @@ -54,7 +54,7 @@ public class ProxyManagerProviderImpl extends ProxyManagerProvider { } @Override - public boolean setup(ShellProcessControl s) throws Exception { + public boolean setup(ShellControl s) throws Exception { var message = checkCompatibility(s); if (message.isPresent()) { if (showAlert()) { diff --git a/app/src/main/java/io/xpipe/app/util/ScanAlert.java b/app/src/main/java/io/xpipe/app/util/ScanAlert.java new file mode 100644 index 00000000..50841e3b --- /dev/null +++ b/app/src/main/java/io/xpipe/app/util/ScanAlert.java @@ -0,0 +1,64 @@ +package io.xpipe.app.util; + +import io.xpipe.app.comp.base.ListSelectorComp; +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppWindowHelper; +import io.xpipe.app.ext.ScanProvider; +import io.xpipe.app.fxcomps.impl.LabelComp; +import io.xpipe.app.fxcomps.impl.VerticalComp; +import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.core.store.DataStore; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; + +import java.util.ArrayList; +import java.util.List; + +public class ScanAlert { + + public static void showIfNeeded(DataStore store) { + var providers = ScanProvider.getAll(); + var applicable = providers.stream() + .map(scanProvider -> scanProvider.create(store)) + .filter(scanOperation -> scanOperation != null) + .toList(); + if (applicable.size() == 0) { + return; + } + + var selected = new SimpleListProperty( + FXCollections.observableList(new ArrayList<>(applicable))); + var busy = new SimpleBooleanProperty(); + AppWindowHelper.showAlert( + alert -> { + alert.setAlertType(Alert.AlertType.NONE); + alert.setTitle(AppI18n.get("scanAlertTitle")); + alert.setWidth(300); + var content = new VerticalComp(List.of( + new LabelComp(AppI18n.get("scanAlertHeader")).apply(struc -> struc.get().setWrapText(true)), + new ListSelectorComp<>( + applicable, scanOperation -> AppI18n.get(scanOperation.getNameKey()), selected))) + .apply(struc -> struc.get().setSpacing(15)) + .styleClass("window-content") + .createRegion(); + alert.getButtonTypes().add(ButtonType.OK); + alert.getDialogPane().setContent(content); + }, + busy, + buttonType -> { + if (buttonType.isPresent() + && buttonType.get().getButtonData().isDefaultButton()) { + try (var ignored = new BusyProperty(busy)) { + for (var a : applicable) { + a.getScanner().run(); + } + } catch (Exception ex) { + ErrorEvent.fromThrowable(ex).handle(); + } + } + }); + } +} diff --git a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java index f64d17d5..417ce0e0 100644 --- a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java @@ -6,7 +6,7 @@ import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialects; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import io.xpipe.core.util.SecretValue; import lombok.SneakyThrows; @@ -15,7 +15,7 @@ import java.util.Random; public class ScriptHelper { - public static String createDefaultOpenCommand(ShellProcessControl pc, String file) { + public static String createDefaultOpenCommand(ShellControl pc, String file) { if (pc.getOsType().equals(OsType.WINDOWS)) { return "\"" + file + "\""; } else if (pc.getOsType().equals(OsType.LINUX)){ @@ -25,7 +25,7 @@ public class ScriptHelper { } } - public static String createDetachCommand(ShellProcessControl pc, String command) { + public static String createDetachCommand(ShellControl pc, String command) { if (pc.getOsType().equals(OsType.WINDOWS)) { return "start \"\" " + command; } else { @@ -123,7 +123,7 @@ public class ScriptHelper { } public static String constructOpenWithInitScriptCommand( - ShellProcessControl processControl, List init, String toExecuteInShell) { + ShellControl processControl, List init, String toExecuteInShell) { ShellDialect t = processControl.getShellDialect(); if (init.size() == 0 && toExecuteInShell == null) { return t.getNormalOpenCommand(); @@ -168,12 +168,12 @@ public class ScriptHelper { } @SneakyThrows - public static String getExecScriptFile(ShellProcessControl processControl) { + public static String getExecScriptFile(ShellControl processControl) { return getExecScriptFile(processControl, processControl.getShellDialect().getScriptFileEnding()); } @SneakyThrows - public static String getExecScriptFile(ShellProcessControl processControl, String fileEnding) { + public static String getExecScriptFile(ShellControl processControl, String fileEnding) { var fileName = "exec-" + getScriptId(); var temp = processControl.getTemporaryDirectory(); var file = FileNames.join(temp, fileName + "." + fileEnding); @@ -181,7 +181,7 @@ public class ScriptHelper { } @SneakyThrows - public static String createExecScript(ShellProcessControl processControl, String content) { + public static String createExecScript(ShellControl processControl, String content) { var fileName = "exec-" + getScriptId(); ShellDialect type = processControl.getShellDialect(); var temp = processControl.getTemporaryDirectory(); @@ -190,7 +190,7 @@ public class ScriptHelper { } @SneakyThrows - private static String createExecScript(ShellProcessControl processControl, String file, String content) { + private static String createExecScript(ShellControl processControl, String file, String content) { ShellDialect type = processControl.getShellDialect(); content = type.prepareScriptContent(content); @@ -207,7 +207,7 @@ public class ScriptHelper { } @SneakyThrows - public static String createAskPassScript(SecretValue pass, ShellProcessControl parent, ShellDialect type) { + public static String createAskPassScript(SecretValue pass, ShellControl parent, ShellDialect type) { var content = type.getScriptEchoCommand(pass.getSecretValue()); var temp = parent.getTemporaryDirectory(); var file = FileNames.join(temp, "askpass-" + getScriptId() + "." + type.getScriptFileEnding()); diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java index fe9e3dce..0e20024c 100644 --- a/app/src/main/java/module-info.java +++ b/app/src/main/java/module-info.java @@ -117,11 +117,13 @@ open module io.xpipe.app { uses XPipeDaemon; uses ProxyFunction; uses ModuleLayerLoader; + uses ScanProvider; provides ModuleLayerLoader with DataSourceTarget.Loader, ActionProvider.Loader, - PrefsProvider.Loader; + PrefsProvider.Loader, + ScanProvider.Loader; provides DataStateProvider with DataStateProviderImpl; provides ProxyManagerProvider with diff --git a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties index f68129fe..ee83c685 100644 --- a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties +++ b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties @@ -10,7 +10,11 @@ mustNotBeEmpty=$NAME$ must not be empty null=$VALUE$ must be not null hostFeatureUnsupported=Host does not support the feature $FEATURE$ missingStore=$NAME$ does not exist +connectionName=Connection name +connectionNameDescription=Give this connection a custom name unknown=Unknown +scanAlertTitle=Connection detection +scanAlertHeader=Select types of connections you want to automatically detect on the host system: namedHostFeatureUnsupported=$HOST$ does not support this feature namedHostNotActive=$HOST$ is not active noInformationAvailable=No information available diff --git a/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css index 7debeb66..1a6d8de9 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/section-comp.css @@ -10,4 +10,13 @@ .choice-pane-comp { -fx-spacing: 7px; +} + +.options-comp .name { +-fx-padding: 9px 0 0 0; +-fx-font-size: 1.1em; +} + +.options-comp .description { +-fx-opacity: 0.75; } \ No newline at end of file diff --git a/core/src/main/java/io/xpipe/core/impl/LocalStore.java b/core/src/main/java/io/xpipe/core/impl/LocalStore.java index 117177ba..dfcc3bc5 100644 --- a/core/src/main/java/io/xpipe/core/impl/LocalStore.java +++ b/core/src/main/java/io/xpipe/core/impl/LocalStore.java @@ -3,7 +3,7 @@ package io.xpipe.core.impl; import com.fasterxml.jackson.annotation.JsonTypeName; import io.xpipe.core.process.ProcessControlProvider; import io.xpipe.core.process.ShellDialects; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import io.xpipe.core.store.ConnectionFileSystem; import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystemStore; @@ -15,12 +15,12 @@ import java.nio.file.Path; @JsonTypeName("local") public class LocalStore extends JacksonizedValue implements ShellStore { - private static ShellProcessControl local; + private static ShellControl local; private static FileSystem localFileSystem; - public static ShellProcessControl getShell() throws Exception { + public static ShellControl getShell() throws Exception { if (local == null) { - local = new LocalStore().create().start(); + local = ProcessControlProvider.createLocal(false).start(); } return local; @@ -128,7 +128,7 @@ public class LocalStore extends JacksonizedValue implements ShellStore { } @Override - public ShellProcessControl createControl() { - return ProcessControlProvider.createLocal(); + public ShellControl createControl() { + return ProcessControlProvider.createLocal(true); } } diff --git a/core/src/main/java/io/xpipe/core/process/CommandProcessControl.java b/core/src/main/java/io/xpipe/core/process/CommandControl.java similarity index 65% rename from core/src/main/java/io/xpipe/core/process/CommandProcessControl.java rename to core/src/main/java/io/xpipe/core/process/CommandControl.java index f5796689..2c00d948 100644 --- a/core/src/main/java/io/xpipe/core/process/CommandProcessControl.java +++ b/core/src/main/java/io/xpipe/core/process/CommandControl.java @@ -1,6 +1,7 @@ package io.xpipe.core.process; import io.xpipe.core.charsetter.Charsetter; +import io.xpipe.core.util.FailableFunction; import java.io.InputStream; import java.io.InputStreamReader; @@ -8,18 +9,18 @@ import java.io.OutputStream; import java.nio.charset.Charset; import java.util.function.Consumer; -public interface CommandProcessControl extends ProcessControl { +public interface CommandControl extends ProcessControl { - public CommandProcessControl doesNotObeyReturnValueConvention(); + public CommandControl doesNotObeyReturnValueConvention(); @Override - public CommandProcessControl sensitive(); + public CommandControl sensitive(); - CommandProcessControl complex(); + CommandControl complex(); - CommandProcessControl workingDirectory(String directory); + CommandControl workingDirectory(String directory); - ShellProcessControl getParent(); + ShellControl getParent(); InputStream startExternalStdout() throws Exception; @@ -27,16 +28,20 @@ public interface CommandProcessControl extends ProcessControl { public boolean waitFor(); - CommandProcessControl customCharset(Charset charset); + CommandControl customCharset(Charset charset); int getExitCode(); - CommandProcessControl elevated(); + default CommandControl elevated() { + return elevated((v) -> true); + } + + CommandControl elevated(FailableFunction elevationFunction); @Override - CommandProcessControl start() throws Exception; + CommandControl start() throws Exception; - CommandProcessControl exitTimeout(Integer timeout); + CommandControl exitTimeout(Integer timeout); public void withStdoutOrThrow(Charsetter.FailableConsumer c) throws Exception; String readOnlyStdout() throws Exception; diff --git a/core/src/main/java/io/xpipe/core/process/OsType.java b/core/src/main/java/io/xpipe/core/process/OsType.java index 9f61d0b1..33efe059 100644 --- a/core/src/main/java/io/xpipe/core/process/OsType.java +++ b/core/src/main/java/io/xpipe/core/process/OsType.java @@ -22,25 +22,32 @@ public interface OsType { } } + String getHomeDirectory(ShellControl pc) throws Exception; + String getName(); - String getTempDirectory(ShellProcessControl pc) throws Exception; + String getTempDirectory(ShellControl pc) throws Exception; String normalizeFileName(String file); - Map getProperties(ShellProcessControl pc) throws Exception; + Map getProperties(ShellControl pc) throws Exception; - String determineOperatingSystemName(ShellProcessControl pc) throws Exception; + String determineOperatingSystemName(ShellControl pc) throws Exception; static class Windows implements OsType { + @Override + public String getHomeDirectory(ShellControl pc) throws Exception { + return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("USERPROFILE")); + } + @Override public String getName() { return "Windows"; } @Override - public String getTempDirectory(ShellProcessControl pc) throws Exception { + public String getTempDirectory(ShellControl pc) throws Exception { return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("TEMP")); } @@ -50,15 +57,15 @@ public interface OsType { } @Override - public Map getProperties(ShellProcessControl pc) throws Exception { - try (CommandProcessControl c = pc.command("systeminfo").start()) { + public Map getProperties(ShellControl pc) throws Exception { + try (CommandControl c = pc.command("systeminfo").start()) { var text = c.readOrThrow(); return PropertiesFormatsParser.parse(text, ":"); } } @Override - public String determineOperatingSystemName(ShellProcessControl pc) throws Exception { + public String determineOperatingSystemName(ShellControl pc) throws Exception { var properties = getProperties(pc); return properties.get("OS Name") + " " + properties.get("OS Version").split(" ")[0]; @@ -68,7 +75,12 @@ public interface OsType { static class Linux implements OsType { @Override - public String getTempDirectory(ShellProcessControl pc) throws Exception { + public String getHomeDirectory(ShellControl pc) throws Exception { + return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME")); + } + + @Override + public String getTempDirectory(ShellControl pc) throws Exception { return "/tmp/"; } @@ -83,20 +95,20 @@ public interface OsType { } @Override - public Map getProperties(ShellProcessControl pc) throws Exception { + public Map getProperties(ShellControl pc) throws Exception { return null; } @Override - public String determineOperatingSystemName(ShellProcessControl pc) throws Exception { - try (CommandProcessControl c = pc.command("lsb_release -a").start()) { + public String determineOperatingSystemName(ShellControl pc) throws Exception { + try (CommandControl c = pc.command("lsb_release -a").start()) { var text = c.readOnlyStdout(); if (c.getExitCode() == 0) { return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", null); } } - try (CommandProcessControl c = pc.command("cat /etc/*release").start()) { + try (CommandControl c = pc.command("cat /etc/*release").start()) { var text = c.readOnlyStdout(); if (c.getExitCode() == 0) { return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", null); @@ -104,7 +116,7 @@ public interface OsType { } String type = "Unknown"; - try (CommandProcessControl c = pc.command("uname -o").start()) { + try (CommandControl c = pc.command("uname -o").start()) { var text = c.readOnlyStdout(); if (c.getExitCode() == 0) { type = text.strip(); @@ -112,7 +124,7 @@ public interface OsType { } String version = "?"; - try (CommandProcessControl c = pc.command("uname -r").start()) { + try (CommandControl c = pc.command("uname -r").start()) { var text = c.readOnlyStdout(); if (c.getExitCode() == 0) { version = text.strip(); @@ -126,7 +138,12 @@ public interface OsType { static class MacOs implements OsType { @Override - public String getTempDirectory(ShellProcessControl pc) throws Exception { + public String getHomeDirectory(ShellControl pc) throws Exception { + return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME")); + } + + @Override + public String getTempDirectory(ShellControl pc) throws Exception { return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintVariableCommand("TMPDIR")); } @@ -141,8 +158,8 @@ public interface OsType { } @Override - public Map getProperties(ShellProcessControl pc) throws Exception { - try (CommandProcessControl c = + public Map getProperties(ShellControl pc) throws Exception { + try (CommandControl c = pc.subShell(ShellDialects.BASH).command("sw_vers").start()) { var text = c.readOrThrow(); return PropertiesFormatsParser.parse(text, ":"); @@ -150,7 +167,7 @@ public interface OsType { } @Override - public String determineOperatingSystemName(ShellProcessControl pc) throws Exception { + public String determineOperatingSystemName(ShellControl pc) throws Exception { var properties = getProperties(pc); var name = pc.executeStringSimpleCommand( "awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup " diff --git a/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java b/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java index fddc67e1..b85a465a 100644 --- a/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java +++ b/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java @@ -18,18 +18,18 @@ public abstract class ProcessControlProvider { .toList(); } - public static ShellProcessControl createLocal() { + public static ShellControl createLocal(boolean stoppable) { return INSTANCES.stream() - .map(localProcessControlProvider -> localProcessControlProvider.createLocalProcessControl()) + .map(localProcessControlProvider -> localProcessControlProvider.createLocalProcessControl(stoppable)) .filter(Objects::nonNull) .findFirst() .orElseThrow(); } - public static ShellProcessControl createSub( - ShellProcessControl parent, - @NonNull FailableFunction commandFunction, - FailableBiFunction terminalCommand) { + public static ShellControl createSub( + ShellControl parent, + @NonNull FailableFunction commandFunction, + FailableBiFunction terminalCommand) { return INSTANCES.stream() .map(localProcessControlProvider -> localProcessControlProvider.sub(parent, commandFunction, terminalCommand)) @@ -38,10 +38,10 @@ public abstract class ProcessControlProvider { .orElseThrow(); } - public static CommandProcessControl createCommand( - ShellProcessControl parent, - @NonNull FailableFunction command, - FailableFunction terminalCommand) { + public static CommandControl createCommand( + ShellControl parent, + @NonNull FailableFunction command, + FailableFunction terminalCommand) { return INSTANCES.stream() .map(localProcessControlProvider -> localProcessControlProvider.command(parent, command, terminalCommand)) @@ -50,7 +50,7 @@ public abstract class ProcessControlProvider { .orElse(null); } - public static ShellProcessControl createSsh(Object sshStore) { + public static ShellControl createSsh(Object sshStore) { return INSTANCES.stream() .map(localProcessControlProvider -> localProcessControlProvider.createSshControl(sshStore)) .filter(Objects::nonNull) @@ -58,17 +58,17 @@ public abstract class ProcessControlProvider { .orElseThrow(); } - public abstract ShellProcessControl sub( - ShellProcessControl parent, - @NonNull FailableFunction commandFunction, - FailableBiFunction terminalCommand); + public abstract ShellControl sub( + ShellControl parent, + @NonNull FailableFunction commandFunction, + FailableBiFunction terminalCommand); - public abstract CommandProcessControl command( - ShellProcessControl parent, - @NonNull FailableFunction command, - FailableFunction terminalCommand); + public abstract CommandControl command( + ShellControl parent, + @NonNull FailableFunction command, + FailableFunction terminalCommand); - public abstract ShellProcessControl createLocalProcessControl(); + public abstract ShellControl createLocalProcessControl(boolean stoppable); - public abstract ShellProcessControl createSshControl(Object sshStore); + public abstract ShellControl createSshControl(Object sshStore); } diff --git a/core/src/main/java/io/xpipe/core/process/ShellProcessControl.java b/core/src/main/java/io/xpipe/core/process/ShellControl.java similarity index 60% rename from core/src/main/java/io/xpipe/core/process/ShellProcessControl.java rename to core/src/main/java/io/xpipe/core/process/ShellControl.java index d2048b49..3d79054f 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellProcessControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -9,13 +9,12 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.Semaphore; import java.util.function.Consumer; -import java.util.function.Predicate; -public interface ShellProcessControl extends ProcessControl { +public interface ShellControl extends ProcessControl { Semaphore getCommandLock(); - void onInit(Consumer pc); + void onInit(Consumer pc); String prepareTerminalOpen() throws Exception; @@ -26,25 +25,25 @@ public interface ShellProcessControl extends ProcessControl { public void checkRunning() throws Exception; default String executeStringSimpleCommand(String command) throws Exception { - try (CommandProcessControl c = command(command).start()) { + try (CommandControl c = command(command).start()) { return c.readOrThrow(); } } default boolean executeBooleanSimpleCommand(String command) throws Exception { - try (CommandProcessControl c = command(command).start()) { + try (CommandControl c = command(command).start()) { return c.discardAndCheckExit(); } } default void executeSimpleCommand(String command) throws Exception { - try (CommandProcessControl c = command(command).start()) { + try (CommandControl c = command(command).start()) { c.discardOrThrow(); } } default void executeSimpleCommand(String command, String failMessage) throws Exception { - try (CommandProcessControl c = command(command).start()) { + try (CommandControl c = command(command).start()) { c.discardOrThrow(); } catch (ProcessOutputException out) { throw ProcessOutputException.of(failMessage, out); @@ -63,52 +62,52 @@ public interface ShellProcessControl extends ProcessControl { OsType getOsType(); - ShellProcessControl elevated(Predicate elevationFunction); + ShellControl elevated(FailableFunction elevationFunction); - ShellProcessControl elevation(SecretValue value); + ShellControl elevationPassword(SecretValue value); - ShellProcessControl initWith(List cmds); + ShellControl initWith(List cmds); SecretValue getElevationPassword(); - default ShellProcessControl subShell(@NonNull ShellDialect type) { + default ShellControl subShell(@NonNull ShellDialect type) { return subShell(p -> type.getNormalOpenCommand(), (shellProcessControl, s) -> { return s == null ? type.getNormalOpenCommand() : type.executeCommandWithShell(s); }) - .elevation(getElevationPassword()); + .elevationPassword(getElevationPassword()); } - default ShellProcessControl subShell(@NonNull List command) { + default ShellControl subShell(@NonNull List command) { return subShell( shellProcessControl -> shellProcessControl.getShellDialect().flatten(command), null); } - default ShellProcessControl subShell(@NonNull String command) { + default ShellControl subShell(@NonNull String command) { return subShell(processControl -> command, null); } - ShellProcessControl subShell( - FailableFunction command, - FailableBiFunction terminalCommand); + ShellControl subShell( + FailableFunction command, + FailableBiFunction terminalCommand); void executeLine(String command) throws Exception; void cd(String directory) throws Exception; @Override - ShellProcessControl start() throws Exception; + ShellControl start() throws Exception; - CommandProcessControl command(FailableFunction command); + CommandControl command(FailableFunction command); - CommandProcessControl command( - FailableFunction command, - FailableFunction terminalCommand); + CommandControl command( + FailableFunction command, + FailableFunction terminalCommand); - default CommandProcessControl command(String command) { + default CommandControl command(String command) { return command(shellProcessControl -> command); } - default CommandProcessControl command(List command) { + default CommandControl command(List command) { return command(shellProcessControl -> shellProcessControl.getShellDialect().flatten(command)); } diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialect.java b/core/src/main/java/io/xpipe/core/process/ShellDialect.java index 3bcf5929..85b4f9c6 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -29,9 +29,9 @@ public interface ShellDialect { String addInlineVariablesToCommand(Map variables, String command); - Stream listFiles(FileSystem fs, ShellProcessControl control, String dir) throws Exception; + Stream listFiles(FileSystem fs, ShellControl control, String dir) throws Exception; - Stream listRoots(ShellProcessControl control) throws Exception; + Stream listRoots(ShellControl control) throws Exception; String getPauseCommand(); @@ -101,7 +101,7 @@ public interface ShellDialect { String getFileMoveCommand(String oldFile, String newFile); - CommandProcessControl getStreamFileWriteCommand(ShellProcessControl processControl, String file); + CommandControl getStreamFileWriteCommand(ShellControl processControl, String file); String getTextFileWriteCommand(String content, String file); @@ -113,7 +113,7 @@ public interface ShellDialect { String getWhichCommand(String executable); - Charset determineCharset(ShellProcessControl control) throws Exception; + Charset determineCharset(ShellControl control) throws Exception; NewLine getNewLine(); diff --git a/core/src/main/java/io/xpipe/core/store/CommandExecutionStore.java b/core/src/main/java/io/xpipe/core/store/CommandExecutionStore.java index b5c8fce7..2c185033 100644 --- a/core/src/main/java/io/xpipe/core/store/CommandExecutionStore.java +++ b/core/src/main/java/io/xpipe/core/store/CommandExecutionStore.java @@ -1,6 +1,6 @@ package io.xpipe.core.store; -import io.xpipe.core.process.CommandProcessControl; +import io.xpipe.core.process.CommandControl; public interface CommandExecutionStore extends DataStore, LaunchableStore { @@ -9,5 +9,5 @@ public interface CommandExecutionStore extends DataStore, LaunchableStore { return create().prepareTerminalOpen(); } - CommandProcessControl create() throws Exception; + CommandControl create() throws Exception; } diff --git a/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java b/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java index 3f731916..74e973b6 100644 --- a/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java +++ b/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java @@ -1,7 +1,7 @@ package io.xpipe.core.store; import com.fasterxml.jackson.annotation.JsonIgnore; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import lombok.Getter; import java.io.IOException; @@ -15,19 +15,19 @@ import java.util.stream.Stream; public class ConnectionFileSystem implements FileSystem { @JsonIgnore - private final ShellProcessControl shellProcessControl; + private final ShellControl shellControl; @JsonIgnore private final ShellStore store; - public ConnectionFileSystem(ShellProcessControl shellProcessControl, ShellStore store) { - this.shellProcessControl = shellProcessControl; + public ConnectionFileSystem(ShellControl shellControl, ShellStore store) { + this.shellControl = shellControl; this.store = store; } @Override public List listRoots() throws Exception { - return shellProcessControl.getShellDialect().listRoots(shellProcessControl).toList(); + return shellControl.getShellDialect().listRoots(shellControl).toList(); } @Override @@ -35,7 +35,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public Stream listFiles(String file) throws Exception { - return shellProcessControl.getShellDialect().listFiles(this, shellProcessControl, file); + return shellControl.getShellDialect().listFiles(this, shellControl, file); } @Override @@ -44,33 +44,33 @@ public class ConnectionFileSystem implements FileSystem { } @Override - public Optional getShell() { - return Optional.of(shellProcessControl); + public Optional getShell() { + return Optional.of(shellControl); } @Override public FileSystem open() throws Exception { - shellProcessControl.start(); + shellControl.start(); return this; } @Override public InputStream openInput(String file) throws Exception { - return shellProcessControl.command(proc -> + return shellControl.command(proc -> proc.getShellDialect().getFileReadCommand(proc.getOsType().normalizeFileName(file))) .startExternalStdout(); } @Override public OutputStream openOutput(String file) throws Exception { - return shellProcessControl.getShellDialect() - .getStreamFileWriteCommand(shellProcessControl, shellProcessControl.getOsType().normalizeFileName(file)) + return shellControl.getShellDialect() + .getStreamFileWriteCommand(shellControl, shellControl.getOsType().normalizeFileName(file)) .startExternalStdin(); } @Override public boolean exists(String file) throws Exception { - try (var pc = shellProcessControl.command(proc -> proc.getShellDialect() + try (var pc = shellControl.command(proc -> proc.getShellDialect() .getFileExistsCommand(proc.getOsType().normalizeFileName(file))) .start()) { return pc.discardAndCheckExit(); @@ -79,7 +79,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public void delete(String file) throws Exception { - try (var pc = shellProcessControl.command(proc -> proc.getShellDialect() + try (var pc = shellControl.command(proc -> proc.getShellDialect() .getFileDeleteCommand(proc.getOsType().normalizeFileName(file))) .start()) { pc.discardOrThrow(); @@ -88,7 +88,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public void copy(String file, String newFile) throws Exception { - try (var pc = shellProcessControl.command(proc -> proc.getShellDialect() + try (var pc = shellControl.command(proc -> proc.getShellDialect() .getFileCopyCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex() .start()) { pc.discardOrThrow(); @@ -97,7 +97,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public void move(String file, String newFile) throws Exception { - try (var pc = shellProcessControl.command(proc -> proc.getShellDialect() + try (var pc = shellControl.command(proc -> proc.getShellDialect() .getFileMoveCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex() .start()) { pc.discardOrThrow(); @@ -106,7 +106,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public boolean mkdirs(String file) throws Exception { - try (var pc = shellProcessControl.command(proc -> proc.getShellDialect() + try (var pc = shellControl.command(proc -> proc.getShellDialect() .flatten(proc.getShellDialect() .getMkdirsCommand(proc.getOsType().normalizeFileName(file)))) .start()) { @@ -116,7 +116,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public void touch(String file) throws Exception { - try (var pc = shellProcessControl.command(proc -> proc.getShellDialect() + try (var pc = shellControl.command(proc -> proc.getShellDialect() .getFileTouchCommand(proc.getOsType().normalizeFileName(file))).complex() .start()) { pc.discardOrThrow(); @@ -125,6 +125,6 @@ public class ConnectionFileSystem implements FileSystem { @Override public void close() throws IOException { - shellProcessControl.close(); + shellControl.close(); } } diff --git a/core/src/main/java/io/xpipe/core/store/FileSystem.java b/core/src/main/java/io/xpipe/core/store/FileSystem.java index 5bf2b923..8231eb41 100644 --- a/core/src/main/java/io/xpipe/core/store/FileSystem.java +++ b/core/src/main/java/io/xpipe/core/store/FileSystem.java @@ -1,7 +1,7 @@ package io.xpipe.core.store; import io.xpipe.core.impl.FileNames; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import lombok.NonNull; import lombok.Value; @@ -44,7 +44,7 @@ public interface FileSystem extends Closeable, AutoCloseable { FileSystemStore getStore(); - Optional getShell(); + Optional getShell(); FileSystem open() throws Exception; diff --git a/core/src/main/java/io/xpipe/core/store/ShellStore.java b/core/src/main/java/io/xpipe/core/store/ShellStore.java index d47d12ec..f7cf0226 100644 --- a/core/src/main/java/io/xpipe/core/store/ShellStore.java +++ b/core/src/main/java/io/xpipe/core/store/ShellStore.java @@ -3,7 +3,7 @@ package io.xpipe.core.store; import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; import io.xpipe.core.process.ShellDialect; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import java.nio.charset.Charset; @@ -27,7 +27,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor return create().prepareTerminalOpen(); } - default ShellProcessControl create() { + default ShellControl create() { var pc = createControl(); pc.onInit(processControl -> { setState("type", processControl.getShellDialect()); @@ -49,7 +49,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor return getState("charset", Charset.class, null); } - ShellProcessControl createControl(); + ShellControl createControl(); public default ShellDialect determineType() throws Exception { try (var pc = create().start()) { @@ -59,7 +59,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor @Override default void validate() throws Exception { - try (ShellProcessControl pc = create().start()) {} + try (ShellControl pc = create().start()) {} } public default String queryMachineName() throws Exception { diff --git a/core/src/main/java/io/xpipe/core/util/ProxyManagerProvider.java b/core/src/main/java/io/xpipe/core/util/ProxyManagerProvider.java index ed511efb..4b2768be 100644 --- a/core/src/main/java/io/xpipe/core/util/ProxyManagerProvider.java +++ b/core/src/main/java/io/xpipe/core/util/ProxyManagerProvider.java @@ -1,6 +1,6 @@ package io.xpipe.core.util; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import java.util.Optional; import java.util.ServiceLoader; @@ -19,7 +19,7 @@ public abstract class ProxyManagerProvider { return INSTANCE; } - public abstract Optional checkCompatibility(ShellProcessControl pc) throws Exception; + public abstract Optional checkCompatibility(ShellControl pc) throws Exception; - public abstract boolean setup(ShellProcessControl pc) throws Exception; + public abstract boolean setup(ShellControl pc) throws Exception; } diff --git a/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java b/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java index d3adb417..55558bcc 100644 --- a/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java +++ b/core/src/main/java/io/xpipe/core/util/XPipeInstallation.java @@ -1,10 +1,10 @@ package io.xpipe.core.util; import io.xpipe.core.impl.FileNames; -import io.xpipe.core.process.CommandProcessControl; +import io.xpipe.core.process.CommandControl; import io.xpipe.core.process.OsType; import io.xpipe.core.process.ProcessOutputException; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import lombok.SneakyThrows; import java.nio.charset.StandardCharsets; @@ -153,21 +153,21 @@ public class XPipeInstallation { return v; } - public static String queryInstallationVersion(ShellProcessControl p, String exec) throws Exception { - try (CommandProcessControl c = p.command(List.of(exec, "version")).start()) { + public static String queryInstallationVersion(ShellControl p, String exec) throws Exception { + try (CommandControl c = p.command(List.of(exec, "version")).start()) { return c.readOrThrow(); } catch (ProcessOutputException ex) { return "?"; } } - public static String getInstallationExecutable(ShellProcessControl p, String installation) throws Exception { + public static String getInstallationExecutable(ShellControl p, String installation) throws Exception { var executable = getDaemonExecutablePath(p.getOsType()); var file = FileNames.join(installation, executable); return file; } - public static String getDataBasePath(ShellProcessControl p) throws Exception { + public static String getDataBasePath(ShellControl p) throws Exception { if (p.getOsType().equals(OsType.WINDOWS)) { var base = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("userprofile")); return FileNames.join(base, ".xpipe"); @@ -218,7 +218,7 @@ public class XPipeInstallation { return path; } - public static String getDefaultInstallationBasePath(ShellProcessControl p, boolean acceptPortable) + public static String getDefaultInstallationBasePath(ShellControl p, boolean acceptPortable) throws Exception { if (acceptPortable) { var customHome = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("XPIPE_HOME")); diff --git a/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java b/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java index 1dbb129c..7286b1ca 100644 --- a/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java +++ b/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java @@ -2,7 +2,7 @@ package io.xpipe.core.util; import io.xpipe.core.impl.FileNames; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellProcessControl; +import io.xpipe.core.process.ShellControl; import java.io.IOException; import java.nio.file.Path; @@ -19,7 +19,7 @@ public class XPipeTempDirectory { } } - public static String get(ShellProcessControl proc) throws Exception { + public static String get(ShellControl proc) throws Exception { var base = proc.getOsType().getTempDirectory(proc); var dir = FileNames.join(base, "xpipe"); @@ -36,7 +36,7 @@ public class XPipeTempDirectory { return dir; } - public static void clear(ShellProcessControl proc) throws Exception { + public static void clear(ShellControl proc) throws Exception { var dir = get(proc); if (!proc.executeBooleanSimpleCommand(proc.getShellDialect().getFileDeleteCommand(dir))) { throw new IOException("Unable to delete temporary directory " + dir);