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