1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.util;
18
19 import java.lang.reflect.Method;
20 import java.util.Stack;
21
22 import org.apache.logging.log4j.Logger;
23 import org.apache.logging.log4j.status.StatusLogger;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public final class ReflectionUtil {
51
52
53 static final int JDK_7u25_OFFSET;
54
55
56 private static final Logger LOGGER = StatusLogger.getLogger();
57 private static final boolean SUN_REFLECTION_SUPPORTED;
58 private static final Method GET_CALLER_CLASS;
59 private static final PrivateSecurityManager SECURITY_MANAGER;
60
61 static {
62 Method getCallerClass;
63 int java7u25CompensationOffset = 0;
64 try {
65 final Class<?> sunReflectionClass = LoaderUtil.loadClass("sun.reflect.Reflection");
66 getCallerClass = sunReflectionClass.getDeclaredMethod("getCallerClass", int.class);
67 Object o = getCallerClass.invoke(null, 0);
68 final Object test1 = getCallerClass.invoke(null, 0);
69 if (o == null || o != sunReflectionClass) {
70 LOGGER.warn("Unexpected return value from Reflection.getCallerClass(): {}", test1);
71 getCallerClass = null;
72 java7u25CompensationOffset = -1;
73 } else {
74 o = getCallerClass.invoke(null, 1);
75 if (o == sunReflectionClass) {
76 LOGGER.warn("You are using Java 1.7.0_25 which has a broken implementation of "
77 + "Reflection.getCallerClass.");
78 LOGGER.warn("You should upgrade to at least Java 1.7.0_40 or later.");
79 LOGGER.debug("Using stack depth compensation offset of 1 due to Java 7u25.");
80 java7u25CompensationOffset = 1;
81 }
82 }
83 } catch (final Exception | LinkageError e) {
84 LOGGER.info("sun.reflect.Reflection.getCallerClass is not supported. "
85 + "ReflectionUtil.getCallerClass will be much slower due to this.", e);
86 getCallerClass = null;
87 java7u25CompensationOffset = -1;
88 }
89
90 SUN_REFLECTION_SUPPORTED = getCallerClass != null;
91 GET_CALLER_CLASS = getCallerClass;
92 JDK_7u25_OFFSET = java7u25CompensationOffset;
93
94 PrivateSecurityManager psm;
95 try {
96 final SecurityManager sm = System.getSecurityManager();
97 if (sm != null) {
98 sm.checkPermission(new RuntimePermission("createSecurityManager"));
99 }
100 psm = new PrivateSecurityManager();
101 } catch (final SecurityException ignored) {
102 LOGGER.debug("Not allowed to create SecurityManager. "
103 + "Falling back to slowest ReflectionUtil implementation.");
104 psm = null;
105 }
106 SECURITY_MANAGER = psm;
107 }
108
109 private ReflectionUtil() {
110 }
111
112 public static boolean supportsFastReflection() {
113 return SUN_REFLECTION_SUPPORTED;
114 }
115
116
117
118
119
120 @PerformanceSensitive
121 public static Class<?> getCallerClass(final int depth) {
122 if (depth < 0) {
123 throw new IndexOutOfBoundsException(Integer.toString(depth));
124 }
125
126
127 if (supportsFastReflection()) {
128 try {
129 return (Class<?>) GET_CALLER_CLASS.invoke(null, depth + 1 + JDK_7u25_OFFSET);
130 } catch (final Exception e) {
131
132 LOGGER.error("Error in ReflectionUtil.getCallerClass({}).", depth, e);
133
134 return null;
135 }
136 }
137
138
139 final StackTraceElement element = getEquivalentStackTraceElement(depth + 1);
140 try {
141 return LoaderUtil.loadClass(element.getClassName());
142 } catch (final ClassNotFoundException e) {
143 LOGGER.error("Could not find class in ReflectionUtil.getCallerClass({}).", depth, e);
144 }
145
146 return null;
147 }
148
149 static StackTraceElement getEquivalentStackTraceElement(final int depth) {
150
151
152 final StackTraceElement[] elements = new Throwable().getStackTrace();
153 int i = 0;
154 for (final StackTraceElement element : elements) {
155 if (isValid(element)) {
156 if (i == depth) {
157 return element;
158 }
159 ++i;
160 }
161 }
162 LOGGER.error("Could not find an appropriate StackTraceElement at index {}", depth);
163 throw new IndexOutOfBoundsException(Integer.toString(depth));
164 }
165
166 private static boolean isValid(final StackTraceElement element) {
167
168 if (element.isNativeMethod()) {
169 return false;
170 }
171 final String cn = element.getClassName();
172
173 if (cn.startsWith("sun.reflect.")) {
174 return false;
175 }
176 final String mn = element.getMethodName();
177
178
179
180
181 if (cn.startsWith("java.lang.reflect.") && (mn.equals("invoke") || mn.equals("newInstance"))) {
182 return false;
183 }
184
185 if (cn.startsWith("jdk.internal.reflect.")) {
186 return false;
187 }
188
189 if (cn.equals("java.lang.Class") && mn.equals("newInstance")) {
190 return false;
191 }
192
193 if (cn.equals("java.lang.invoke.MethodHandle") && mn.startsWith("invoke")) {
194 return false;
195 }
196
197 return true;
198 }
199
200
201 @PerformanceSensitive
202 public static Class<?> getCallerClass(final String fqcn) {
203 return getCallerClass(fqcn, Strings.EMPTY);
204 }
205
206
207 @PerformanceSensitive
208 public static Class<?> getCallerClass(final String fqcn, final String pkg) {
209 if (supportsFastReflection()) {
210 boolean next = false;
211 Class<?> clazz;
212 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
213 if (fqcn.equals(clazz.getName())) {
214 next = true;
215 continue;
216 }
217 if (next && clazz.getName().startsWith(pkg)) {
218 return clazz;
219 }
220 }
221
222 return null;
223 }
224 if (SECURITY_MANAGER != null) {
225 return SECURITY_MANAGER.getCallerClass(fqcn, pkg);
226 }
227 try {
228 return LoaderUtil.loadClass(getCallerClassName(fqcn, pkg, new Throwable().getStackTrace()));
229 } catch (final ClassNotFoundException ignored) {
230
231 }
232
233 return null;
234 }
235
236
237 @PerformanceSensitive
238 public static Class<?> getCallerClass(final Class<?> anchor) {
239 if (supportsFastReflection()) {
240 boolean next = false;
241 Class<?> clazz;
242 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
243 if (anchor.equals(clazz)) {
244 next = true;
245 continue;
246 }
247 if (next) {
248 return clazz;
249 }
250 }
251 return Object.class;
252 }
253 if (SECURITY_MANAGER != null) {
254 return SECURITY_MANAGER.getCallerClass(anchor);
255 }
256 try {
257 return LoaderUtil.loadClass(getCallerClassName(anchor.getName(), Strings.EMPTY,
258 new Throwable().getStackTrace()));
259 } catch (final ClassNotFoundException ignored) {
260
261 }
262 return Object.class;
263 }
264
265 private static String getCallerClassName(final String fqcn, final String pkg, final StackTraceElement... elements) {
266 boolean next = false;
267 for (final StackTraceElement element : elements) {
268 final String className = element.getClassName();
269 if (className.equals(fqcn)) {
270 next = true;
271 continue;
272 }
273 if (next && className.startsWith(pkg)) {
274 return className;
275 }
276 }
277 return Object.class.getName();
278 }
279
280
281 @PerformanceSensitive
282 public static Stack<Class<?>> getCurrentStackTrace() {
283
284 if (SECURITY_MANAGER != null) {
285 final Class<?>[] array = SECURITY_MANAGER.getClassContext();
286 final Stack<Class<?>> classes = new Stack<>();
287 classes.ensureCapacity(array.length);
288 for (final Class<?> clazz : array) {
289 classes.push(clazz);
290 }
291 return classes;
292 }
293
294 if (supportsFastReflection()) {
295 final Stack<Class<?>> classes = new Stack<>();
296 Class<?> clazz;
297 for (int i = 1; null != (clazz = getCallerClass(i)); i++) {
298 classes.push(clazz);
299 }
300 return classes;
301 }
302 return new Stack<>();
303 }
304
305
306
307
308 static final class PrivateSecurityManager extends SecurityManager {
309
310 @Override
311 protected Class<?>[] getClassContext() {
312 return super.getClassContext();
313 }
314
315 protected Class<?> getCallerClass(final String fqcn, final String pkg) {
316 boolean next = false;
317 for (final Class<?> clazz : getClassContext()) {
318 if (fqcn.equals(clazz.getName())) {
319 next = true;
320 continue;
321 }
322 if (next && clazz.getName().startsWith(pkg)) {
323 return clazz;
324 }
325 }
326
327 return null;
328 }
329
330 protected Class<?> getCallerClass(final Class<?> anchor) {
331 boolean next = false;
332 for (final Class<?> clazz : getClassContext()) {
333 if (anchor.equals(clazz)) {
334 next = true;
335 continue;
336 }
337 if (next) {
338 return clazz;
339 }
340 }
341 return Object.class;
342 }
343 }
344 }