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.api.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.Thread.currentThread;
37 import static java.util.Objects.requireNonNull;
38 import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray;
39 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT;
40 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_9;
41 import static org.apache.maven.surefire.shared.lang3.StringUtils.isNumeric;
42 import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodChain;
43 import static org.apache.maven.surefire.api.util.ReflectionUtils.tryLoadClass;
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_FREE_BSD;
46 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_NET_BSD;
47 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_OPEN_BSD;
48
49
50
51
52
53
54
55 public final class SystemUtils
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 public static boolean isJava9AtLeast( String jvmExecutablePath )
168 {
169 File externalJavaHome = toJdkHomeFromJvmExec( jvmExecutablePath );
170 File thisJavaHome = toJdkHomeFromJre();
171 if ( thisJavaHome.equals( externalJavaHome ) )
172 {
173 return isBuiltInJava9AtLeast();
174 }
175 else
176 {
177 BigDecimal releaseFileVersion =
178 externalJavaHome == null ? null : toJdkVersionFromReleaseFile( externalJavaHome );
179 return isJava9AtLeast( releaseFileVersion );
180 }
181 }
182
183 public static boolean isBuiltInJava9AtLeast()
184 {
185 return JAVA_RECENT.atLeast( JAVA_9 );
186 }
187
188 public static boolean isJava9AtLeast( BigDecimal version )
189 {
190 return version != null && version.compareTo( JIGSAW_JAVA_VERSION ) >= 0;
191 }
192
193 public static ClassLoader platformClassLoader()
194 {
195 if ( isBuiltInJava9AtLeast() )
196 {
197 return reflectClassLoader( ClassLoader.class, "getPlatformClassLoader" );
198 }
199 return null;
200 }
201
202 public static Long pid()
203 {
204 if ( isBuiltInJava9AtLeast() )
205 {
206 Long pid = pidOnJava9();
207 if ( pid != null )
208 {
209 return pid;
210 }
211 }
212
213 if ( IS_OS_LINUX )
214 {
215 try
216 {
217 return pidStatusOnLinux();
218 }
219 catch ( Exception e )
220 {
221
222 }
223 }
224 else if ( IS_OS_FREE_BSD || IS_OS_NET_BSD || IS_OS_OPEN_BSD )
225 {
226 try
227 {
228 return pidStatusOnBSD();
229 }
230 catch ( Exception e )
231 {
232
233 }
234 }
235
236 return pidOnJMX();
237 }
238
239 static Long pidOnJMX()
240 {
241 String processName = ManagementFactory.getRuntimeMXBean().getName();
242 if ( processName.contains( "@" ) )
243 {
244 String pid = processName.substring( 0, processName.indexOf( '@' ) ).trim();
245 try
246 {
247 return Long.parseLong( pid );
248 }
249 catch ( NumberFormatException e )
250 {
251 return null;
252 }
253 }
254
255 return null;
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 static Long pidStatusOnLinux() throws Exception
275 {
276 return pidStatusOnLinux( "" );
277 }
278
279
280
281
282
283
284
285
286 static Long pidStatusOnLinux( String root ) throws Exception
287 {
288 try ( FileReader input = new FileReader( root + "/proc/self/stat" ) )
289 {
290
291
292 char[] buffer = new char[PROC_STATUS_PID_FIRST_CHARS];
293 String startLine = new String( buffer, 0, input.read( buffer ) );
294 return Long.parseLong( startLine.substring( 0, startLine.indexOf( ' ' ) ) );
295 }
296 }
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316 static Long pidStatusOnBSD() throws Exception
317 {
318 return pidStatusOnBSD( "" );
319 }
320
321
322
323
324
325
326
327
328 static Long pidStatusOnBSD( String root ) throws Exception
329 {
330 try ( BufferedReader input = new BufferedReader( new FileReader( root + "/proc/curproc/status" ) ) )
331 {
332 String line = input.readLine();
333 int i1 = 1 + line.indexOf( ' ' );
334 int i2 = line.indexOf( ' ', i1 );
335 return Long.parseLong( line.substring( i1, i2 ) );
336 }
337 }
338
339 static Long pidOnJava9()
340 {
341 ClassLoader classLoader = currentThread().getContextClassLoader();
342 Class<?> processHandle = tryLoadClass( classLoader, "java.lang.ProcessHandle" );
343 Class<?>[] classesChain = { processHandle, processHandle };
344 String[] methodChain = { "current", "pid" };
345 return invokeMethodChain( classesChain, methodChain, null );
346 }
347
348 static ClassLoader reflectClassLoader( Class<?> target, String getterMethodName )
349 {
350 try
351 {
352 Method getter = ReflectionUtils.getMethod( target, getterMethodName );
353 return invokeMethodWithArray( null, getter );
354 }
355 catch ( RuntimeException e )
356 {
357 return null;
358 }
359 }
360 }