1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.impl;
18
19 import org.apache.logging.log4j.core.helpers.Loader;
20 import org.apache.logging.log4j.status.StatusLogger;
21
22 import java.io.PrintStream;
23 import java.io.PrintWriter;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.net.URL;
27 import java.security.CodeSource;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Stack;
31
32
33
34
35 public class ThrowableProxy extends Throwable {
36
37 private static final long serialVersionUID = -2752771578252251910L;
38
39 private static Method getCallerClass;
40
41 private static PrivateSecurityManager securityManager;
42
43 private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
44
45 private static Method getSuppressed;
46
47 private final Throwable throwable;
48 private final ThrowableProxy cause;
49 private final ThrowableProxy[] suppressed;
50 private int commonElementCount;
51
52 private final StackTracePackageElement[] callerPackageData;
53
54
55 static {
56 setupCallerCheck();
57 versionCheck();
58 }
59
60
61
62
63
64 public ThrowableProxy(Throwable throwable) {
65 this.throwable = throwable;
66 Map<String, CacheEntry> map = new HashMap<String, CacheEntry>();
67 Stack<Class> stack = getCurrentStack();
68 callerPackageData = resolvePackageData(stack, map, null, throwable.getStackTrace());
69 this.cause = (throwable.getCause() == null) ? null :
70 new ThrowableProxy(throwable, stack, map, throwable.getCause());
71 suppressed = getSuppressed(throwable);
72 }
73
74
75
76
77
78
79
80
81
82 private ThrowableProxy(Throwable parent, Stack<Class> stack, Map<String, CacheEntry> map, Throwable cause) {
83 this.throwable = cause;
84 callerPackageData = resolvePackageData(stack, map, parent.getStackTrace(), cause.getStackTrace());
85 this.cause = (throwable.getCause() == null) ? null :
86 new ThrowableProxy(parent, stack, map, throwable.getCause());
87 suppressed = getSuppressed(throwable);
88 }
89
90
91 @Override
92 public void setStackTrace(StackTraceElement[] stackTraceElements) {
93 throw new UnsupportedOperationException("Cannot set the stack trace on a ThrowableProxy");
94 }
95
96 @Override
97 public String getMessage() {
98 return throwable.getMessage();
99 }
100
101 @Override
102 public String getLocalizedMessage() {
103 return throwable.getLocalizedMessage();
104 }
105
106 @Override
107 public Throwable getCause() {
108 return cause;
109 }
110
111
112
113
114
115 public void addSuppressed(Throwable exception) {
116 throw new UnsupportedOperationException("Cannot add suppressed exceptions to a ThrowableProxy");
117 }
118
119
120
121
122
123 public Throwable[] getSuppressed() {
124 return suppressed;
125 }
126
127 @Override
128 public Throwable initCause(Throwable throwable) {
129 throw new IllegalStateException("Cannot set the cause on a ThrowableProxy");
130 }
131
132 @Override
133 public String toString() {
134 return throwable.toString();
135 }
136
137 @Override
138 public void printStackTrace() {
139 throwable.printStackTrace();
140 }
141
142 @Override
143 public void printStackTrace(PrintStream printStream) {
144 throwable.printStackTrace(printStream);
145 }
146
147 @Override
148 public void printStackTrace(PrintWriter printWriter) {
149 throwable.printStackTrace(printWriter);
150 }
151
152 @Override
153 public Throwable fillInStackTrace() {
154 return this;
155 }
156
157 @Override
158 public StackTraceElement[] getStackTrace() {
159 return throwable.getStackTrace();
160 }
161
162
163
164
165
166 public String getRootCauseStackTrace() {
167 StringBuilder sb = new StringBuilder();
168 if (cause != null) {
169 formatWrapper(sb, cause);
170 sb.append("Wrapped by: ");
171 }
172 sb.append(throwable.toString());
173 sb.append("\n");
174 formatElements(sb, 0, throwable.getStackTrace(), callerPackageData);
175 return sb.toString();
176 }
177
178
179
180
181
182
183 public void formatWrapper(StringBuilder sb, ThrowableProxy cause) {
184 Throwable caused = cause.getCause();
185 if (caused != null) {
186 formatWrapper(sb, cause.cause);
187 sb.append("Wrapped by: ");
188 }
189 sb.append(cause).append("\n");
190 formatElements(sb, cause.commonElementCount, cause.getStackTrace(), cause.callerPackageData);
191 }
192
193
194
195
196
197 public String getExtendedStackTrace() {
198 StringBuilder sb = new StringBuilder(throwable.toString());
199 sb.append("\n");
200 formatElements(sb, 0, throwable.getStackTrace(), callerPackageData);
201 if (cause != null) {
202 formatCause(sb, cause);
203 }
204 return sb.toString();
205 }
206
207
208
209
210
211 public String getSuppressedStackTrace() {
212 if (suppressed == null || suppressed.length == 0) {
213 return "";
214 }
215 StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:\n");
216 for (ThrowableProxy proxy : suppressed) {
217 sb.append(proxy.getExtendedStackTrace());
218 }
219 return sb.toString();
220 }
221
222 private void formatCause(StringBuilder sb, ThrowableProxy cause) {
223 sb.append("Caused by: ").append(cause).append("\n");
224 formatElements(sb, cause.commonElementCount, cause.getStackTrace(), cause.callerPackageData);
225 if (cause.getCause() != null) {
226 formatCause(sb, cause.cause);
227 }
228 }
229
230 private void formatElements(StringBuilder sb, int commonCount, StackTraceElement[] causedTrace,
231 StackTracePackageElement[] packageData) {
232 for (int i = 0; i < packageData.length; ++i) {
233 sb.append("\tat ");
234 sb.append(causedTrace[i]);
235 sb.append(" ");
236 sb.append(packageData[i]);
237 sb.append("\n");
238 }
239 if (commonCount != 0) {
240 sb.append("\t... ").append(commonCount).append(" more").append("\n");
241 }
242 }
243
244
245
246
247
248
249
250 private Stack<Class> getCurrentStack() {
251 if (getCallerClass != null) {
252 Stack<Class> classes = new Stack<Class>();
253 int index = 2;
254 Class clazz = getCallerClass(index);
255 while (clazz != null) {
256 classes.push(clazz);
257 clazz = getCallerClass(++index);
258 }
259 return classes;
260 } else if (securityManager != null) {
261 Class[] array = securityManager.getClasses();
262 Stack<Class> classes = new Stack<Class>();
263 for (Class clazz : array) {
264 classes.push(clazz);
265 }
266 return classes;
267 }
268 return new Stack<Class>();
269 }
270
271
272
273
274
275
276
277
278
279 private StackTracePackageElement[] resolvePackageData(Stack<Class> stack, Map<String, CacheEntry> map,
280 StackTraceElement[] rootTrace,
281 StackTraceElement[] stackTrace) {
282 int stackLength;
283 if (rootTrace != null) {
284 int rootIndex = rootTrace.length - 1;
285 int stackIndex = stackTrace.length - 1;
286 while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) {
287 --rootIndex;
288 --stackIndex;
289 }
290 commonElementCount = stackTrace.length - 1 - stackIndex;
291 stackLength = stackIndex + 1;
292 } else {
293 commonElementCount = 0;
294 stackLength = stackTrace.length;
295 }
296 StackTracePackageElement[] packageArray = new StackTracePackageElement[stackLength];
297 Class clazz = stack.peek();
298 ClassLoader lastLoader = null;
299 for (int i = stackLength - 1; i >= 0; --i) {
300 String className = stackTrace[i].getClassName();
301
302
303
304 if (className.equals(clazz.getName())) {
305 CacheEntry entry = resolvePackageElement(clazz, true);
306 packageArray[i] = entry.element;
307 lastLoader = entry.loader;
308 stack.pop();
309 clazz = stack.peek();
310 } else {
311 if (map.containsKey(className)) {
312 CacheEntry entry = map.get(className);
313 packageArray[i] = entry.element;
314 if (entry.loader != null) {
315 lastLoader = entry.loader;
316 }
317 } else {
318 CacheEntry entry = resolvePackageElement(loadClass(lastLoader, className), false);
319 packageArray[i] = entry.element;
320 map.put(className, entry);
321 if (entry.loader != null) {
322 lastLoader = entry.loader;
323 }
324 }
325 }
326 }
327 return packageArray;
328 }
329
330
331
332
333
334
335
336
337 private CacheEntry resolvePackageElement(Class callerClass, boolean exact) {
338 String location = "?";
339 String version = "?";
340 ClassLoader lastLoader = null;
341 if (callerClass != null) {
342 try {
343 CodeSource source = callerClass.getProtectionDomain().getCodeSource();
344 if (source != null) {
345 URL locationURL = source.getLocation();
346 if (locationURL != null) {
347 String str = locationURL.toString().replace('\\', '/');
348 int index = str.lastIndexOf("/");
349 if (index >= 0 && index == str.length() - 1) {
350 index = str.lastIndexOf("/", index - 1);
351 location = str.substring(index + 1);
352 } else {
353 location = str.substring(index + 1);
354 }
355 }
356 }
357 } catch (Exception ex) {
358
359 }
360 Package pkg = callerClass.getPackage();
361 if (pkg != null) {
362 String ver = pkg.getImplementationVersion();
363 if (ver != null) {
364 version = ver;
365 }
366 }
367 lastLoader = callerClass.getClassLoader();
368 }
369 return new CacheEntry(new StackTracePackageElement(location, version, exact), lastLoader);
370 }
371
372
373
374
375
376
377
378
379 private Class getCallerClass(int index) {
380 if (getCallerClass != null) {
381 try {
382 Object[] params = new Object[]{index};
383 return (Class) getCallerClass.invoke(null, params);
384 } catch (Exception ex) {
385
386 }
387 }
388 return null;
389 }
390
391
392
393
394
395
396
397 private Class loadClass(ClassLoader lastLoader, String className) {
398 Class clazz;
399 if (lastLoader != null) {
400 try {
401 clazz = lastLoader.loadClass(className);
402 if (clazz != null) {
403 return clazz;
404 }
405 } catch (Exception ex) {
406
407 }
408 }
409 try {
410 clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
411 } catch (ClassNotFoundException e) {
412 try {
413 clazz = Class.forName(className);
414 } catch (ClassNotFoundException e1) {
415 try {
416 clazz = getClass().getClassLoader().loadClass(className);
417 } catch (ClassNotFoundException e2) {
418 return null;
419 }
420 }
421 }
422 return clazz;
423 }
424
425 private static void versionCheck() {
426 Method[] methods = Throwable.class.getMethods();
427 for (Method method : methods) {
428 if (method.getName().equals("getSuppressed")) {
429 getSuppressed = method;
430 }
431 }
432 }
433
434
435
436
437 private static void setupCallerCheck() {
438 try {
439 ClassLoader loader = Loader.getClassLoader();
440 Class clazz = loader.loadClass("sun.reflect.Reflection");
441 Method[] methods = clazz.getMethods();
442 for (Method method : methods) {
443 int modifier = method.getModifiers();
444 if (method.getName().equals("getCallerClass") && Modifier.isStatic(modifier)) {
445 getCallerClass = method;
446 return;
447 }
448 }
449 } catch (ClassNotFoundException cnfe) {
450 LOGGER.debug("sun.reflect.Reflection is not installed");
451 }
452
453 try {
454 PrivateSecurityManager mgr = new PrivateSecurityManager();
455 if (mgr.getClasses() != null) {
456 securityManager = mgr;
457 } else {
458
459 LOGGER.error("Unable to obtain call stack from security manager");
460 }
461 } catch (Exception ex) {
462 LOGGER.debug("Unable to install security manager", ex);
463 }
464 }
465
466 private ThrowableProxy[] getSuppressed(Throwable throwable) {
467 ThrowableProxy[] supp = null;
468 if (getSuppressed != null) {
469 try {
470 Throwable[] array = (Throwable[]) getSuppressed.invoke(throwable, null);
471 supp = new ThrowableProxy[array.length];
472 int i = 0;
473 for (Throwable t : array) {
474 supp[i] = new ThrowableProxy(t);
475 ++i;
476 }
477 } catch (Exception ex) {
478
479 }
480 }
481 return supp;
482 }
483
484
485
486
487 private class CacheEntry {
488 private StackTracePackageElement element;
489 private ClassLoader loader;
490
491 public CacheEntry(StackTracePackageElement element, ClassLoader loader) {
492 this.element = element;
493 this.loader = loader;
494 }
495 }
496
497
498
499
500 private static class PrivateSecurityManager extends SecurityManager {
501 public Class[] getClasses() {
502 return getClassContext();
503 }
504 }
505 }