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.util.Loader;
20 import org.apache.logging.log4j.status.StatusLogger;
21 import org.apache.logging.log4j.util.LoaderUtil;
22
23 import java.net.URL;
24 import java.security.CodeSource;
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.Stack;
31
32
33
34
35
36 class ThrowableProxyHelper {
37
38 static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0];
39
40 private ThrowableProxyHelper() {
41
42 }
43
44
45
46
47
48
49
50 static final class CacheEntry {
51 private final ExtendedClassInfo element;
52 private final ClassLoader loader;
53
54 private CacheEntry(final ExtendedClassInfo element, final ClassLoader loader) {
55 this.element = element;
56 this.loader = loader;
57 }
58 }
59
60
61
62
63
64
65
66
67
68
69
70 static ExtendedStackTraceElement[] toExtendedStackTrace(
71 final ThrowableProxy src,
72 final Stack<Class<?>> stack, final Map<String, CacheEntry> map,
73 final StackTraceElement[] rootTrace,
74 final StackTraceElement[] stackTrace) {
75 int stackLength;
76 if (rootTrace != null) {
77 int rootIndex = rootTrace.length - 1;
78 int stackIndex = stackTrace.length - 1;
79 while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) {
80 --rootIndex;
81 --stackIndex;
82 }
83 src.setCommonElementCount(stackTrace.length - 1 - stackIndex);
84 stackLength = stackIndex + 1;
85 } else {
86 src.setCommonElementCount(0);
87 stackLength = stackTrace.length;
88 }
89 final ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength];
90 Class<?> clazz = stack.isEmpty() ? null : stack.peek();
91 ClassLoader lastLoader = null;
92 for (int i = stackLength - 1; i >= 0; --i) {
93 final StackTraceElement stackTraceElement = stackTrace[i];
94 final String className = stackTraceElement.getClassName();
95
96
97
98 ExtendedClassInfo extClassInfo;
99 if (clazz != null && className.equals(clazz.getName())) {
100 final CacheEntry entry = toCacheEntry(clazz, true);
101 extClassInfo = entry.element;
102 lastLoader = entry.loader;
103 stack.pop();
104 clazz = stack.isEmpty() ? null : stack.peek();
105 } else {
106 final CacheEntry cacheEntry = map.get(className);
107 if (cacheEntry != null) {
108 final CacheEntry entry = cacheEntry;
109 extClassInfo = entry.element;
110 if (entry.loader != null) {
111 lastLoader = entry.loader;
112 }
113 } else {
114 final CacheEntry entry = toCacheEntry(ThrowableProxyHelper.loadClass(lastLoader, className), false);
115 extClassInfo = entry.element;
116 map.put(className, entry);
117 if (entry.loader != null) {
118 lastLoader = entry.loader;
119 }
120 }
121 }
122 extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo);
123 }
124 return extStackTrace;
125 }
126
127 static ThrowableProxy[] toSuppressedProxies(final Throwable thrown, Set<Throwable> suppressedVisited) {
128 try {
129 final Throwable[] suppressed = thrown.getSuppressed();
130 if (suppressed == null || suppressed.length == 0) {
131 return EMPTY_THROWABLE_PROXY_ARRAY;
132 }
133 final List<ThrowableProxy> proxies = new ArrayList<>(suppressed.length);
134 if (suppressedVisited == null) {
135 suppressedVisited = new HashSet<>(suppressed.length);
136 }
137 for (int i = 0; i < suppressed.length; i++) {
138 final Throwable candidate = suppressed[i];
139 if (suppressedVisited.add(candidate)) {
140 proxies.add(new ThrowableProxy(candidate, suppressedVisited));
141 }
142 }
143 return proxies.toArray(new ThrowableProxy[proxies.size()]);
144 } catch (final Exception e) {
145 StatusLogger.getLogger().error(e);
146 }
147 return null;
148 }
149
150
151
152
153
154
155
156
157 private static CacheEntry toCacheEntry(final Class<?> callerClass, final boolean exact) {
158 String location = "?";
159 String version = "?";
160 ClassLoader lastLoader = null;
161 if (callerClass != null) {
162 try {
163 final CodeSource source = callerClass.getProtectionDomain().getCodeSource();
164 if (source != null) {
165 final URL locationURL = source.getLocation();
166 if (locationURL != null) {
167 final String str = locationURL.toString().replace('\\', '/');
168 int index = str.lastIndexOf("/");
169 if (index >= 0 && index == str.length() - 1) {
170 index = str.lastIndexOf("/", index - 1);
171 location = str.substring(index + 1);
172 } else {
173 location = str.substring(index + 1);
174 }
175 }
176 }
177 } catch (final Exception ex) {
178
179 }
180 final Package pkg = callerClass.getPackage();
181 if (pkg != null) {
182 final String ver = pkg.getImplementationVersion();
183 if (ver != null) {
184 version = ver;
185 }
186 }
187 try {
188 lastLoader = callerClass.getClassLoader();
189 } catch (final SecurityException e) {
190 lastLoader = null;
191 }
192 }
193 return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader);
194 }
195
196
197
198
199
200
201
202
203
204 private static Class<?> loadClass(final ClassLoader lastLoader, final String className) {
205
206 Class<?> clazz;
207 if (lastLoader != null) {
208 try {
209 clazz = lastLoader.loadClass(className);
210 if (clazz != null) {
211 return clazz;
212 }
213 } catch (final Throwable ignore) {
214
215 }
216 }
217 try {
218 clazz = LoaderUtil.loadClass(className);
219 } catch (final ClassNotFoundException | NoClassDefFoundError e) {
220 return loadClass(className);
221 } catch (final SecurityException e) {
222 return null;
223 }
224 return clazz;
225 }
226
227 private static Class<?> loadClass(final String className) {
228 try {
229 return Loader.loadClass(className, ThrowableProxyHelper.class.getClassLoader());
230 } catch (final ClassNotFoundException | NoClassDefFoundError | SecurityException e) {
231 return null;
232 }
233 }
234 }