Skip to content

Commit 9f510d6

Browse files
committed
feat: install multiple JDKs with same major version
Fixes #2118
1 parent 4421f5a commit 9f510d6

File tree

6 files changed

+154
-56
lines changed

6 files changed

+154
-56
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ sourceSets {
130130
sourceSets.main.compileClasspath += sourceSets.java9.output.classesDirs;
131131

132132
dependencies {
133-
implementation 'dev.jbang:devkitman:0.3.0'
133+
implementation 'dev.jbang:devkitman:0.3.0.6-SNAPSHOT'
134134
implementation 'org.jspecify:jspecify:1.0.0'
135135
implementation 'org.apache.commons:commons-text:1.11.0'
136136
implementation 'org.apache.commons:commons-compress:1.27.1'

src/main/java/dev/jbang/Cache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static void clearCache(CacheClass... classes) {
3030
// We're running using a managed JDK on Windows so we can't just delete the
3131
// entire folder!
3232
for (Jdk.InstalledJdk jdk : jdkMan.listInstalledJdks()) {
33-
jdkMan.uninstallJdk(jdk);
33+
jdk.uninstall();
3434
}
3535
}
3636
if (cc == CacheClass.deps) {

src/main/java/dev/jbang/cli/Jdk.java

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ public Integer install(
4242
@CommandLine.Parameters(paramLabel = "existingJdkPath", index = "1", description = "Pre installed JDK path", arity = "0..1") String path)
4343
throws IOException {
4444
JdkManager jdkMan = jdkProvidersMixin.getJdkManager();
45-
dev.jbang.devkitman.Jdk jdk = jdkMan.getInstalledJdk(versionOrId, JdkProvider.Predicates.canUpdate);
45+
dev.jbang.devkitman.Jdk jdk = jdkMan.getInstalledJdk(versionOrId, JdkProvider.Predicates.canInstall);
4646
if (force || jdk == null) {
4747
if (!Util.isNullOrBlankString(path)) {
4848
jdkMan.linkToExistingJdk(Paths.get(path), versionOrId);
4949
} else {
5050
if (jdk == null) {
51-
jdk = jdkMan.getJdk(versionOrId, JdkProvider.Predicates.canUpdate);
51+
jdk = jdkMan.getJdk(versionOrId, JdkProvider.Predicates.canInstall);
5252
if (jdk == null) {
5353
throw new IllegalArgumentException("JDK is not available for installation: " + versionOrId);
5454
}
@@ -67,14 +67,15 @@ public Integer install(
6767
@CommandLine.Command(name = "list", aliases = "l", description = "Lists installed JDKs.")
6868
public Integer list(
6969
@CommandLine.Option(names = {
70-
"--available" }, description = "Shows versions available for installation") boolean available,
70+
"--available", "-a" }, description = "Shows versions available for installation") boolean available,
7171
@CommandLine.Option(names = {
72-
"--show-details" }, description = "Shows detailed information for each JDK (only when format=text)") boolean details,
72+
"--show-details", "--details",
73+
"-d" }, description = "Shows detailed information for each JDK (only when format=text)") boolean details,
7374
@CommandLine.Option(names = {
7475
"--format" }, description = "Specify output format ('text' or 'json')") FormatMixin.Format format) {
7576
JdkManager jdkMan = jdkProvidersMixin.getJdkManager();
7677
dev.jbang.devkitman.Jdk defaultJdk = jdkMan.getDefaultJdk();
77-
int defMajorVersion = defaultJdk != null ? defaultJdk.majorVersion() : 0;
78+
String defVersion = defaultJdk != null ? defaultJdk.version() : "";
7879
PrintStream out = System.out;
7980
List<? extends dev.jbang.devkitman.Jdk> jdks;
8081
if (available) {
@@ -86,11 +87,12 @@ public Integer list(
8687
.map(jdk -> new JdkOut(jdk.id(), jdk.version(), jdk.provider().name(),
8788
jdk.isInstalled() ? ((dev.jbang.devkitman.Jdk.InstalledJdk) jdk).home() : null,
8889
details ? jdk.equals(defaultJdk)
89-
: jdk.majorVersion() == defMajorVersion))
90+
: jdk.version().equals(defVersion),
91+
jdk.tags()))
9092
.collect(Collectors.toList());
9193
if (!details) {
92-
// Only keep a list of unique major versions
93-
Set<JdkOut> uniqueJdks = new TreeSet<>(Comparator.comparingInt(j -> j.version));
94+
// Only keep a list of unique versions
95+
Set<JdkOut> uniqueJdks = new TreeSet<>(Comparator.<JdkOut>comparingInt(j -> j.version).reversed());
9496
uniqueJdks.addAll(jdkOuts);
9597
jdkOuts = new ArrayList<>(uniqueJdks);
9698
}
@@ -106,12 +108,17 @@ public Integer list(
106108
out.print(" ");
107109
out.print(jdk.version);
108110
out.print(" (");
109-
out.print(jdk.fullVersion);
110111
if (details) {
112+
out.print(jdk.fullVersion);
111113
out.print(", " + jdk.providerName + ", " + jdk.id);
112114
if (jdk.javaHomeDir != null) {
113115
out.print(", " + jdk.javaHomeDir);
114116
}
117+
if (!jdk.tags.isEmpty()) {
118+
out.print(", " + jdk.tags);
119+
}
120+
} else {
121+
out.print(available ? jdk.id : jdk.fullVersion);
115122
}
116123
out.print(")");
117124
if (!available) {
@@ -134,20 +141,40 @@ static class JdkOut implements Comparable<JdkOut> {
134141
String fullVersion;
135142
String providerName;
136143
String javaHomeDir;
144+
String realHomeDir;
137145
@SerializedName("default")
138146
Boolean isDefault;
147+
Set<String> tags;
139148

140-
public JdkOut(String id, String version, String providerName, Path home, boolean isDefault) {
149+
public JdkOut(String id, String version, String providerName, Path home, boolean isDefault, Set<String> tags) {
141150
this.id = id;
142151
this.version = JavaUtil.parseJavaVersion(version);
143152
this.fullVersion = version;
144153
this.providerName = providerName;
145154
if (home != null) {
146155
this.javaHomeDir = home.toString();
156+
this.realHomeDir = home.toString();
157+
try {
158+
this.realHomeDir = home.toRealPath().toString();
159+
} catch (IOException e) {
160+
// Ignore
161+
}
147162
}
148163
if (isDefault) {
149164
this.isDefault = true;
150165
}
166+
this.tags = tags != null ? trimTags(tags) : Collections.emptySet();
167+
}
168+
169+
private Set<String> trimTags(Set<String> tags) {
170+
Set<String> trimmedTags = new HashSet<>();
171+
for (String tag : tags) {
172+
if (!tag.equalsIgnoreCase("ga")
173+
&& !tag.equalsIgnoreCase("jdk")) {
174+
trimmedTags.add(tag.toLowerCase());
175+
}
176+
}
177+
return trimmedTags;
151178
}
152179

153180
@Override
@@ -162,15 +189,20 @@ public int compareTo(JdkOut o) {
162189

163190
@CommandLine.Command(name = "uninstall", aliases = "u", description = "Uninstalls an existing JDK.")
164191
public Integer uninstall(
165-
@CommandLine.Parameters(paramLabel = "version", index = "0", description = "The version to install", arity = "1") String versionOrId) {
192+
@CommandLine.Parameters(paramLabel = "versionOrId", index = "0", description = "The version to install", arity = "1") String versionOrId) {
166193
JdkManager jdkMan = jdkProvidersMixin.getJdkManager();
194+
// This will first select for JDKs from providers that can actually install JDKs
167195
dev.jbang.devkitman.Jdk.InstalledJdk jdk = jdkMan.getInstalledJdk(versionOrId,
168-
JdkProvider.Predicates.canUpdate);
196+
JdkProvider.Predicates.canInstall);
169197
if (jdk == null) {
170-
throw new ExitException(EXIT_INVALID_INPUT, "JDK " + versionOrId + " is not installed");
198+
// If necessary we select JDKs from providers that can update JDKs
199+
jdk = jdkMan.getInstalledJdk(versionOrId, JdkProvider.Predicates.canUpdate);
200+
if (jdk == null) {
201+
throw new ExitException(EXIT_INVALID_INPUT, "JDK " + versionOrId + " is not installed");
202+
}
171203
}
172-
jdkMan.uninstallJdk(jdk);
173-
Util.infoMsg("Uninstalled JDK:\n " + versionOrId);
204+
jdk.uninstall();
205+
Util.infoMsg("Uninstalled JDK:\n " + jdk.id());
174206
return EXIT_OK;
175207
}
176208

@@ -193,7 +225,7 @@ public Integer javaEnv(
193225
JdkManager jdkMan = jdkProvidersMixin.getJdkManager();
194226
dev.jbang.devkitman.Jdk jdk = null;
195227
if (versionOrId != null && JavaUtil.isRequestedVersion(versionOrId)) {
196-
jdk = jdkMan.getJdk(versionOrId, JdkProvider.Predicates.canUpdate);
228+
jdk = jdkMan.getJdk(versionOrId, JdkProvider.Predicates.canInstall);
197229
}
198230
if (jdk == null || !jdk.isInstalled()) {
199231
jdk = jdkMan.getOrInstallJdk(versionOrId);
@@ -273,7 +305,12 @@ public Integer exec(
273305

274306
@CommandLine.Command(name = "default", description = "Sets the default JDK to be used by JBang.")
275307
public Integer defaultJdk(
276-
@CommandLine.Parameters(paramLabel = "version", index = "0", description = "The version of the JDK to select", arity = "0..1") String versionOrId) {
308+
@CommandLine.Parameters(paramLabel = "versionOrId", index = "0", description = "The version of the JDK to select", arity = "0..1") String versionOrId,
309+
@CommandLine.Option(names = {
310+
"--show-details", "--details",
311+
"-d" }, description = "Shows detailed information for each JDK (only when format=text)") boolean details,
312+
@CommandLine.Option(names = {
313+
"--format" }, description = "Specify output format ('text' or 'json')") FormatMixin.Format format) {
277314
JdkManager jdkMan = jdkProvidersMixin.getJdkManager();
278315
if (!jdkMan.hasDefaultProvider()) {
279316
Util.warnMsg("Cannot perform operation, the 'default' provider was not found");
@@ -288,13 +325,41 @@ public Integer defaultJdk(
288325
Util.infoMsg("Default JDK already set to " + defjdk.majorVersion());
289326
}
290327
} else {
291-
if (defjdk == null) {
292-
Util.infoMsg("No default JDK set, use 'jbang jdk default <version>' to set one.");
328+
List<dev.jbang.devkitman.Jdk.InstalledJdk> jdks = jdkMan.listDefaultJdks();
329+
List<JdkOut> jdkOuts = jdks.stream()
330+
.map(jdk -> new JdkOut(jdk.id(), jdk.version(), jdk.provider().name(), jdk.home(),
331+
jdk.id().equals("default"), jdk.tags()))
332+
.collect(Collectors.toList());
333+
PrintStream out = System.out;
334+
if (format == FormatMixin.Format.json) {
335+
Gson parser = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();
336+
parser.toJson(jdkOuts, out);
293337
} else {
294-
Util.infoMsg("Default JDK is currently set to " + defjdk.majorVersion());
338+
if (!jdkOuts.isEmpty()) {
339+
out.println("Default JDKs:");
340+
jdkOuts.forEach(jdk -> {
341+
out.print(" ");
342+
if (Boolean.TRUE.equals(jdk.isDefault)) {
343+
out.print("*");
344+
} else {
345+
out.print(jdk.version);
346+
}
347+
out.print(" -> ");
348+
out.print(jdk.realHomeDir);
349+
if (details) {
350+
out.print(" (" + jdk.fullVersion + ", " + jdk.id);
351+
if (!jdk.tags.isEmpty()) {
352+
out.print(", " + jdk.tags);
353+
}
354+
out.print(")");
355+
}
356+
out.println();
357+
});
358+
} else {
359+
out.println("No default JDK set, use 'jbang jdk default <version>' to set one.");
360+
}
295361
}
296362
}
297363
return EXIT_OK;
298364
}
299-
300365
}

src/main/java/dev/jbang/cli/JdkProvidersMixin.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
public class JdkProvidersMixin {
1212

1313
@CommandLine.Option(names = {
14-
"--jdk-providers" }, description = "Use the given providers to check for installed JDKs", split = ",", hidden = true)
14+
"--jdk-providers" }, description = "Use the given providers to manage JDKs", split = ",", hidden = true)
1515
List<String> jdkProviders;
1616

17+
@CommandLine.Option(names = {
18+
"--jdk-vendors" }, description = "Use the given vendors/distributions to install new JDKs", split = ",", hidden = true)
19+
List<String> jdkVendors;
20+
1721
private JdkManager jdkMan;
1822

1923
protected JdkManager getJdkManager() {
2024
if (jdkMan == null) {
21-
jdkMan = JavaUtil.defaultJdkManager(jdkProviders);
25+
jdkMan = JavaUtil.defaultJdkManager(jdkProviders, jdkVendors);
2226
}
2327
return jdkMan;
2428
}
@@ -31,6 +35,12 @@ public List<String> opts() {
3135
opts.add(p);
3236
}
3337
}
38+
if (jdkVendors != null) {
39+
for (String v : jdkVendors) {
40+
opts.add("--jdk-vendors");
41+
opts.add(v);
42+
}
43+
}
3444
return opts;
3545
}
3646
}

src/main/java/dev/jbang/util/JavaUtil.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,26 @@ public static JdkManager defaultJdkManager(String... names) {
3737
}
3838

3939
@NonNull
40-
public static JdkManager defaultJdkManager(List<String> names) {
40+
public static JdkManager defaultJdkManager(List<String> providers, List<String> vendors) {
4141
return (new JdkManBuilder())
42-
.provider(names)
43-
.defaultJavaVersion(Settings.getDefaultJavaVersion())
44-
.build();
42+
.provider(providers)
43+
.vendor(vendors)
44+
.defaultJavaVersion(Settings.getDefaultJavaVersion())
45+
.build();
4546
}
4647

4748
public static class JdkManBuilder extends JdkManager.Builder {
4849
private final List<String> providerNames = new ArrayList<>();
50+
private final List<String> vendorNames = new ArrayList<>();
4951

5052
public static final List<String> PROVIDERS_ALL = JdkProviders.instance().allNames();
5153
public static final List<String> PROVIDERS_DEFAULT = JdkProviders.instance().basicNames();
5254

53-
public JdkManager.Builder provider(String... names) {
55+
public JdkManBuilder provider(String... names) {
5456
return provider(names != null ? Arrays.asList(names) : null);
5557
}
5658

57-
public JdkManager.Builder provider(List<String> names) {
59+
public JdkManBuilder provider(List<String> names) {
5860
if (names != null) {
5961
for (String providerName : names) {
6062
if (PROVIDERS_ALL.contains(providerName)) {
@@ -67,6 +69,17 @@ public JdkManager.Builder provider(List<String> names) {
6769
return this;
6870
}
6971

72+
public JdkManBuilder vendor(String... names) {
73+
return vendor(names != null ? Arrays.asList(names) : null);
74+
}
75+
76+
public JdkManBuilder vendor(List<String> names) {
77+
if (names != null) {
78+
vendorNames.addAll(names);
79+
}
80+
return this;
81+
}
82+
7083
public JdkManager build() {
7184
if (providerNames.isEmpty() && providers.isEmpty()) {
7285
Util.verboseMsg("No JDK providers specified, using default providers");
@@ -86,7 +99,7 @@ public JdkManager build() {
8699

87100
}
88101

89-
if (providers.size() == 0) {
102+
if (providers.isEmpty()) {
90103
Util.warnMsg("No JDK providers selected or available. Run with --verbose for more details.");
91104
Util.verboseMsg("Available JDK providers: " + PROVIDERS_ALL);
92105
}
@@ -97,13 +110,19 @@ private JdkProvider createProvider(String providerName) {
97110
JdkProvider provider;
98111
switch (providerName) {
99112
case "default":
100-
provider = new DefaultJdkProvider(Settings.getDefaultJdkDir());
113+
provider = new DefaultJdkProvider(Settings.getDefaultJdkDir(),
114+
Settings.getCacheDir(Cache.CacheClass.jdks));
101115
break;
102116
case "jbang":
103117
JBangJdkProvider p = new JBangJdkProvider();
104-
p.installer(new FoojayJdkInstaller(p, p::jdkId)
105-
.distro(Util.getVendor())
106-
.remoteAccessProvider(new JBangRemoteAccessProvider()));
118+
String distro = Util.getVendor();
119+
if (!vendorNames.isEmpty()) {
120+
distro = String.join(",", vendorNames);
121+
}
122+
FoojayJdkInstaller installer = new FoojayJdkInstaller(p)
123+
.distro(distro)
124+
.remoteAccessProvider(new JBangRemoteAccessProvider());
125+
p.installer(installer);
107126
provider = p;
108127
break;
109128
default:

0 commit comments

Comments
 (0)