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