From 8c0f8efed95989e0d97f0637febe01421bca0716 Mon Sep 17 00:00:00 2001 From: Max Rydahl Andersen Date: Thu, 12 Sep 2024 09:35:19 +0200 Subject: [PATCH] feat: linuxdistro jdk provider (#1826) * feat: liunxdistro jdk provider adds `linuxdistro` provider which for now searches `/usr/lib/jvm` for jdks. It is not enabled by default as it is not possible to safely determine the version and build of the jdk present or needed to be installed if mising. Meaning to get it user need to select it `--jdk-provider=linuxdistro` or just enable all providers `--jdk-provider=all` on command line or run `jbang config set jdk-provider linuxdistro` to make it default for all jbang invocations. * fix: check before assuning name is jdkprovider * fix: make sure linux distro JDKs are valid * chore: simplified LinuxDistroJdkProvider name and id --------- Co-authored-by: Tako Schotanus --- src/main/java/dev/jbang/net/JdkManager.java | 13 ++-- src/main/java/dev/jbang/net/JdkProvider.java | 6 +- .../jdkproviders/BaseFoldersJdkProvider.java | 10 +++- .../net/jdkproviders/LinuxJdkProvider.java | 59 +++++++++++++++++++ src/test/java/dev/jbang/cli/TestJdk.java | 6 ++ 5 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 src/main/java/dev/jbang/net/jdkproviders/LinuxJdkProvider.java diff --git a/src/main/java/dev/jbang/net/JdkManager.java b/src/main/java/dev/jbang/net/JdkManager.java index 0102888b5..e5279cefa 100644 --- a/src/main/java/dev/jbang/net/JdkManager.java +++ b/src/main/java/dev/jbang/net/JdkManager.java @@ -21,13 +21,7 @@ import dev.jbang.Settings; import dev.jbang.cli.ExitException; -import dev.jbang.net.jdkproviders.CurrentJdkProvider; -import dev.jbang.net.jdkproviders.DefaultJdkProvider; -import dev.jbang.net.jdkproviders.JBangJdkProvider; -import dev.jbang.net.jdkproviders.JavaHomeJdkProvider; -import dev.jbang.net.jdkproviders.PathJdkProvider; -import dev.jbang.net.jdkproviders.ScoopJdkProvider; -import dev.jbang.net.jdkproviders.SdkmanJdkProvider; +import dev.jbang.net.jdkproviders.*; import dev.jbang.util.JavaUtil; import dev.jbang.util.Util; @@ -36,7 +30,7 @@ public class JdkManager { // TODO Don't hard-code this list public static final String[] PROVIDERS_ALL = new String[] { "current", "default", "javahome", "path", "jbang", - "sdkman", "scoop" }; + "sdkman", "scoop", "linux" }; public static final String[] PROVIDERS_DEFAULT = new String[] { "current", "default", "javahome", "path", "jbang" }; public static void initProvidersByName(String... providerNames) { @@ -74,6 +68,9 @@ public static void initProvidersByName(List providerNames) { case "scoop": provider = new ScoopJdkProvider(); break; + case "linux": + provider = new LinuxJdkProvider(); + break; default: Util.warnMsg("Unknown JDK provider: " + name); continue; diff --git a/src/main/java/dev/jbang/net/JdkProvider.java b/src/main/java/dev/jbang/net/JdkProvider.java index 80c57eb5d..9a2e00715 100644 --- a/src/main/java/dev/jbang/net/JdkProvider.java +++ b/src/main/java/dev/jbang/net/JdkProvider.java @@ -121,7 +121,11 @@ default Jdk createJdk(@Nonnull String id, @Nullable Path home, @Nonnull String v default String name() { String nm = getClass().getSimpleName(); - return nm.substring(0, nm.length() - 11).toLowerCase(); + if (nm.endsWith("JdkProvider")) { + return nm.substring(0, nm.length() - 11).toLowerCase(); + } else { + return nm.toLowerCase(); + } } /** diff --git a/src/main/java/dev/jbang/net/jdkproviders/BaseFoldersJdkProvider.java b/src/main/java/dev/jbang/net/jdkproviders/BaseFoldersJdkProvider.java index d680e352b..9426a14f0 100644 --- a/src/main/java/dev/jbang/net/jdkproviders/BaseFoldersJdkProvider.java +++ b/src/main/java/dev/jbang/net/jdkproviders/BaseFoldersJdkProvider.java @@ -87,7 +87,7 @@ protected Path getJdkPath(@Nonnull String jdk) { return getJdksRoot().resolve(jdk); } - private Predicate sameJdk(Path jdkRoot) { + protected Predicate sameJdk(Path jdkRoot) { Path release = jdkRoot.resolve("release"); return (Path p) -> { try { @@ -100,7 +100,7 @@ private Predicate sameJdk(Path jdkRoot) { protected Stream listJdkPaths() throws IOException { if (Files.isDirectory(getJdksRoot())) { - return Files.list(getJdksRoot()); + return Files.list(getJdksRoot()).filter(this::acceptFolder); } return Stream.empty(); } @@ -114,12 +114,16 @@ protected Path getJdksRoot() { protected Jdk createJdk(Path home) { String name = home.getFileName().toString(); Optional version = JavaUtil.resolveJavaVersionStringFromPath(home); - if (version.isPresent()) { + if (version.isPresent() && acceptFolder(home)) { return createJdk(jdkId(name), home, version.get()); } return null; } + protected boolean acceptFolder(Path jdkFolder) { + return Util.searchPath("javac", jdkFolder.resolve("bin").toString()) != null; + } + protected boolean isValidId(String id) { return id.endsWith("-" + name()); } diff --git a/src/main/java/dev/jbang/net/jdkproviders/LinuxJdkProvider.java b/src/main/java/dev/jbang/net/jdkproviders/LinuxJdkProvider.java new file mode 100644 index 000000000..c8dd49b84 --- /dev/null +++ b/src/main/java/dev/jbang/net/jdkproviders/LinuxJdkProvider.java @@ -0,0 +1,59 @@ +package dev.jbang.net.jdkproviders; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * This JDK provider is intended to detects JDKs that have been installed in + * standard location of the users linux distro. + * + * For now just using `/usr/lib/jvm` as apparently fedora, debian, ubuntu and + * centos/rhel use it. + * + * If need different behavior per linux distro its intended this provider will + * adjust based on identified distro. + * + */ +public class LinuxJdkProvider extends BaseFoldersJdkProvider { + private static final Path JDKS_ROOT = Paths.get("/usr/lib/jvm"); + + @Nonnull + @Override + protected Path getJdksRoot() { + return JDKS_ROOT; + } + + @Nullable + @Override + protected String jdkId(String name) { + return name + "-linux"; + } + + @Override + public boolean canUse() { + return Files.isDirectory(JDKS_ROOT); + } + + @Override + protected boolean acceptFolder(Path jdkFolder) { + return super.acceptFolder(jdkFolder) && !isSameFolderSymLink(jdkFolder); + } + + // Returns true if a path is a symlink to an entry in the same folder + private boolean isSameFolderSymLink(Path jdkFolder) { + Path absFolder = jdkFolder.toAbsolutePath(); + if (Files.isSymbolicLink(absFolder)) { + try { + Path realPath = absFolder.toRealPath(); + return Files.isSameFile(absFolder.getParent(), realPath.getParent()); + } catch (IOException e) { + /* ignore */ } + } + return false; + } +} diff --git a/src/test/java/dev/jbang/cli/TestJdk.java b/src/test/java/dev/jbang/cli/TestJdk.java index b2efc58df..0d11ed857 100644 --- a/src/test/java/dev/jbang/cli/TestJdk.java +++ b/src/test/java/dev/jbang/cli/TestJdk.java @@ -451,9 +451,15 @@ private void initMockJdkDir(Path jdkPath, String version) { private void initMockJdkDir(Path jdkPath, String version, String key) { Util.mkdirs(jdkPath); + Path jdkBinPath = jdkPath.resolve("bin"); + Util.mkdirs(jdkBinPath); String rawJavaVersion = key + "=\"" + version + "\""; Path release = jdkPath.resolve("release"); try { + Path javacPath = jdkBinPath.resolve("javac"); + Util.writeString(javacPath, "dummy"); + javacPath.toFile().setExecutable(true, true); + Util.writeString(jdkBinPath.resolve("javac.exe"), "dummy"); Util.writeString(release, rawJavaVersion); } catch (IOException e) { throw new RuntimeException(e);