Skip to content

Commit 9ce9487

Browse files
Revamp the JavaFX locator (#1023)
* Revamp JavaFX lib lookup - add Java version 16 and 17 paths for linux - add checking parent directory of `jre` and `jdk` folders for windows (relevant for ojdkbuild) - add home directory installation `.chunky` check - add checks for lib directory and parent directory - add eager check for directories named `*javafx*` in some locations * add/update license headers * Bump launcher version (patch -> 1.12.2)
1 parent 06340ec commit 9ce9487

2 files changed

Lines changed: 131 additions & 30 deletions

File tree

launcher/src/se/llbit/chunky/launcher/ChunkyLauncher.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* Copyright (c) 2013-2016 Jesper Öqvist <jesper@llbit.se>
2+
* Copyright (c) 2016-2021 Chunky contributors
23
*
34
* This file is part of Chunky.
45
*
@@ -58,7 +59,7 @@
5859
*/
5960
public class ChunkyLauncher {
6061

61-
public static final String LAUNCHER_VERSION = "v1.12.1";
62+
public static final String LAUNCHER_VERSION = "v1.12.2";
6263

6364
/**
6465
* Print a launch error message to the console.
@@ -239,7 +240,7 @@ else if(!settings.javaOptions.contains(args[i+1]))
239240
}
240241
} catch(NoClassDefFoundError e) {
241242
String cause = e.getMessage();
242-
if(cause.contains("javafx")) {
243+
if(cause != null && cause.contains("javafx")) {
243244
// Javafx error
244245
if(retryIfMissingJavafx)
245246
JavaFxLocator.retryWithJavafx(args);

launcher/src/se/llbit/chunky/launcher/JavaFxLocator.java

Lines changed: 128 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,135 @@
1+
/* Copyright (c) 2021 Chunky contributors
2+
*
3+
* This file is part of Chunky.
4+
*
5+
* Chunky is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* Chunky is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
* You should have received a copy of the GNU General Public License
15+
* along with Chunky. If not, see <http://www.gnu.org/licenses/>.
16+
*/
117
package se.llbit.chunky.launcher;
218

319
import java.io.File;
420
import java.io.IOException;
521
import java.lang.management.ManagementFactory;
622
import java.net.URISyntaxException;
23+
import java.nio.file.DirectoryStream;
24+
import java.nio.file.Files;
725
import java.nio.file.Path;
26+
import java.nio.file.Paths;
827
import java.util.ArrayList;
928
import java.util.Arrays;
10-
import java.util.List;
29+
import java.util.LinkedHashSet;
1130

1231
public class JavaFxLocator {
1332

33+
/**
34+
* Lazy set of absolute paths to JavaFX library folders.
35+
* Filled using {@link #scanForJavaFXLibs()}.
36+
*/
37+
private static final LinkedHashSet<Path> javafxPathCandidates = new LinkedHashSet<>();
1438

15-
private static final List<String> javafxPathCandidates;
39+
private static void scanForJavaFXLibs() {
40+
// working directory (local lib overwrites installed system libs)
41+
addJavaFXPathIfValid(getJavaFXPathBySystemProperty("user.dir"));
42+
try {
43+
// directory of the .jar
44+
File executableFile = new File(
45+
ChunkyLauncher.class.getProtectionDomain().getCodeSource().getLocation().toURI());
46+
addJavaFXPathIfValid(executableFile.toPath().getParent());
47+
} catch(URISyntaxException ignored) {
48+
}
49+
50+
// home directory installation
51+
Path userHomePath = getJavaFXPathBySystemProperty("user.home");
52+
if(userHomePath != null) {
53+
addJavaFXPathIfValid(userHomePath.resolve(".chunky"), true);
54+
}
1655

17-
static {
18-
javafxPathCandidates = new ArrayList<>();
19-
javafxPathCandidates.add("C:\\Program Files\\openjfx\\lib");
20-
javafxPathCandidates.add("/usr/share/openjfx/lib");
21-
javafxPathCandidates.add("/usr/lib/jvm/java-11-openjdk/lib");
22-
javafxPathCandidates.add("/usr/lib/jvm/java-12-openjdk/lib");
23-
javafxPathCandidates.add("/usr/lib/jvm/java-13-openjdk/lib");
24-
javafxPathCandidates.add("/usr/lib/jvm/java-14-openjdk/lib");
25-
javafxPathCandidates.add("/usr/lib/jvm/java-15-openjdk/lib");
56+
// java home
57+
Path javaHomePath = getJavaFXPathBySystemProperty("java.home");
58+
addJavaFXPathIfValid(javaHomePath);
59+
60+
if(System.getProperty("os.name").startsWith("Windows")) {
61+
// windows paths
62+
if(javaHomePath != null &&
63+
(javaHomePath.endsWith("jre") || javaHomePath.endsWith("jdk"))) {
64+
// if jre is in a subfolder of the jdk, try jdks lib path (example: OpenJDK from odjkbuild)
65+
addJavaFXPathIfValid(javaHomePath.getParent(), true);
66+
}
67+
addJavaFXPathIfValid("C:\\Program Files\\openjfx");
68+
} else {
69+
// linux paths
70+
addJavaFXPathIfValid("/usr/share/openjfx");
71+
for(int javaVersion = 11; javaVersion <= 17; javaVersion++) {
72+
addJavaFXPathIfValid("/usr/lib/jvm/java-" + javaVersion + "-openjdk");
73+
}
74+
}
75+
}
2676

27-
javafxPathCandidates.add(System.getProperty("java.home", "") + File.separator + "lib"); // java home
28-
javafxPathCandidates.add(System.getProperty("user.dir", "") + File.separator + "lib"); // working directory
77+
private static Path getJavaFXPathBySystemProperty(String propertyName) {
2978
try {
30-
javafxPathCandidates.add(new File(ChunkyLauncher.class.getProtectionDomain().getCodeSource().getLocation().toURI())
31-
.toPath().getParent().toAbsolutePath().toString() + File.separator + "lib"); // directory of the .jar
32-
} catch(URISyntaxException e) {
33-
e.printStackTrace();
79+
String propertyValue = System.getProperty(propertyName);
80+
if(propertyValue != null) {
81+
return Paths.get(propertyValue);
82+
}
83+
} catch(SecurityException ignored) {
84+
}
85+
return null;
86+
}
87+
88+
89+
private static void addJavaFXPathIfValid(String path) {
90+
addJavaFXPathIfValid(Paths.get(path), false);
91+
}
92+
93+
private static void addJavaFXPathIfValid(Path path) {
94+
addJavaFXPathIfValid(path, false);
95+
}
96+
97+
/**
98+
* Validates the path to contain the required JavaFX library files.
99+
* Retests the path with lib appended, if the initial test does not find the files.
100+
*
101+
* @param path path to library files, gets converted to absolute while following links
102+
* @return true if path contains required files
103+
*/
104+
private static boolean addJavaFXPathIfValid(Path path, boolean searchJavaFXSDK) {
105+
try {
106+
// get absolute path, follow links
107+
path = path.toRealPath();
108+
109+
if(isValidJavaFXDirectory(path)) {
110+
javafxPathCandidates.add(path);
111+
return true;
112+
}
113+
if(!path.endsWith("lib")) {
114+
if(addJavaFXPathIfValid(path.resolve("lib"), searchJavaFXSDK)) {
115+
return true;
116+
}
117+
}
118+
if(searchJavaFXSDK) {
119+
try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, subPath -> subPath.toFile().isDirectory())) {
120+
for(Path subPath : directoryStream) {
121+
if(subPath.getFileName().toString().contains("javafx")) {
122+
addJavaFXPathIfValid(subPath);
123+
}
124+
}
125+
}
126+
}
127+
} catch(IOException ignored) {
34128
}
129+
return false;
35130
}
36131

37-
private static boolean validJavafxDirectory(Path dir) {
132+
private static boolean isValidJavaFXDirectory(Path dir) {
38133
return dir.toFile().exists()
39134
&& dir.resolve("javafx.base.jar").toFile().exists()
40135
&& dir.resolve("javafx.controls.jar").toFile().exists()
@@ -48,7 +143,7 @@ private static void runWithJavafx(Path javafxDir, String[] args) {
48143
cmd.add(JreUtil.javaCommand(""));
49144
cmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
50145
cmd.add("--module-path");
51-
cmd.add(javafxDir.toAbsolutePath().toString());
146+
cmd.add(javafxDir.toString());
52147
cmd.add("--add-modules");
53148
cmd.add("javafx.controls,javafx.fxml");
54149

@@ -61,16 +156,19 @@ private static void runWithJavafx(Path javafxDir, String[] args) {
61156
cmd.add("--javaOptions");
62157
StringBuilder javaOptions = new StringBuilder();
63158
javaOptions.append("--module-path ");
64-
if (System.getProperty("os.name").startsWith("Windows")) {
65-
// Escape the path twice to make the second launcher pass the options to Chunky retaining the double speechmarks (fixes paths with spaces)
66-
javaOptions.append("\\\"" + javafxDir.toAbsolutePath().toString() + "\\\"");
159+
if(System.getProperty("os.name").startsWith("Windows")) {
160+
// Escape the path twice to make the second launcher pass the options to Chunky retaining the double quotation marks (fixes paths with spaces)
161+
javaOptions.append("\\\"").append(javafxDir.toAbsolutePath()).append("\\\"");
67162
} else {
68-
javaOptions.append(javafxDir.toAbsolutePath().toString());
163+
javaOptions.append(javafxDir);
69164
}
70165
javaOptions.append(" --add-modules ");
71166
javaOptions.append("javafx.controls,javafx.fxml");
72167
cmd.add(javaOptions.toString());
73168

169+
System.out.println("Trying to start the chunky process with the following arguments:");
170+
System.out.println(String.join(" ", cmd));
171+
74172
ProcessBuilder builder = new ProcessBuilder(cmd);
75173
builder.inheritIO();
76174
try {
@@ -82,11 +180,13 @@ private static void runWithJavafx(Path javafxDir, String[] args) {
82180
}
83181

84182
public static void retryWithJavafx(String[] args) {
85-
for(String candiate : javafxPathCandidates) {
86-
Path directory = new File(candiate).toPath();
87-
if(validJavafxDirectory(directory)) {
88-
runWithJavafx(directory, args);
89-
}
183+
if(javafxPathCandidates.isEmpty()) {
184+
scanForJavaFXLibs();
185+
System.out.println("JavaFX scan found the following candidates:");
186+
javafxPathCandidates.forEach(System.out::println);
187+
}
188+
for(Path pathCandiate : javafxPathCandidates) {
189+
runWithJavafx(pathCandiate, args);
90190
}
91191
}
92192

0 commit comments

Comments
 (0)