From 872ead61c5ec726233f60b29030229315629e31c Mon Sep 17 00:00:00 2001 From: Tako Schotanus Date: Fri, 12 Jun 2026 16:01:32 +0200 Subject: [PATCH] fix: fixed issue with `linked()` and real paths The issue was that `linked()` calls `toRealPath()` on a Jdk's home path and then asks all providers which of them manages the Jdk that it points to, using a call to `getInstalledByPath()`. However, if the provider was intialized with a path that contains symlinks, it would not be able to find the Jdk, because the path it was initialized with would not match the real path. The soution was to call `toRealPath()` on the provider's path during initialization. --- src/main/java/dev/jbang/devkitman/Jdk.java | 8 ++------ .../devkitman/jdkproviders/BaseFoldersJdkProvider.java | 5 ++++- .../jbang/devkitman/jdkproviders/LinuxJdkProvider.java | 6 +++++- .../jbang/devkitman/jdkproviders/MacJdkProvider.java | 2 +- .../jbang/devkitman/jdkproviders/MiseJdkProvider.java | 6 +++++- .../jbang/devkitman/jdkproviders/ScoopJdkProvider.java | 6 +++++- .../devkitman/jdkproviders/SdkmanJdkProvider.java | 6 +++++- src/main/java/dev/jbang/devkitman/util/JavaUtils.java | 7 +------ src/test/java/dev/jbang/devkitman/TestJdkManager.java | 10 ++++++++++ 9 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/main/java/dev/jbang/devkitman/Jdk.java b/src/main/java/dev/jbang/devkitman/Jdk.java index b7ab83f..14fe32d 100644 --- a/src/main/java/dev/jbang/devkitman/Jdk.java +++ b/src/main/java/dev/jbang/devkitman/Jdk.java @@ -11,6 +11,7 @@ import org.jspecify.annotations.Nullable; import dev.jbang.devkitman.jdkproviders.ExternalJdkProvider; +import dev.jbang.devkitman.util.FileUtils; import dev.jbang.devkitman.util.JavaUtils; public interface Jdk extends Comparable { @@ -229,12 +230,7 @@ public Default( @Override @NonNull public InstalledJdk linked() { - Path jdkHome; - try { - jdkHome = home().toRealPath(); - } catch (Exception e) { - jdkHome = home().toAbsolutePath(); - } + Path jdkHome = FileUtils.realPath(home()); // First look for a Jdk in updatable non-linking providers InstalledJdk linkedJdk = getLinkedJdk(jdkHome, p -> p.canUpdate() && !p.hasLinkedVersions()); if (linkedJdk == null) { diff --git a/src/main/java/dev/jbang/devkitman/jdkproviders/BaseFoldersJdkProvider.java b/src/main/java/dev/jbang/devkitman/jdkproviders/BaseFoldersJdkProvider.java index 609c924..6ecdc77 100644 --- a/src/main/java/dev/jbang/devkitman/jdkproviders/BaseFoldersJdkProvider.java +++ b/src/main/java/dev/jbang/devkitman/jdkproviders/BaseFoldersJdkProvider.java @@ -14,15 +14,18 @@ import org.jspecify.annotations.Nullable; import dev.jbang.devkitman.Jdk; +import dev.jbang.devkitman.util.FileUtils; import dev.jbang.devkitman.util.JavaUtils; public abstract class BaseFoldersJdkProvider extends BaseJdkProvider { protected final Path jdksRoot; + protected final Path realRoot; private static final Logger LOGGER = Logger.getLogger(BaseFoldersJdkProvider.class.getName()); protected BaseFoldersJdkProvider(Path jdksRoot) { this.jdksRoot = jdksRoot; + this.realRoot = FileUtils.realPath(jdksRoot); } @Override @@ -130,7 +133,7 @@ protected Stream listJdkPaths() throws IOException { } protected boolean acceptFolder(@NonNull Path jdkFolder) { - return jdkFolder.startsWith(jdksRoot) && JavaUtils.hasJavacCmd(jdkFolder); + return (jdkFolder.startsWith(jdksRoot) || jdkFolder.startsWith(realRoot)) && JavaUtils.hasJavacCmd(jdkFolder); } private final Pattern validId = Pattern.compile("^[a-zA-Z0-9._+-]+$"); diff --git a/src/main/java/dev/jbang/devkitman/jdkproviders/LinuxJdkProvider.java b/src/main/java/dev/jbang/devkitman/jdkproviders/LinuxJdkProvider.java index b7701fd..4b447ff 100644 --- a/src/main/java/dev/jbang/devkitman/jdkproviders/LinuxJdkProvider.java +++ b/src/main/java/dev/jbang/devkitman/jdkproviders/LinuxJdkProvider.java @@ -25,7 +25,11 @@ public class LinuxJdkProvider extends BaseFoldersJdkProvider { private static final Path JDKS_ROOT = Paths.get("/usr/lib/jvm"); public LinuxJdkProvider() { - super(jdksRoot()); + this(jdksRoot()); + } + + LinuxJdkProvider(@NonNull Path jdksRoot) { + super(jdksRoot); } public static Path jdksRoot() { diff --git a/src/main/java/dev/jbang/devkitman/jdkproviders/MacJdkProvider.java b/src/main/java/dev/jbang/devkitman/jdkproviders/MacJdkProvider.java index 9a8debf..76e90e7 100644 --- a/src/main/java/dev/jbang/devkitman/jdkproviders/MacJdkProvider.java +++ b/src/main/java/dev/jbang/devkitman/jdkproviders/MacJdkProvider.java @@ -26,7 +26,7 @@ public class MacJdkProvider extends BaseFoldersJdkProvider { private static final String CONTENTS_HOME = "Contents/Home"; public MacJdkProvider() { - super(jdksRoot()); + this(jdksRoot()); } MacJdkProvider(@NonNull Path jdksRoot) { diff --git a/src/main/java/dev/jbang/devkitman/jdkproviders/MiseJdkProvider.java b/src/main/java/dev/jbang/devkitman/jdkproviders/MiseJdkProvider.java index 1c80f46..a278641 100644 --- a/src/main/java/dev/jbang/devkitman/jdkproviders/MiseJdkProvider.java +++ b/src/main/java/dev/jbang/devkitman/jdkproviders/MiseJdkProvider.java @@ -17,7 +17,11 @@ public class MiseJdkProvider extends BaseFoldersJdkProvider { private static final Path JDKS_ROOT = Paths.get(".local", "share", "mise", "installs", "java"); public MiseJdkProvider() { - super(jdksRoot()); + this(jdksRoot()); + } + + MiseJdkProvider(@NonNull Path jdksRoot) { + super(jdksRoot); } public static Path jdksRoot() { diff --git a/src/main/java/dev/jbang/devkitman/jdkproviders/ScoopJdkProvider.java b/src/main/java/dev/jbang/devkitman/jdkproviders/ScoopJdkProvider.java index dad5af7..f1798b3 100644 --- a/src/main/java/dev/jbang/devkitman/jdkproviders/ScoopJdkProvider.java +++ b/src/main/java/dev/jbang/devkitman/jdkproviders/ScoopJdkProvider.java @@ -20,7 +20,11 @@ public class ScoopJdkProvider extends BaseFoldersJdkProvider { private static final Path JDKS_ROOT = Paths.get("scoop", "apps"); public ScoopJdkProvider() { - super(jdksRoot()); + this(jdksRoot()); + } + + ScoopJdkProvider(@NonNull Path jdksRoot) { + super(jdksRoot); } public static Path jdksRoot() { diff --git a/src/main/java/dev/jbang/devkitman/jdkproviders/SdkmanJdkProvider.java b/src/main/java/dev/jbang/devkitman/jdkproviders/SdkmanJdkProvider.java index d36d8fc..e073c57 100644 --- a/src/main/java/dev/jbang/devkitman/jdkproviders/SdkmanJdkProvider.java +++ b/src/main/java/dev/jbang/devkitman/jdkproviders/SdkmanJdkProvider.java @@ -17,7 +17,11 @@ public class SdkmanJdkProvider extends BaseFoldersJdkProvider { private static final Path JDKS_ROOT = Paths.get(".sdkman", "candidates", "java"); public SdkmanJdkProvider() { - super(jdksRoot()); + this(jdksRoot()); + } + + SdkmanJdkProvider(@NonNull Path jdksRoot) { + super(jdksRoot); } public static Path jdksRoot() { diff --git a/src/main/java/dev/jbang/devkitman/util/JavaUtils.java b/src/main/java/dev/jbang/devkitman/util/JavaUtils.java index 037cfde..675640e 100644 --- a/src/main/java/dev/jbang/devkitman/util/JavaUtils.java +++ b/src/main/java/dev/jbang/devkitman/util/JavaUtils.java @@ -144,12 +144,7 @@ public static Path getJavaHomeEnv() { public static Path jre2jdk(@NonNull Path jdkHome) { // Detect if the current JDK is a JRE and try to find the real home if (!Files.isRegularFile(jdkHome.resolve("release"))) { - Path jh = jdkHome.toAbsolutePath(); - try { - jh = jh.toRealPath(); - } catch (IOException e) { - // Ignore error - } + Path jh = FileUtils.realPath(jdkHome); if (jh.endsWith("jre") && Files.isRegularFile(jh.getParent().resolve("release"))) { jdkHome = jh.getParent(); } diff --git a/src/test/java/dev/jbang/devkitman/TestJdkManager.java b/src/test/java/dev/jbang/devkitman/TestJdkManager.java index e2c820d..7ee49ae 100644 --- a/src/test/java/dev/jbang/devkitman/TestJdkManager.java +++ b/src/test/java/dev/jbang/devkitman/TestJdkManager.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; import dev.jbang.devkitman.jdkproviders.DefaultJdkProvider; import dev.jbang.devkitman.jdkproviders.JavaHomeJdkProvider; @@ -125,6 +126,15 @@ void testDefaultCustomLinkPath() { assertThat(jm.getDefaultJdk().id(), is("default")); } + @Test + void testDefaultUnstableBasePath(@TempDir Path tempPath1, @TempDir Path tempPath2) throws IOException { + Path tmp = tempPath1.resolve("dkmtest"); + FileUtils.createLink(tmp, tempPath2); + System.setProperty("user.home", tmp.resolve("home").toString()); + config = new JdkDiscovery.Config(tmp.resolve("jdks"), null, null); + testDefault(); + } + @Test void testHomeDir() { Arrays.asList(11, 14, 17).forEach(this::createMockJdk);