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