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.util.Properties;
33 import java.util.StringTokenizer;
34
35 import static java.lang.Thread.currentThread;
36 import static org.apache.commons.io.IOUtils.closeQuietly;
37 import static org.apache.commons.lang3.JavaVersion.JAVA_9;
38 import static org.apache.commons.lang3.JavaVersion.JAVA_RECENT;
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.commons.lang3.SystemUtils.isJavaVersionAtLeast;
45 import static org.apache.maven.surefire.util.ReflectionUtils.invokeMethodChain;
46 import static org.apache.maven.surefire.util.ReflectionUtils.tryLoadClass;
47 import static org.apache.maven.surefire.util.internal.ObjectUtils.requireNonNull;
48
49
50
51
52
53
54
55 public final class SystemUtils
56 {
57 private static final double JIGSAW_JAVA_VERSION = 9.0d;
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 Double toJdkVersionFromReleaseFile( File jdkHome )
131 {
132 File release = new File( requireNonNull( jdkHome ).getAbsoluteFile(), "release" );
133 if ( !release.isFile() )
134 {
135 return null;
136 }
137 InputStream is = null;
138 try
139 {
140 Properties properties = new Properties();
141 is = new FileInputStream( release );
142 properties.load( is );
143 String javaVersion = properties.getProperty( "JAVA_VERSION" ).replace( "\"", "" );
144 StringTokenizer versions = new StringTokenizer( javaVersion, "._" );
145
146 if ( versions.countTokens() == 1 )
147 {
148 javaVersion = versions.nextToken();
149 }
150 else if ( versions.countTokens() >= 2 )
151 {
152 String majorVersion = versions.nextToken();
153 String minorVersion = versions.nextToken();
154 javaVersion = isNumeric( minorVersion ) ? majorVersion + "." + minorVersion : majorVersion;
155 }
156 else
157 {
158 return null;
159 }
160
161 return Double.valueOf( javaVersion );
162 }
163 catch ( IOException e )
164 {
165 return null;
166 }
167 finally
168 {
169 closeQuietly( is );
170 }
171 }
172
173 public static boolean isJava9AtLeast( String jvmExecutablePath )
174 {
175 File externalJavaHome = toJdkHomeFromJvmExec( jvmExecutablePath );
176 File thisJavaHome = toJdkHomeFromJre();
177 if ( thisJavaHome.equals( externalJavaHome ) )
178 {
179 return isBuiltInJava9AtLeast();
180 }
181 Double releaseFileVersion = externalJavaHome == null ? null : toJdkVersionFromReleaseFile( externalJavaHome );
182 return SystemUtils.isJava9AtLeast( releaseFileVersion );
183 }
184
185 static boolean isBuiltInJava9AtLeast()
186 {
187 return isJavaVersionAtLeast( JAVA_9 );
188 }
189
190 public static boolean isJava9AtLeast( Double version )
191 {
192 return version != null && version >= JIGSAW_JAVA_VERSION;
193 }
194
195 public static ClassLoader platformClassLoader()
196 {
197 if ( JAVA_RECENT.atLeast( JAVA_9 ) )
198 {
199 return reflectClassLoader( ClassLoader.class, "getPlatformClassLoader" );
200 }
201 return null;
202 }
203
204 public static Long pid()
205 {
206 if ( JAVA_RECENT.atLeast( JAVA_9 ) )
207 {
208 Long pid = pidOnJava9();
209 if ( pid != null )
210 {
211 return pid;
212 }
213 }
214
215 if ( IS_OS_LINUX )
216 {
217 try
218 {
219 return pidStatusOnLinux();
220 }
221 catch ( Exception e )
222 {
223
224 }
225 }
226 else if ( IS_OS_FREE_BSD || IS_OS_NET_BSD || IS_OS_OPEN_BSD )
227 {
228 try
229 {
230 return pidStatusOnBSD();
231 }
232 catch ( Exception e )
233 {
234
235 }
236 }
237
238 return pidOnJMX();
239 }
240
241 static Long pidOnJMX()
242 {
243 String processName = ManagementFactory.getRuntimeMXBean().getName();
244 if ( processName.contains( "@" ) )
245 {
246 String pid = processName.substring( 0, processName.indexOf( '@' ) ).trim();
247 try
248 {
249 return Long.parseLong( pid );
250 }
251 catch ( NumberFormatException e )
252 {
253 return null;
254 }
255 }
256
257 return null;
258 }
259
260 static Long pidStatusOnLinux() throws Exception
261 {
262 FileReader input = new FileReader( "/proc/self/stat" );
263 try
264 {
265
266
267 char[] buffer = new char[PROC_STATUS_PID_FIRST_CHARS];
268 String startLine = new String( buffer, 0, input.read( buffer ) );
269 return Long.parseLong( startLine.substring( 0, startLine.indexOf( ' ' ) ) );
270 }
271 finally
272 {
273 input.close();
274 }
275 }
276
277
278
279
280
281
282
283
284
285 static Long pidStatusOnBSD() throws Exception
286 {
287 BufferedReader input = new BufferedReader( new FileReader( "/proc/curproc/status" ) );
288 try
289 {
290 String line = input.readLine();
291 int i1 = 1 + line.indexOf( ' ' );
292 int i2 = line.indexOf( ' ', i1 );
293 return Long.parseLong( line.substring( i1, i2 ) );
294 }
295 finally
296 {
297 input.close();
298 }
299 }
300
301 static Long pidOnJava9()
302 {
303 ClassLoader classLoader = currentThread().getContextClassLoader();
304 Class<?> processHandle = tryLoadClass( classLoader, "java.lang.ProcessHandle" );
305 Class<?>[] classesChain = { processHandle, processHandle };
306 String[] methodChain = { "current", "pid" };
307 return (Long) invokeMethodChain( classesChain, methodChain, null );
308 }
309
310 static ClassLoader reflectClassLoader( Class<?> target, String getterMethodName )
311 {
312 try
313 {
314 Method getter = ReflectionUtils.getMethod( target, getterMethodName );
315 return (ClassLoader) ReflectionUtils.invokeMethodWithArray( null, getter );
316 }
317 catch ( RuntimeException e )
318 {
319 return null;
320 }
321 }
322 }