Skip to content

Commit 2cd912f

Browse files
authored
Access java.lang.management.* via reflection (junit-team#1187)
Several platforms, including Android and j2objc, do not provide the java management APIs and crash or otherwise fail when encountering code that imports or uses these APIs directly.
1 parent 0dfdcd0 commit 2cd912f

9 files changed

+319
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.junit.internal.management;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
6+
/**
7+
* No-op implementation of RuntimeMXBean when the platform doesn't provide it.
8+
*/
9+
class FakeRuntimeMXBean implements RuntimeMXBean {
10+
11+
/**
12+
* {@inheritDoc}
13+
*
14+
* <p>Always returns an empty list.
15+
*/
16+
@Override
17+
public List<String> getInputArguments() {
18+
return Collections.emptyList();
19+
}
20+
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.junit.internal.management;
2+
3+
/**
4+
* No-op implementation of ThreadMXBean when the platform doesn't provide it.
5+
*/
6+
final class FakeThreadMXBean implements ThreadMXBean {
7+
8+
/**
9+
* {@inheritDoc}
10+
*
11+
* <p>Always throws an {@link UnsupportedOperationException}
12+
*/
13+
@Override
14+
public long getThreadCpuTime(long id) {
15+
throw new UnsupportedOperationException();
16+
}
17+
18+
/**
19+
* {@inheritDoc}
20+
*
21+
* <p>Always returns false.
22+
*/
23+
@Override
24+
public boolean isThreadCpuTimeSupported() {
25+
return false;
26+
}
27+
28+
}
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package org.junit.internal.management;
2+
3+
import org.junit.internal.Classes;
4+
5+
import java.lang.reflect.InvocationTargetException;
6+
7+
/**
8+
* Reflective wrapper around {@link java.lang.management.ManagementFactory}
9+
*/
10+
public class ManagementFactory {
11+
private static final class FactoryHolder {
12+
private static final Class<?> MANAGEMENT_FACTORY_CLASS;
13+
14+
static {
15+
Class<?> managementFactoryClass = null;
16+
try {
17+
managementFactoryClass = Classes.getClass("java.lang.management.ManagementFactory");
18+
} catch (ClassNotFoundException e) {
19+
// do nothing, managementFactoryClass will be none on failure
20+
}
21+
MANAGEMENT_FACTORY_CLASS = managementFactoryClass;
22+
}
23+
24+
static Object getBeanObject(String methodName) {
25+
if (MANAGEMENT_FACTORY_CLASS != null) {
26+
try {
27+
return MANAGEMENT_FACTORY_CLASS.getMethod(methodName).invoke(null);
28+
} catch (IllegalAccessException e) {
29+
// fallthrough
30+
} catch (IllegalArgumentException e) {
31+
// fallthrough
32+
} catch (InvocationTargetException e) {
33+
// fallthrough
34+
} catch (NoSuchMethodException e) {
35+
// fallthrough
36+
} catch (SecurityException e) {
37+
// fallthrough
38+
}
39+
}
40+
return null;
41+
}
42+
}
43+
44+
private static final class RuntimeHolder {
45+
private static final RuntimeMXBean RUNTIME_MX_BEAN =
46+
getBean(FactoryHolder.getBeanObject("getRuntimeMXBean"));
47+
48+
private static final RuntimeMXBean getBean(Object runtimeMxBean) {
49+
return runtimeMxBean != null
50+
? new ReflectiveRuntimeMXBean(runtimeMxBean) : new FakeRuntimeMXBean();
51+
}
52+
}
53+
54+
private static final class ThreadHolder {
55+
private static final ThreadMXBean THREAD_MX_BEAN =
56+
getBean(FactoryHolder.getBeanObject("getThreadMXBean"));
57+
58+
private static final ThreadMXBean getBean(Object threadMxBean) {
59+
return threadMxBean != null
60+
? new ReflectiveThreadMXBean(threadMxBean) : new FakeThreadMXBean();
61+
}
62+
}
63+
64+
/**
65+
* @see java.lang.management.ManagementFactory#getRuntimeMXBean()
66+
*/
67+
public static RuntimeMXBean getRuntimeMXBean() {
68+
return RuntimeHolder.RUNTIME_MX_BEAN;
69+
}
70+
71+
/**
72+
* @see java.lang.management.ManagementFactory#getThreadMXBean()
73+
*/
74+
public static ThreadMXBean getThreadMXBean() {
75+
return ThreadHolder.THREAD_MX_BEAN;
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.junit.internal.management;
2+
3+
import org.junit.internal.Classes;
4+
5+
import java.lang.reflect.InvocationTargetException;
6+
import java.lang.reflect.Method;
7+
import java.util.Collections;
8+
import java.util.List;
9+
10+
/**
11+
* Implementation of {@link RuntimeMXBean} using the JVM reflectively.
12+
*/
13+
final class ReflectiveRuntimeMXBean implements RuntimeMXBean {
14+
private final Object runtimeMxBean;
15+
16+
private static final class Holder {
17+
private static final Method getInputArgumentsMethod;
18+
static {
19+
Method inputArguments = null;
20+
try {
21+
Class<?> threadMXBeanClass = Classes.getClass("java.lang.management.RuntimeMXBean");
22+
inputArguments = threadMXBeanClass.getMethod("getInputArguments");
23+
} catch (ClassNotFoundException e) {
24+
// do nothing, input arguments will be null on failure
25+
} catch (NoSuchMethodException e) {
26+
// do nothing, input arguments will be null on failure
27+
} catch (SecurityException e) {
28+
// do nothing, input arguments will be null on failure
29+
}
30+
getInputArgumentsMethod = inputArguments;
31+
}
32+
}
33+
34+
ReflectiveRuntimeMXBean(Object runtimeMxBean) {
35+
super();
36+
this.runtimeMxBean = runtimeMxBean;
37+
}
38+
39+
/**
40+
* {@inheritDoc}
41+
*/
42+
@SuppressWarnings("unchecked")
43+
@Override
44+
public List<String> getInputArguments() {
45+
if (Holder.getInputArgumentsMethod != null) {
46+
try {
47+
return (List<String>) Holder.getInputArgumentsMethod.invoke(runtimeMxBean);
48+
} catch (ClassCastException e) { // no multi-catch with source level 6
49+
// fallthrough
50+
} catch (IllegalAccessException e) {
51+
// fallthrough
52+
} catch (IllegalArgumentException e) {
53+
// fallthrough
54+
} catch (InvocationTargetException e) {
55+
// fallthrough
56+
}
57+
}
58+
return Collections.emptyList();
59+
}
60+
61+
}
62+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.junit.internal.management;
2+
3+
import org.junit.internal.Classes;
4+
5+
import java.lang.reflect.InvocationTargetException;
6+
import java.lang.reflect.Method;
7+
8+
/**
9+
* Implementation of {@link ThreadMXBean} using the JVM reflectively.
10+
*/
11+
final class ReflectiveThreadMXBean implements ThreadMXBean {
12+
private final Object threadMxBean;
13+
14+
15+
private static final class Holder {
16+
static final Method getThreadCpuTimeMethod;
17+
static final Method isThreadCpuTimeSupportedMethod;
18+
19+
private static final String FAILURE_MESSAGE = "Unable to access ThreadMXBean";
20+
21+
static {
22+
Method threadCpuTime = null;
23+
Method threadCpuTimeSupported = null;
24+
try {
25+
Class<?> threadMXBeanClass = Classes.getClass("java.lang.management.ThreadMXBean");
26+
threadCpuTime = threadMXBeanClass.getMethod("getThreadCpuTime", long.class);
27+
threadCpuTimeSupported = threadMXBeanClass.getMethod("isThreadCpuTimeSupported");
28+
} catch (ClassNotFoundException e) {
29+
// do nothing, the methods will be null on failure
30+
} catch (NoSuchMethodException e) {
31+
// do nothing, the methods will be null on failure
32+
} catch (SecurityException e) {
33+
// do nothing, the methods will be null on failure
34+
}
35+
getThreadCpuTimeMethod = threadCpuTime;
36+
isThreadCpuTimeSupportedMethod = threadCpuTimeSupported;
37+
}
38+
}
39+
40+
ReflectiveThreadMXBean(Object threadMxBean) {
41+
super();
42+
this.threadMxBean = threadMxBean;
43+
}
44+
45+
/**
46+
* {@inheritDoc}
47+
*/
48+
@Override
49+
public long getThreadCpuTime(long id) {
50+
if (Holder.getThreadCpuTimeMethod != null) {
51+
Exception error = null;
52+
try {
53+
return (Long) Holder.getThreadCpuTimeMethod.invoke(threadMxBean, id);
54+
} catch (ClassCastException e) {
55+
error = e;
56+
// fallthrough
57+
} catch (IllegalAccessException e) {
58+
error = e;
59+
// fallthrough
60+
} catch (IllegalArgumentException e) {
61+
error = e;
62+
// fallthrough
63+
} catch (InvocationTargetException e) {
64+
error = e;
65+
// fallthrough
66+
}
67+
throw new UnsupportedOperationException(Holder.FAILURE_MESSAGE, error);
68+
}
69+
throw new UnsupportedOperationException(Holder.FAILURE_MESSAGE);
70+
}
71+
72+
/**
73+
* {@inheritDoc}
74+
*/
75+
@Override
76+
public boolean isThreadCpuTimeSupported() {
77+
if (Holder.isThreadCpuTimeSupportedMethod != null) {
78+
try {
79+
return (Boolean) Holder.isThreadCpuTimeSupportedMethod.invoke(threadMxBean);
80+
} catch (ClassCastException e) {
81+
// fallthrough
82+
} catch (IllegalAccessException e) {
83+
// fallthrough
84+
} catch (IllegalArgumentException e) {
85+
// fallthrough
86+
} catch (InvocationTargetException e) {
87+
// fallthrough
88+
}
89+
}
90+
return false;
91+
}
92+
93+
}
94+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.junit.internal.management;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Wrapper for {@link java.lang.management.RuntimeMXBean}.
7+
*/
8+
public interface RuntimeMXBean {
9+
10+
/**
11+
* @see java.lang.management.RuntimeMXBean#getInputArguments()
12+
*/
13+
List<String> getInputArguments();
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.junit.internal.management;
2+
3+
/**
4+
* Wrapper for {@link java.lang.management.ThreadMXBean}.
5+
*/
6+
public interface ThreadMXBean {
7+
/**
8+
* @see java.lang.management.ThreadMXBean#getThreadCpuTime(long)
9+
*/
10+
long getThreadCpuTime(long id);
11+
12+
/**
13+
* @see java.lang.management.ThreadMXBean#isThreadCpuTimeSupported()
14+
*/
15+
boolean isThreadCpuTimeSupported();
16+
}
17+

src/main/java/org/junit/internal/runners/statements/FailOnTimeout.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.junit.internal.runners.statements;
22

3-
import java.lang.management.ManagementFactory;
4-
import java.lang.management.ThreadMXBean;
53
import java.util.Arrays;
64
import java.util.Collections;
75
import java.util.List;
@@ -12,6 +10,8 @@
1210
import java.util.concurrent.TimeUnit;
1311
import java.util.concurrent.TimeoutException;
1412

13+
import org.junit.internal.management.ManagementFactory;
14+
import org.junit.internal.management.ThreadMXBean;
1515
import org.junit.runners.model.MultipleFailureException;
1616
import org.junit.runners.model.Statement;
1717
import org.junit.runners.model.TestTimedOutException;

src/main/java/org/junit/rules/DisableOnDebug.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package org.junit.rules;
22

3-
import java.lang.management.ManagementFactory;
4-
import java.lang.management.RuntimeMXBean;
53
import java.util.List;
64

5+
import org.junit.internal.management.ManagementFactory;
6+
import org.junit.internal.management.RuntimeMXBean;
77
import org.junit.runner.Description;
88
import org.junit.runners.model.Statement;
99

0 commit comments

Comments
 (0)