1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.surefire.booter;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileReader;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.lang.management.ManagementFactory;
28 import java.lang.reflect.Method;
29 import java.math.BigDecimal;
30 import java.util.Properties;
31 import java.util.StringTokenizer;
32
33 import org.apache.maven.surefire.api.util.ReflectionUtils;
34
35 import static java.lang.Thread.currentThread;
36 import static java.util.Objects.requireNonNull;
37 import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodChain;
38 import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray;
39 import static org.apache.maven.surefire.api.util.ReflectionUtils.tryLoadClass;
40 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_9;
41 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT;
42 import static org.apache.maven.surefire.shared.lang3.StringUtils.isNumeric;
43 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_FREE_BSD;
44 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_LINUX;
45 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_NET_BSD;
46 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_OPEN_BSD;
47
48
49
50
51
52
53
54 public final class SystemUtils {
55 private static final BigDecimal JIGSAW_JAVA_VERSION = new BigDecimal(9).stripTrailingZeros();
56
57 private static final int PROC_STATUS_PID_FIRST_CHARS = 20;
58
59 private SystemUtils() {
60 throw new IllegalStateException("no instantiable constructor");
61 }
62
63
64
65
66
67 public static boolean endsWithJavaPath(String jvmExecPath) {
68 File javaExec = new File(jvmExecPath).getAbsoluteFile();
69 File bin = javaExec.getParentFile();
70 String exec = javaExec.getName();
71 return exec.startsWith("java") && bin != null && bin.getName().equals("bin");
72 }
73
74
75
76
77
78
79
80
81
82
83 public static File toJdkHomeFromJvmExec(String jvmExecutable) {
84 File bin = new File(jvmExecutable).getAbsoluteFile().getParentFile();
85 if ("bin".equals(bin.getName())) {
86 File parent = bin.getParentFile();
87 if ("jre".equals(parent.getName())) {
88 File jdk = parent.getParentFile();
89 return new File(jdk, "bin").isDirectory() ? jdk : null;
90 }
91 return parent;
92 }
93 return null;
94 }
95
96
97
98
99
100
101
102
103 public static File toJdkHomeFromJre() {
104 return toJdkHomeFromJre(System.getProperty("java.home"));
105 }
106
107
108
109
110
111
112
113
114
115
116 static File toJdkHomeFromJre(String jreHome) {
117 File pathToJreOrJdk = new File(jreHome).getAbsoluteFile();
118 return "jre".equals(pathToJreOrJdk.getName()) ? pathToJreOrJdk.getParentFile() : pathToJreOrJdk;
119 }
120
121 public static BigDecimal toJdkVersionFromReleaseFile(File jdkHome) {
122 File release = new File(requireNonNull(jdkHome).getAbsoluteFile(), "release");
123 if (!release.isFile()) {
124 return null;
125 }
126 Properties properties = new Properties();
127 try (InputStream is = new FileInputStream(release)) {
128 properties.load(is);
129 String javaVersion = properties.getProperty("JAVA_VERSION").replace("\"", "");
130 StringTokenizer versions = new StringTokenizer(javaVersion, "._");
131
132 if (versions.countTokens() == 1) {
133 javaVersion = versions.nextToken();
134 } else if (versions.countTokens() >= 2) {
135 String majorVersion = versions.nextToken();
136 String minorVersion = versions.nextToken();
137 javaVersion = isNumeric(minorVersion) ? majorVersion + "." + minorVersion : majorVersion;
138 } else {
139 return null;
140 }
141
142 return new BigDecimal(javaVersion);
143 } catch (IOException e) {
144 return null;
145 }
146 }
147
148 public static boolean isJava9AtLeast(String jvmExecutablePath) {
149 File externalJavaHome = toJdkHomeFromJvmExec(jvmExecutablePath);
150 File thisJavaHome = toJdkHomeFromJre();
151 if (thisJavaHome.equals(externalJavaHome)) {
152 return isBuiltInJava9AtLeast();
153 } else {
154 BigDecimal releaseFileVersion =
155 externalJavaHome == null ? null : toJdkVersionFromReleaseFile(externalJavaHome);
156 return isJava9AtLeast(releaseFileVersion);
157 }
158 }
159
160 public static boolean isBuiltInJava9AtLeast() {
161 return JAVA_RECENT.atLeast(JAVA_9);
162 }
163
164 public static boolean isJava9AtLeast(BigDecimal version) {
165 return version != null && version.compareTo(JIGSAW_JAVA_VERSION) >= 0;
166 }
167
168 public static ClassLoader platformClassLoader() {
169 if (isBuiltInJava9AtLeast()) {
170 return reflectClassLoader(ClassLoader.class, "getPlatformClassLoader");
171 }
172 return null;
173 }
174
175 public static Long pid() {
176 if (isBuiltInJava9AtLeast()) {
177 Long pid = pidOnJava9();
178 if (pid != null) {
179 return pid;
180 }
181 }
182
183 if (IS_OS_LINUX) {
184 try {
185 return pidStatusOnLinux();
186 } catch (Exception e) {
187
188 }
189 } else if (IS_OS_FREE_BSD || IS_OS_NET_BSD || IS_OS_OPEN_BSD) {
190 try {
191 return pidStatusOnBSD();
192 } catch (Exception e) {
193
194 }
195 }
196
197 return pidOnJMX();
198 }
199
200 static Long pidOnJMX() {
201 String processName = ManagementFactory.getRuntimeMXBean().getName();
202 if (processName.contains("@")) {
203 String pid = processName.substring(0, processName.indexOf('@')).trim();
204 try {
205 return Long.parseLong(pid);
206 } catch (NumberFormatException e) {
207 return null;
208 }
209 }
210
211 return null;
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230 static Long pidStatusOnLinux() throws Exception {
231 return pidStatusOnLinux("");
232 }
233
234
235
236
237
238
239
240
241 static Long pidStatusOnLinux(String root) throws Exception {
242 try (FileReader input = new FileReader(root + "/proc/self/stat")) {
243
244
245 char[] buffer = new char[PROC_STATUS_PID_FIRST_CHARS];
246 String startLine = new String(buffer, 0, input.read(buffer));
247 return Long.parseLong(startLine.substring(0, startLine.indexOf(' ')));
248 }
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269 static Long pidStatusOnBSD() throws Exception {
270 return pidStatusOnBSD("");
271 }
272
273
274
275
276
277
278
279
280 static Long pidStatusOnBSD(String root) throws Exception {
281 try (BufferedReader input = new BufferedReader(new FileReader(root + "/proc/curproc/status"))) {
282 String line = input.readLine();
283 int i1 = 1 + line.indexOf(' ');
284 int i2 = line.indexOf(' ', i1);
285 return Long.parseLong(line.substring(i1, i2));
286 }
287 }
288
289 static Long pidOnJava9() {
290 ClassLoader classLoader = currentThread().getContextClassLoader();
291 Class<?> processHandle = tryLoadClass(classLoader, "java.lang.ProcessHandle");
292 Class<?>[] classesChain = {processHandle, processHandle};
293 String[] methodChain = {"current", "pid"};
294 return invokeMethodChain(classesChain, methodChain, null);
295 }
296
297 static ClassLoader reflectClassLoader(Class<?> target, String getterMethodName) {
298 try {
299 Method getter = ReflectionUtils.getMethod(target, getterMethodName);
300 return invokeMethodWithArray(null, getter);
301 } catch (RuntimeException e) {
302 return null;
303 }
304 }
305 }