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
176 public static Long pid() {
177 if (isBuiltInJava9AtLeast()) {
178 Long pid = pidOnJava9();
179 if (pid != null) {
180 return pid;
181 }
182 }
183
184 if (IS_OS_LINUX) {
185 try {
186 return pidStatusOnLinux();
187 } catch (Exception e) {
188
189 }
190 } else if (IS_OS_FREE_BSD || IS_OS_NET_BSD || IS_OS_OPEN_BSD) {
191 try {
192 return pidStatusOnBSD();
193 } catch (Exception e) {
194
195 }
196 }
197
198 return pidOnJMX();
199 }
200
201 static Long pidOnJMX() {
202 String processName = ManagementFactory.getRuntimeMXBean().getName();
203 if (processName.contains("@")) {
204 String pid = processName.substring(0, processName.indexOf('@')).trim();
205 try {
206 return Long.parseLong(pid);
207 } catch (NumberFormatException e) {
208 return null;
209 }
210 }
211
212 return null;
213 }
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 static Long pidStatusOnLinux() throws Exception {
232 return pidStatusOnLinux("");
233 }
234
235
236
237
238
239
240
241
242 static Long pidStatusOnLinux(String root) throws Exception {
243 try (FileReader input = new FileReader(root + "/proc/self/stat")) {
244
245
246 char[] buffer = new char[PROC_STATUS_PID_FIRST_CHARS];
247 String startLine = new String(buffer, 0, input.read(buffer));
248 return Long.parseLong(startLine.substring(0, startLine.indexOf(' ')));
249 }
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 static Long pidStatusOnBSD() throws Exception {
271 return pidStatusOnBSD("");
272 }
273
274
275
276
277
278
279
280
281 static Long pidStatusOnBSD(String root) throws Exception {
282 try (BufferedReader input = new BufferedReader(new FileReader(root + "/proc/curproc/status"))) {
283 String line = input.readLine();
284 int i1 = 1 + line.indexOf(' ');
285 int i2 = line.indexOf(' ', i1);
286 return Long.parseLong(line.substring(i1, i2));
287 }
288 }
289
290 static Long pidOnJava9() {
291 ClassLoader classLoader = currentThread().getContextClassLoader();
292 Class<?> processHandle = tryLoadClass(classLoader, "java.lang.ProcessHandle");
293 Class<?>[] classesChain = {processHandle, processHandle};
294 String[] methodChain = {"current", "pid"};
295 return invokeMethodChain(classesChain, methodChain, null);
296 }
297
298 static ClassLoader reflectClassLoader(Class<?> target, String getterMethodName) {
299 try {
300 Method getter = ReflectionUtils.getMethod(target, getterMethodName);
301 return invokeMethodWithArray(null, getter);
302 } catch (RuntimeException e) {
303 return null;
304 }
305 }
306 }