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 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 public static Class<?> getCallerClass(final int depth) {
121 if (depth < 0) {
122 throw new IndexOutOfBoundsException(Integer.toString(depth));
123 }
124
125
126 if (supportsFastReflection()) {
127 try {
128 return (Class<?>) GET_CALLER_CLASS.invoke(null, depth + 1 + JDK_7u25_OFFSET);
129 } catch (final Exception e) {
130
131 LOGGER.error("Error in ReflectionUtil.getCallerClass({}).", depth, e);
132
133 return null;
134 }
135 }
136
137
138 final StackTraceElement element = getEquivalentStackTraceElement(depth + 1);
139 try {
140 return LoaderUtil.loadClass(element.getClassName());
141 } catch (final ClassNotFoundException e) {
142 LOGGER.error("Could not find class in ReflectionUtil.getCallerClass({}).", depth, e);
143 }
144
145 return null;
146 }
147
148 static StackTraceElement getEquivalentStackTraceElement(final int depth) {
149
150
151 final StackTraceElement[] elements = new Throwable().getStackTrace();
152 int i = 0;
153 for (final StackTraceElement element : elements) {
154 if (isValid(element)) {
155 if (i == depth) {
156 return element;
157 }
158 ++i;
159 }
160 }
161 LOGGER.error("Could not find an appropriate StackTraceElement at index {}", depth);
162 throw new IndexOutOfBoundsException(Integer.toString(depth));
163 }
164
165 private static boolean isValid(final StackTraceElement element) {
166
167 if (element.isNativeMethod()) {
168 return false;
169 }
170 final String cn = element.getClassName();
171
172 if (cn.startsWith("sun.reflect.")) {
173 return false;
174 }
175 final String mn = element.getMethodName();
176
177
178
179
180 if (cn.startsWith("java.lang.reflect.") && (mn.equals("invoke") || mn.equals("newInstance"))) {
181 return false;
182 }
183
184 if (cn.equals("java.lang.Class") && mn.equals("newInstance")) {
185 return false;
186 }
187
188 if (cn.equals("java.lang.invoke.MethodHandle") && mn.startsWith("invoke")) {
189 return false;
190 }
191
192 return true;
193 }
194
195
196 public static Class<?> getCallerClass(final String fqcn) {
197 return getCallerClass(fqcn, Strings.EMPTY);
198 }
199
200
201 public static Class<?> getCallerClass(final String fqcn, final String pkg) {
202 if (supportsFastReflection()) {
203 boolean next = false;
204 Class<?> clazz;
205 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
206 if (fqcn.equals(clazz.getName())) {
207 next = true;
208 continue;
209 }
210 if (next && clazz.getName().startsWith(pkg)) {
211 return clazz;
212 }
213 }
214
215 return null;
216 }
217 if (SECURITY_MANAGER != null) {
218 return SECURITY_MANAGER.getCallerClass(fqcn, pkg);
219 }
220 try {
221 return LoaderUtil.loadClass(getCallerClassName(fqcn, pkg, new Throwable().getStackTrace()));
222 } catch (final ClassNotFoundException ignored) {
223
224 }
225
226 return null;
227 }
228
229
230 public static Class<?> getCallerClass(final Class<?> anchor) {
231 if (supportsFastReflection()) {
232 boolean next = false;
233 Class<?> clazz;
234 for (int i = 2; null != (clazz = getCallerClass(i)); i++) {
235 if (anchor.equals(clazz)) {
236 next = true;
237 continue;
238 }
239 if (next) {
240 return clazz;
241 }
242 }
243 return Object.class;
244 }
245 if (SECURITY_MANAGER != null) {
246 return SECURITY_MANAGER.getCallerClass(anchor);
247 }
248 try {
249 return LoaderUtil.loadClass(getCallerClassName(anchor.getName(), Strings.EMPTY,
250 new Throwable().getStackTrace()));
251 } catch (final ClassNotFoundException ignored) {
252
253 }
254 return Object.class;
255 }
256
257 private static String getCallerClassName(final String fqcn, final String pkg, final StackTraceElement... elements) {
258 boolean next = false;
259 for (final StackTraceElement element : elements) {
260 final String className = element.getClassName();
261 if (className.equals(fqcn)) {
262 next = true;
263 continue;
264 }
265 if (next && className.startsWith(pkg)) {
266 return className;
267 }
268 }
269 return Object.class.getName();
270 }
271
272
273 public static Stack<Class<?>> getCurrentStackTrace() {
274
275 if (SECURITY_MANAGER != null) {
276 final Class<?>[] array = SECURITY_MANAGER.getClassContext();
277 final Stack<Class<?>> classes = new Stack<>();
278 classes.ensureCapacity(array.length);
279 for (final Class<?> clazz : array) {
280 classes.push(clazz);
281 }
282 return classes;
283 }
284
285 if (supportsFastReflection()) {
286 final Stack<Class<?>> classes = new Stack<>();
287 Class<?> clazz;
288 for (int i = 1; null != (clazz = getCallerClass(i)); i++) {
289 classes.push(clazz);
290 }
291 return classes;
292 }
293 return new Stack<>();
294 }
295
296
297
298
299 static final class PrivateSecurityManager extends SecurityManager {
300
301 @Override
302 protected Class<?>[] getClassContext() {
303 return super.getClassContext();
304 }
305
306 protected Class<?> getCallerClass(final String fqcn, final String pkg) {
307 boolean next = false;
308 for (final Class<?> clazz : getClassContext()) {
309 if (fqcn.equals(clazz.getName())) {
310 next = true;
311 continue;
312 }
313 if (next && clazz.getName().startsWith(pkg)) {
314 return clazz;
315 }
316 }
317
318 return null;
319 }
320
321 protected Class<?> getCallerClass(final Class<?> anchor) {
322 boolean next = false;
323 for (final Class<?> clazz : getClassContext()) {
324 if (anchor.equals(clazz)) {
325 next = true;
326 continue;
327 }
328 if (next) {
329 return clazz;
330 }
331 }
332 return Object.class;
333 }
334 }
335 }