diff --git a/README.md b/README.md
index 4f6c3b1..02140ef 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# jvm - Java Version Manager
+oes# jvm - Java Version Manager
A command line tool that helps you install and manage multiple Java versions on your machine.
@@ -27,3 +27,26 @@ To build the project simply run:
```shell
./mvnw spotless:apply clean verify
```
+ mavern have
+### Building Native Executables
+
+The project supports building native executables using GraalVM. This creates a standalone binary with faster startup time and lower memory footprint.
+
+#### Prerequisites
+
+1. Install GraalVM (recommended: GraalVM 22.3 or later)
+2. Set `GRAALVM_HOME` or `JAVA_HOME` to point to your GraalVM installation
+3. Install the native-image tool:
+ ```shell
+ gu install native-image
+ ```
+
+#### Build Native Executable
+
+To build a native executable, run:
+
+```shell
+./mvnw clean package -Pnative
+```
+
+The native executable will be created in `target/jvm` (or `target/jvm.exe` on Windows).
diff --git a/app.yml b/app.yml
index acfa2d1..d0dda2a 100644
--- a/app.yml
+++ b/app.yml
@@ -8,7 +8,7 @@ links:
documentation: https://github.com/codejive/java-jvm/blob/main/README.md
java: 11
dependencies:
- - dev.jbang:devkitman:0.3.0
+ - dev.jbang:devkitman:0.4.2
- de.vandemeer:asciitable:0.3.2
- info.picocli:picocli:4.7.7
- org.yaml:snakeyaml:2.5
diff --git a/pom.xml b/pom.xml
index ff05b50..95deeb6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,8 +31,10 @@
UTF-8
11
11
+ 0.11.2
org.codejive.jvm.Main
- 0.3.0
+ 0.4.3
+ 5.6
0.3.2
4.7.7
2.5
@@ -50,6 +52,11 @@
devkitman
${version.devkitman}
+
+ org.apache.httpcomponents.client5
+ httpclient5-cache
+ ${version.httpclient}
+
de.vandermeer
asciitable
@@ -75,7 +82,12 @@
slf4j-simple
${version.slf4j}
-
+
+ org.slf4j
+ jcl-over-slf4j
+ ${version.slf4j}
+
+
org.junit.jupiter
@@ -315,6 +327,40 @@
+
+ native
+
+
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${native.maven.plugin}
+ true
+
+
+ build-native
+
+ compile-no-fork
+
+ package
+
+
+
+ ${mainClass}
+ jvm
+
+ --no-fallback
+ -H:+ReportExceptionStackTraces
+ --enable-url-protocols=http,https
+ --initialize-at-build-time=org.slf4j
+ --initialize-at-run-time=com.google.gson.internal.UnsafeAllocator
+
+
+
+
+
+
+
release
diff --git a/src/main/java/org/codejive/jvm/Main.java b/src/main/java/org/codejive/jvm/Main.java
index 7aa0fe5..487348c 100644
--- a/src/main/java/org/codejive/jvm/Main.java
+++ b/src/main/java/org/codejive/jvm/Main.java
@@ -1,5 +1,5 @@
// spotless:off Dependencies for JBang
-//DEPS dev.jbang:devkitman:0.3.0
+//DEPS dev.jbang:devkitman:0.4.2
//DEPS org.yaml:snakeyaml:2.4
//DEPS info.picocli:picocli:4.7.7
//DEPS de.vandermeer:asciitable:0.3.2
@@ -44,36 +44,39 @@
})
public class Main {
+ static boolean verbose = false;
+ static boolean quiet = false;
+
@Option(
names = {"-h", "--help"},
description = "Show this help message and exit.",
- usageHelp = true)
+ usageHelp = true,
+ scope = CommandLine.ScopeType.INHERIT)
boolean showHelp;
@Option(
- names = {"-v", "--version"},
+ names = {"-q", "--quiet"},
+ description = "We will be quiet, only print when error occurs.",
+ scope = CommandLine.ScopeType.INHERIT)
+ public void setQuiet(boolean quiet) {
+ Main.quiet = quiet;
+ }
+
+ @Option(
+ names = {"-v", "--verbose"},
+ description = "Enable verbose output for debugging",
+ scope = CommandLine.ScopeType.INHERIT)
+ public void setVerbose(boolean verbose) {
+ Main.verbose = verbose;
+ }
+
+ @Option(
+ names = {"-V", "--version"},
description = "Show application version.",
versionHelp = true)
boolean showVersion;
- @Option(
- names = {"--quiet"},
- description = "We will be quiet, only print when error occurs.")
- boolean quiet;
-
- abstract static class CmdBase implements Callable {
- @Option(
- names = {"-h", "--help"},
- description = "Show this help message and exit.",
- usageHelp = true,
- scope = CommandLine.ScopeType.INHERIT)
- boolean showHelp;
-
- @Option(
- names = {"--quiet"},
- description = "We will be quiet, only print when error occurs.")
- boolean quiet;
- }
+ abstract static class CmdBase implements Callable {}
@Command(
name = "list",
@@ -82,7 +85,7 @@ abstract static class CmdBase implements Callable {
static class ListInstalled extends CmdBase {
@Override
public Integer call() {
- JdkManager manager = JdkManager.create();
+ JdkManager manager = createJdkManager();
manager.getOrInstallJdk("11+");
List jdks = manager.listInstalledJdks();
jdks.sort(Comparator.naturalOrder().reversed());
@@ -116,7 +119,7 @@ static class ListAvailable extends CmdBase {
@Override
public Integer call() {
System.err.println("Retrieving available Java versions, this can take a moment...");
- JdkManager manager = JdkManager.create();
+ JdkManager manager = createJdkManager();
List jdks = manager.listAvailableJdks();
jdks.sort(Comparator.naturalOrder().reversed());
@@ -180,7 +183,7 @@ static class Install extends CmdBase {
@Override
public Integer call() {
String versionOrId = javaVersionParamMixin.getVersionOrId(quiet);
- JdkManager jdkMan = JdkManager.create();
+ JdkManager jdkMan = createJdkManager();
Jdk jdk = jdkMan.getInstalledJdk(versionOrId, JdkProvider.Predicates.canUpdate);
if (!force && jdk != null) {
if (!quiet) {
@@ -223,14 +226,14 @@ void setJavaVersion(String versionOrId) {
@Override
public Integer call() {
- JdkManager jdkMan = JdkManager.create();
+ JdkManager jdkMan = createJdkManager();
Jdk.InstalledJdk jdk =
jdkMan.getInstalledJdk(versionOrId, JdkProvider.Predicates.canUpdate);
if (jdk == null) {
System.err.println("Java version not installed: " + versionOrId);
return 1;
}
- jdkMan.uninstallJdk(jdk);
+ jdk.uninstall();
if (!quiet) {
System.err.println("Successfully uninstalled Java version " + versionOrId);
}
@@ -280,7 +283,7 @@ static class Env extends CmdBase {
@Override
public Integer call() {
- JdkManager jdkMan = JdkManager.create();
+ JdkManager jdkMan = createJdkManager();
Jdk jdk = jdkMan.getOrInstallJdk(javaVersionParamMixin.getVersionOrId(quiet));
return 0;
}
@@ -302,7 +305,7 @@ public Integer call() throws IOException {
if (cmd.isEmpty()) {
return 0;
}
- JdkManager jdkMan = JdkManager.create();
+ JdkManager jdkMan = createJdkManager();
Jdk.InstalledJdk jdk =
jdkMan.getOrInstallJdk(javaVersionOptionMixin.getVersionOrId(quiet));
if (Paths.get(cmd.get(0)).getNameCount() == 1) {
@@ -427,6 +430,41 @@ private static boolean isId(String s) {
return s.matches("[a-zA-Z0-9_\\-.]+");
}
+ private static JdkManager createJdkManager() {
+ Path installPath = JBangJdkProvider.getJBangJdkDir();
+ Path cachePath = cachePath();
+ JdkDiscovery.Config cfg = new JdkDiscovery.Config(installPath, cachePath, null);
+ cfg.properties()
+ .put("link", JBangJdkProvider.getJBangConfigDir().resolve("currentjdk").toString());
+ return JdkManager.builder().providers(JdkProviders.instance().all(cfg)).build();
+ }
+
+ private static Path cachePath() {
+ String cachePath = System.getenv("JVM_CACHE");
+ Path cacheDir = null;
+ if (cachePath == null) {
+ cachePath = System.getProperty("jvm.cache");
+ if (cachePath != null) {
+ cacheDir = Paths.get(cachePath);
+ } else {
+ cacheDir = Paths.get(System.getProperty("user.home"), ".cache", "jvm");
+ }
+ }
+ return cacheDir;
+ }
+
+ static CommandLine.IExecutionExceptionHandler errorHandler =
+ (ex, commandLine, parseResult) -> {
+ System.err.println("Error: " + ex.getMessage());
+ if (verbose) {
+ ex.printStackTrace();
+ } else if (!quiet) {
+ System.err.println(
+ "(Run with --verbose for more details. If you believe you found a bug in jpm, open an issue at https://github.com/codejive/java-jpm/issues)");
+ }
+ return commandLine.getCommandSpec().exitCodeOnExecutionException();
+ };
+
/**
* Main entry point for the jvm command line tool.
*
diff --git a/src/main/resources/META-INF/native-image/dev.jbang/devkitman/reflect-config.json b/src/main/resources/META-INF/native-image/dev.jbang/devkitman/reflect-config.json
new file mode 100644
index 0000000..10cafd2
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/dev.jbang/devkitman/reflect-config.json
@@ -0,0 +1,183 @@
+[
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$VersionsResponse",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$JdkResult",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$JdkResultLinks",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$AvailableFoojayJdk",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.MetadataJdkInstaller",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.MetadataJdkInstaller$MetadataResult",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.MetadataJdkInstaller$AvailableMetadataJdk",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.JdkProvider",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.JdkProviders",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "java.util.ArrayList",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "java.util.LinkedHashMap",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "java.util.HashMap",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "java.lang.String",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.DefaultJdkProvider",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.DefaultJdkProvider$AvailableDefaultJdk",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.JBangJdkProvider",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.SdkmanJdkProvider",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.LinkedJdkProvider",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "unsafeAllocated": true
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.LinkedJdkProvider$AvailableLinkedJdk",
+ "allDeclaredConstructors": true,
+ "allPublicConstructors": true,
+ "allDeclaredMethods": true,
+ "allPublicMethods": true,
+ "allDeclaredFields": true,
+ "allPublicFields": true,
+ "unsafeAllocated": true
+ }
+]
+
diff --git a/src/main/resources/META-INF/native-image/native-image.properties b/src/main/resources/META-INF/native-image/native-image.properties
new file mode 100644
index 0000000..a127131
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/native-image.properties
@@ -0,0 +1,5 @@
+Args = --no-fallback \
+ -H:+ReportExceptionStackTraces \
+ --enable-url-protocols=http,https \
+ --initialize-at-build-time=org.slf4j \
+ --initialize-at-run-time=com.google.gson.internal.UnsafeAllocator
diff --git a/src/main/resources/META-INF/native-image/resource-config.json b/src/main/resources/META-INF/native-image/resource-config.json
new file mode 100644
index 0000000..da828d8
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/resource-config.json
@@ -0,0 +1,17 @@
+{
+ "resources": {
+ "includes": [
+ {
+ "pattern": ".*\\.properties$"
+ },
+ {
+ "pattern": ".*\\.yml$"
+ },
+ {
+ "pattern": ".*\\.yaml$"
+ }
+ ]
+ },
+ "bundles": []
+}
+
diff --git a/src/main/resources/META-INF/native-image/serialization-config.json b/src/main/resources/META-INF/native-image/serialization-config.json
new file mode 100644
index 0000000..736c6e8
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/serialization-config.json
@@ -0,0 +1,27 @@
+[
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$VersionsResponse"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$JdkResult"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$JdkResultLinks"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.FoojayJdkInstaller$AvailableFoojayJdk"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.MetadataJdkInstaller$MetadataResult"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkinstallers.MetadataJdkInstaller$AvailableMetadataJdk"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.DefaultJdkProvider$AvailableDefaultJdk"
+ },
+ {
+ "name": "dev.jbang.devkitman.jdkproviders.LinkedJdkProvider$AvailableLinkedJdk"
+ }
+]
+