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