Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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).
2 changes: 1 addition & 1 deletion app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
50 changes: 48 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<native.maven.plugin>0.11.2</native.maven.plugin>
<mainClass>org.codejive.jvm.Main</mainClass>
<version.devkitman>0.3.0</version.devkitman>
<version.devkitman>0.4.3</version.devkitman>
<version.httpclient>5.6</version.httpclient>
<version.asciitable>0.3.2</version.asciitable>
<version.picocli>4.7.7</version.picocli>
<version.snakeyaml>2.5</version.snakeyaml>
Expand All @@ -50,6 +52,11 @@
<artifactId>devkitman</artifactId>
<version>${version.devkitman}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5-cache</artifactId>
<version>${version.httpclient}</version>
</dependency>
<dependency>
<groupId>de.vandermeer</groupId>
<artifactId>asciitable</artifactId>
Expand All @@ -75,7 +82,12 @@
<artifactId>slf4j-simple</artifactId>
<version>${version.slf4j}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${version.slf4j}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down Expand Up @@ -315,6 +327,40 @@
</build>

<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<mainClass>${mainClass}</mainClass>
<imageName>jvm</imageName>
<buildArgs>
<buildArg>--no-fallback</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
<buildArg>--enable-url-protocols=http,https</buildArg>
<buildArg>--initialize-at-build-time=org.slf4j</buildArg>
<buildArg>--initialize-at-run-time=com.google.gson.internal.UnsafeAllocator</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>

<profile>
<id>release</id>
<properties>
Expand Down
94 changes: 66 additions & 28 deletions src/main/java/org/codejive/jvm/Main.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<Integer> {
@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<Integer> {}

@Command(
name = "list",
Expand All @@ -82,7 +85,7 @@ abstract static class CmdBase implements Callable<Integer> {
static class ListInstalled extends CmdBase {
@Override
public Integer call() {
JdkManager manager = JdkManager.create();
JdkManager manager = createJdkManager();
manager.getOrInstallJdk("11+");
List<Jdk.InstalledJdk> jdks = manager.listInstalledJdks();
jdks.sort(Comparator.<Jdk>naturalOrder().reversed());
Expand Down Expand Up @@ -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<Jdk.AvailableJdk> jdks = manager.listAvailableJdks();
jdks.sort(Comparator.<Jdk>naturalOrder().reversed());

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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.
*
Expand Down
Loading