diff --git a/app/build.gradle b/app/build.gradle index 169664462..c2f8cdebe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,7 +55,7 @@ dependencies { api "io.projectreactor:reactor-core:3.7.9" api "org.reactivestreams:reactive-streams:1.0.4" - api ("com.networknt:json-schema-validator:1.5.8") { + api ("com.networknt:json-schema-validator:2.0.0") { exclude group: "com.ethlo.time", module: "itu" exclude group: "com.fasterxml.jackson.dataformat", module: "jackson-dataformat-yaml" } @@ -79,7 +79,9 @@ dependencies { api 'io.xpipe:modulefs:0.1.8' api 'net.synedra:validatorfx:0.4.2' api files("$rootDir/gradle/gradle_scripts/atlantafx-base-2.0.2.jar") - api("com.dlsc.atlantafx:themes:1.9.0") + api("com.dlsc.atlantafx:themes:1.9.0") { + exclude group: "io.github.mkpaz", module: "atlantafx-base" + } api("org.int4.fx:fx-values:0.4") api files("$rootDir/gradle/gradle_scripts/fx-builders-1.0.0-SNAPSHOT.jar") diff --git a/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java b/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java index 761c7ca1d..d5ef144fc 100644 --- a/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java +++ b/app/src/main/java/io/xpipe/app/core/AppExtensionManager.java @@ -10,8 +10,12 @@ import io.xpipe.app.util.ModuleLayerLoader; import lombok.Getter; +import java.io.IOException; import java.lang.module.Configuration; import java.lang.module.ModuleFinder; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -23,7 +27,9 @@ public class AppExtensionManager { private static AppExtensionManager INSTANCE; private final List loadedModules = new ArrayList<>(); private final List leafModuleLayers = new ArrayList<>(); - private final List extensionBaseDirectories = new ArrayList<>(); + + private FileSystem externalModules; + private ModuleLayer baseLayer = ModuleLayer.boot(); @Getter @@ -35,7 +41,6 @@ public class AppExtensionManager { } INSTANCE = new AppExtensionManager(); - INSTANCE.determineExtensionDirectories(); INSTANCE.loadBaseExtension(); INSTANCE.loadAllExtensions(); try { @@ -49,6 +54,15 @@ public class AppExtensionManager { } public static void reset() { + if (INSTANCE.externalModules != null) { + try { + INSTANCE.externalModules.close(); + } catch (IOException e) { + ErrorEventFactory.fromThrowable(e).handle(); + } + INSTANCE.externalModules = null; + } + INSTANCE = null; } @@ -77,43 +91,6 @@ public class AppExtensionManager { loadedModules.add(baseModule.get()); } - private void determineExtensionDirectories() throws Exception { - if (!AppProperties.get().isFullVersion()) { - var localInstallation = - !AppProperties.get().isStaging() && AppProperties.get().isLocatePtb() - ? AppInstallation.ofDefault(true) - : AppInstallation.ofCurrent(); - Path p = localInstallation.getBaseInstallationPath(); - if (!Files.exists(p)) { - throw new IllegalStateException( - "Required local " + AppNames.ofCurrent().getName() - + " installation was not found but is required for development. See https://github" - + ".com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup"); - } - - if (AppProperties.get().isLocatorVersionCheck()) { - var iv = getLocalInstallVersion(localInstallation); - var installVersion = AppVersion.parse(iv) - .orElseThrow(() -> new IllegalArgumentException("Invalid installation version: " + iv)); - var sv = !AppProperties.get().isImage() - ? Files.readString(Path.of("version")).strip() - : AppProperties.get().getVersion(); - var sourceVersion = AppVersion.parse(sv) - .orElseThrow(() -> new IllegalArgumentException("Invalid source version: " + sv)); - if (!installVersion.equals(sourceVersion)) { - throw new IllegalStateException("Incompatible development version. Source: " + sv - + ", Installation: " - + iv - + "\n\nPlease try to check out the matching release version in the repository. See https://github" - + ".com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup"); - } - } - - var extensions = localInstallation.getExtensionsPath(); - extensionBaseDirectories.add(extensions); - } - } - public Set getContentModules() { return Stream.concat( Stream.of(ModuleLayer.boot() @@ -157,12 +134,47 @@ public class AppExtensionManager { return inModulePath; } - for (Path extensionBaseDirectory : extensionBaseDirectories) { - var extensionDir = extensionBaseDirectory.resolve(name); - var found = parseExtensionDirectory(extensionDir, parent); - if (found.isPresent()) { - TrackEvent.info("Loaded extension " + name + " from module " + extensionDir); - return found; + if (!AppProperties.get().isFullVersion()) { + try { + if (externalModules == null) { + var localInstallation = !AppProperties.get().isStaging() && AppProperties.get().isLocatePtb() ? + AppInstallation.ofDefault(true) : + AppInstallation.ofCurrent(); + Path p = localInstallation.getBaseInstallationPath(); + if (!Files.exists(p)) { + throw new IllegalStateException("Required local " + + AppNames.ofCurrent().getName() + + " installation was not found but is required for development. See https://github" + + ".com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup"); + } + + if (AppProperties.get().isLocatorVersionCheck()) { + var iv = getLocalInstallVersion(localInstallation); + var installVersion = AppVersion.parse(iv).orElseThrow( + () -> new IllegalArgumentException("Invalid installation version: " + iv)); + var sv = !AppProperties.get().isImage() ? Files.readString(Path.of("version")).strip() : AppProperties.get().getVersion(); + var sourceVersion = AppVersion.parse(sv).orElseThrow(() -> new IllegalArgumentException("Invalid source version: " + sv)); + if (!installVersion.equals(sourceVersion)) { + throw new IllegalStateException("Incompatible development version. Source: " + + sv + + ", Installation: " + + iv + + "\n\nPlease try to check out the matching release version in the repository. See https://github" + + ".com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup"); + } + } + + externalModules = FileSystems.newFileSystem(URI.create("jrt:/"), Map.of("java.home", localInstallation.getRuntimePath())); + } + + var basePath = externalModules.getPath("modules", "io.xpipe.ext." + name); + var found = parseExtensionDirectory(basePath, parent); + if (found.isPresent()) { + TrackEvent.info("Loaded extension " + name + " from module " + basePath); + return found; + } + } catch (Throwable t) { + ErrorEventFactory.fromThrowable(t).handle(); } } diff --git a/app/src/main/java/io/xpipe/app/core/AppInstallation.java b/app/src/main/java/io/xpipe/app/core/AppInstallation.java index 746c21760..434daa1af 100644 --- a/app/src/main/java/io/xpipe/app/core/AppInstallation.java +++ b/app/src/main/java/io/xpipe/app/core/AppInstallation.java @@ -172,6 +172,8 @@ public abstract class AppInstallation { public abstract Path getLogoPath(); + public abstract Path getRuntimePath(); + public static class Windows extends AppInstallation { private Windows(Path base) { @@ -208,6 +210,11 @@ public abstract class AppInstallation { public Path getLogoPath() { return getBaseInstallationPath().resolve("logo.ico"); } + + @Override + public Path getRuntimePath() { + return getBaseInstallationPath().resolve("runtime"); + } } public static class WindowsDev extends Windows { @@ -236,6 +243,11 @@ public abstract class AppInstallation { super(base); } + @Override + public Path getRuntimePath() { + return getBaseInstallationPath().resolve("lib", "runtime"); + } + @Override public Path getDaemonDebugScriptPath() { return getBaseInstallationPath() @@ -293,6 +305,11 @@ public abstract class AppInstallation { super(base); } + @Override + public Path getRuntimePath() { + return getBaseInstallationPath().resolve("Contents", "runtime", "Contents", "Home"); + } + @Override public Path getDaemonDebugScriptPath() { return getBaseInstallationPath() diff --git a/app/src/main/java/io/xpipe/app/core/AppProperties.java b/app/src/main/java/io/xpipe/app/core/AppProperties.java index 6e0cc6a4c..12df507a8 100644 --- a/app/src/main/java/io/xpipe/app/core/AppProperties.java +++ b/app/src/main/java/io/xpipe/app/core/AppProperties.java @@ -175,7 +175,7 @@ public class AppProperties { .orElse("info"); loginTarget = Optional.ofNullable(System.getProperty(AppNames.propertyName("login"))) .orElse(null); - localWebtopDockerfile = Optional.of(System.getProperty(AppNames.propertyName("localWebtopDockerfile"))) + localWebtopDockerfile = Optional.ofNullable(System.getProperty(AppNames.propertyName("localWebtopDockerfile"))) .map(s -> FilePath.parse(s)).orElse(null); isCli = Optional.ofNullable(System.getProperty(AppNames.propertyName("isCli"))) .map(Boolean::parseBoolean) diff --git a/app/src/main/java/io/xpipe/app/terminal/TerminalPrompt.java b/app/src/main/java/io/xpipe/app/terminal/TerminalPrompt.java index c149b4217..619ed23ba 100644 --- a/app/src/main/java/io/xpipe/app/terminal/TerminalPrompt.java +++ b/app/src/main/java/io/xpipe/app/terminal/TerminalPrompt.java @@ -28,7 +28,7 @@ public interface TerminalPrompt { String getDocsLink(); default FilePath getConfigurationDirectory(ShellControl sc) throws Exception { - var d = ShellTemp.createUserSpecificTempDataDirectory(sc, "prompt").join(getId()); + var d = ShellTemp.createUserSpecificTempDataDirectory(sc, null).join(getId()); sc.view().mkdir(d); return d; }