1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.bcel.generic;
21
22 import static com.sun.jna.platform.win32.WinReg.HKEY_LOCAL_MACHINE;
23
24 import java.io.File;
25 import java.nio.file.FileVisitOption;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.nio.file.attribute.BasicFileAttributes;
30 import java.util.HashSet;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.function.BiPredicate;
34 import java.util.jar.JarEntry;
35 import java.util.jar.JarFile;
36 import java.util.stream.Stream;
37
38 import org.apache.bcel.classfile.JavaClass;
39 import org.apache.bcel.classfile.Module;
40 import org.apache.bcel.classfile.Utility;
41 import org.apache.bcel.util.ModularRuntimeImage;
42 import org.apache.commons.io.function.Uncheck;
43 import org.apache.commons.lang3.StringUtils;
44 import org.apache.commons.lang3.SystemUtils;
45
46 import com.sun.jna.platform.win32.Advapi32Util;
47
48
49
50
51 public class JavaHome {
52
53 private static final String EXTRA_JAVA_HOMES = "ExtraJavaHomes";
54
55
56 private static final String EXTRA_JAVA_ROOT = "ExtraJavaRoot";
57
58
59 private static final String ADOPTIUM_WINDOWS = "C:/Program Files/Eclipse Adoptium/";
60
61
62 private static final String ORACLE_WINDOWS = "C:/Program Files/Java/";
63
64 private static final String EXTRA_JAVA_ROOT_DEFAULT = ADOPTIUM_WINDOWS + File.pathSeparator + ORACLE_WINDOWS;
65
66 private static final String KEY_JDK = "SOFTWARE\\JavaSoft\\Java Development Kit";
67 private static final String KEY_JDK_9 = "SOFTWARE\\JavaSoft\\JDK";
68 private static final String KEY_JRE = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
69 private static final String KEY_JRE_9 = "SOFTWARE\\JavaSoft\\JRE";
70
71 private static Stream<Path> find(final Path start, final int maxDepth, final BiPredicate<Path, BasicFileAttributes> matcher,
72 final FileVisitOption... options) {
73
74 return Files.exists(start) ? Uncheck.apply(Files::find, start, maxDepth, matcher, options) : Stream.empty();
75 }
76
77 private static JavaHome from(final String javaHome) {
78 return new JavaHome(Paths.get(javaHome));
79 }
80
81 private static Stream<String> streamAllWindowsJavaHomes(final String keyJre) {
82 if (Advapi32Util.registryKeyExists(HKEY_LOCAL_MACHINE, keyJre)) {
83 return streamWindowsJavaHomes(keyJre, Advapi32Util.registryGetKeys(HKEY_LOCAL_MACHINE, keyJre));
84 }
85 return Stream.empty();
86 }
87
88 private static Stream<String> streamFromCustomKey(final String key, final String defaultValue) {
89 return streamPropertyAndEnvVarValues(key, defaultValue).flatMap(s -> find(Paths.get(s), 1, (p, a) -> Files.isDirectory(p)).map(Path::toString));
90 }
91
92 private static Stream<String> streamFromCustomKeys() {
93 final String defaultRoot = SystemUtils.IS_OS_WINDOWS ? EXTRA_JAVA_ROOT_DEFAULT : null;
94 return Stream.concat(streamPropertyAndEnvVarValues(EXTRA_JAVA_HOMES, null), streamFromCustomKey(EXTRA_JAVA_ROOT, defaultRoot));
95 }
96
97
98
99
100
101
102 public static Stream<JarEntry> streamJarEntry() {
103 return streamJavaHome().flatMap(JavaHome::streamJarEntryByExt);
104 }
105
106
107
108
109
110
111 public static Stream<JarEntry> streamJarEntryClass() {
112 return streamJavaHome().flatMap(JavaHome::streamJarEntryByExtClass);
113 }
114
115
116
117
118
119
120 public static Stream<String> streamJarEntryClassName() {
121 return streamJavaHome().flatMap(JavaHome::streamJarEntryByExtClassName);
122 }
123
124
125
126
127
128
129 public static Stream<JarFile> streamJarFile() {
130 return streamJavaHome().flatMap(JavaHome::streamJarFileByExt);
131 }
132
133
134
135
136
137
138 public static Stream<Path> streamJarPath() {
139 return streamJavaHome().flatMap(JavaHome::streamJarPathByExt);
140 }
141
142
143
144
145
146
147 public static Stream<JavaHome> streamJavaHome() {
148 return streamJavaHomeString().map(JavaHome::from);
149 }
150
151
152
153
154
155
156 public static Stream<String> streamJavaHomeString() {
157 final Stream<String> streamW = SystemUtils.IS_OS_WINDOWS ? streamWindowsStrings() : Stream.empty();
158 final Stream<String> streamK = Stream.concat(streamW, streamFromCustomKeys());
159 final Stream<String> streamJ = StringUtils.isEmpty(SystemUtils.JAVA_HOME) ? Stream.empty() : Stream.of(SystemUtils.JAVA_HOME);
160 return Stream.concat(streamK, streamJ);
161 }
162
163
164
165
166
167
168 public static Stream<ModularRuntimeImage> streamModularRuntimeImage() {
169 return streamJavaHome().map(JavaHome::getModularRuntimeImage);
170 }
171
172
173
174
175
176
177 public static Stream<Path> streamModulePath() {
178 return streamJavaHome().flatMap(JavaHome::streamModuleByExt);
179 }
180
181 private static Stream<String> streamPropertyAndEnvVarValues(final String key, final String defaultValue) {
182 return Stream.concat(toPathStringStream(System.getProperty(key, defaultValue)), toPathStringStream(System.getenv(key)));
183 }
184
185 private static Stream<String> streamWindowsJavaHomes(final String keyJavaHome, final String[] keys) {
186 final Set<String> javaHomes = new HashSet<>(keys.length);
187 for (final String key : keys) {
188 if (Advapi32Util.registryKeyExists(HKEY_LOCAL_MACHINE, keyJavaHome + "\\" + key)) {
189 final String javaHome = Advapi32Util.registryGetStringValue(HKEY_LOCAL_MACHINE, keyJavaHome + "\\" + key, "JavaHome");
190 if (StringUtils.isNoneBlank(javaHome) && new File(javaHome).exists()) {
191 javaHomes.add(javaHome);
192 }
193 }
194 }
195 return javaHomes.stream();
196 }
197
198 private static Stream<String> streamWindowsStrings() {
199 return Stream.concat(Stream.of(KEY_JRE, KEY_JRE_9, KEY_JDK, KEY_JDK_9).flatMap(JavaHome::streamAllWindowsJavaHomes),
200 streamPropertyAndEnvVarValues(EXTRA_JAVA_HOMES, null)).distinct();
201 }
202
203 private static Stream<String> toPathStringStream(final String path) {
204 return StringUtils.isEmpty(path) ? Stream.empty() : Stream.of(path.split(File.pathSeparator));
205 }
206
207 private final Path path;
208
209 private JavaHome(final Path path) {
210 this.path = Objects.requireNonNull(path, "path");
211 }
212
213 Stream<Path> find(final int maxDepth, final BiPredicate<Path, BasicFileAttributes> matcher, final FileVisitOption... options) {
214 return find(path, maxDepth, matcher, options);
215 }
216
217 ModularRuntimeImage getModularRuntimeImage() {
218 return Uncheck.get(() -> new ModularRuntimeImage(path.toString()));
219 }
220
221 Path getPath() {
222 return path;
223 }
224
225 private Stream<Path> streamEndsWith(final String suffix) {
226 return find(10, (p, a) -> p.toString().endsWith(suffix));
227 }
228
229 private Stream<JarEntry> streamJarEntryByExt() {
230 return streamJarFileByExt().flatMap(JarFile::stream);
231 }
232
233 private Stream<JarEntry> streamJarEntryByExtClass() {
234 return streamJarEntryByExt().filter(je -> je.getName().endsWith(JavaClass.EXTENSION));
235 }
236
237 private Stream<String> streamJarEntryByExtClassName() {
238 return streamJarEntryByExtClass().map(je -> Utility.pathToPackage(je.getName().substring(0, je.getName().indexOf(JavaClass.EXTENSION))));
239 }
240
241 private Stream<JarFile> streamJarFileByExt() {
242 return streamJarPathByExt().map(this::toJarFile);
243 }
244
245 private Stream<Path> streamJarPathByExt() {
246 return streamEndsWith(".jar");
247 }
248
249 private Stream<Path> streamModuleByExt() {
250 return streamEndsWith(Module.EXTENSION);
251 }
252
253 private JarFile toJarFile(final Path path) {
254 return Uncheck.get(() -> new JarFile(path.toFile()));
255 }
256
257 @Override
258 public String toString() {
259 return path.toString();
260 }
261 }