|
15 | 15 |
|
16 | 16 | import ch.qos.logback.core.CoreConstants;
|
17 | 17 |
|
| 18 | +import java.lang.reflect.InvocationTargetException; |
| 19 | +import java.lang.reflect.Method; |
18 | 20 | import java.util.ArrayList;
|
19 | 21 | import java.util.Collections;
|
20 | 22 | import java.util.IdentityHashMap;
|
|
23 | 25 |
|
24 | 26 | public class ThrowableProxy implements IThrowableProxy {
|
25 | 27 |
|
26 |
| - private Throwable throwable; |
27 |
| - private String className; |
28 |
| - private String message; |
29 |
| - // package-private because of ThrowableProxyUtil |
30 |
| - StackTraceElementProxy[] stackTraceElementProxyArray; |
31 |
| - // package-private because of ThrowableProxyUtil |
32 |
| - int commonFrames; |
33 |
| - private ThrowableProxy cause; |
34 |
| - private ThrowableProxy[] suppressed = NO_SUPPRESSED; |
35 |
| - private final Set<Throwable> alreadyProcessedSet; |
36 |
| - |
37 |
| - private transient PackagingDataCalculator packagingDataCalculator; |
38 |
| - private boolean calculatedPackageData = false; |
39 |
| - |
40 |
| - private static final ThrowableProxy[] NO_SUPPRESSED = new ThrowableProxy[0]; |
41 |
| - |
42 |
| - public ThrowableProxy(Throwable throwable) { |
43 |
| - // Using Collections.newSetFromMap(new IdentityHashMap<>()) is inspired from |
44 |
| - // Throwable.printStackTrace(PrintStreamOrWriter): |
45 |
| - // Guard against malicious overrides of Throwable.equals by |
46 |
| - // using a Set with identity equality semantics. |
47 |
| - this(throwable, Collections.newSetFromMap(new IdentityHashMap<>())); |
48 |
| - } |
49 |
| - |
50 |
| - public ThrowableProxy(Throwable throwable, Set<Throwable> alreadyProcessedSet) { |
51 |
| - |
52 |
| - this.throwable = throwable; |
53 |
| - this.className = throwable.getClass().getName(); |
54 |
| - this.message = throwable.getMessage(); |
55 |
| - this.stackTraceElementProxyArray = ThrowableProxyUtil.steArrayToStepArray(throwable.getStackTrace()); |
56 |
| - this.alreadyProcessedSet = alreadyProcessedSet; |
57 |
| - |
58 |
| - alreadyProcessedSet.add(throwable); |
59 |
| - |
60 |
| - Throwable nested = throwable.getCause(); |
61 |
| - |
62 |
| - if (nested != null && !alreadyProcessedSet.contains(nested)) { |
63 |
| - this.cause = new ThrowableProxy(nested, alreadyProcessedSet); |
64 |
| - this.cause.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(nested.getStackTrace(), stackTraceElementProxyArray); |
65 |
| - } |
66 |
| - Throwable[] throwableSuppressed = throwable.getSuppressed(); |
67 |
| - if (throwableSuppressed.length > 0) { |
68 |
| - List<ThrowableProxy> suppressedList = new ArrayList<>(throwableSuppressed.length); |
69 |
| - for (int i = 0; i < throwableSuppressed.length; i++) { |
70 |
| - Throwable sup = throwableSuppressed[i]; |
71 |
| - if (alreadyProcessedSet.contains(sup)) { |
72 |
| - continue; // loop detected |
73 |
| - } |
74 |
| - ThrowableProxy throwableProxy = new ThrowableProxy(sup, alreadyProcessedSet); |
75 |
| - throwableProxy.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(sup.getStackTrace(), |
76 |
| - stackTraceElementProxyArray); |
77 |
| - suppressedList.add(throwableProxy); |
78 |
| - } |
79 |
| - this.suppressed = suppressedList.toArray(new ThrowableProxy[suppressedList.size()]); |
80 |
| - } |
81 |
| - } |
82 |
| - |
83 |
| - public Throwable getThrowable() { |
84 |
| - return throwable; |
85 |
| - } |
86 |
| - |
87 |
| - public String getMessage() { |
88 |
| - return message; |
89 |
| - } |
90 |
| - |
91 |
| - /* |
92 |
| - * (non-Javadoc) |
93 |
| - * |
94 |
| - * @see ch.qos.logback.classic.spi.IThrowableProxy#getClassName() |
95 |
| - */ |
96 |
| - public String getClassName() { |
97 |
| - return className; |
98 |
| - } |
99 |
| - |
100 |
| - public StackTraceElementProxy[] getStackTraceElementProxyArray() { |
101 |
| - return stackTraceElementProxyArray; |
102 |
| - } |
103 |
| - |
104 |
| - public int getCommonFrames() { |
105 |
| - return commonFrames; |
106 |
| - } |
107 |
| - |
108 |
| - /* |
109 |
| - * (non-Javadoc) |
110 |
| - * |
111 |
| - * @see ch.qos.logback.classic.spi.IThrowableProxy#getCause() |
112 |
| - */ |
113 |
| - public IThrowableProxy getCause() { |
114 |
| - return cause; |
115 |
| - } |
116 |
| - |
117 |
| - public IThrowableProxy[] getSuppressed() { |
118 |
| - return suppressed; |
119 |
| - } |
120 |
| - |
121 |
| - public PackagingDataCalculator getPackagingDataCalculator() { |
122 |
| - // if original instance (non-deserialized), and packagingDataCalculator |
123 |
| - // is not already initialized, then create an instance. |
124 |
| - // here we assume that (throwable == null) for deserialized instances |
125 |
| - if (throwable != null && packagingDataCalculator == null) { |
126 |
| - packagingDataCalculator = new PackagingDataCalculator(); |
127 |
| - } |
128 |
| - return packagingDataCalculator; |
129 |
| - } |
130 |
| - |
131 |
| - public void calculatePackagingData() { |
132 |
| - if (calculatedPackageData) { |
133 |
| - return; |
134 |
| - } |
135 |
| - PackagingDataCalculator pdc = this.getPackagingDataCalculator(); |
136 |
| - if (pdc != null) { |
137 |
| - calculatedPackageData = true; |
138 |
| - pdc.calculate(this); |
139 |
| - } |
140 |
| - } |
141 |
| - |
142 |
| - public void fullDump() { |
143 |
| - StringBuilder builder = new StringBuilder(); |
144 |
| - for (StackTraceElementProxy step : stackTraceElementProxyArray) { |
145 |
| - String string = step.toString(); |
146 |
| - builder.append(CoreConstants.TAB).append(string); |
147 |
| - ThrowableProxyUtil.subjoinPackagingData(builder, step); |
148 |
| - builder.append(CoreConstants.LINE_SEPARATOR); |
149 |
| - } |
150 |
| - System.out.println(builder.toString()); |
151 |
| - } |
| 28 | + static final StackTraceElementProxy[] EMPTY_STEP = new StackTraceElementProxy[0]; |
| 29 | + |
| 30 | + private Throwable throwable; |
| 31 | + private String className; |
| 32 | + private String message; |
| 33 | + // package-private because of ThrowableProxyUtil |
| 34 | + StackTraceElementProxy[] stackTraceElementProxyArray; |
| 35 | + // package-private because of ThrowableProxyUtil |
| 36 | + int commonFrames; |
| 37 | + private ThrowableProxy cause; |
| 38 | + private static final ThrowableProxy[] NO_SUPPRESSED = new ThrowableProxy[0]; |
| 39 | + private ThrowableProxy[] suppressed = NO_SUPPRESSED; |
| 40 | + |
| 41 | + // private final Set<Throwable> alreadyProcessedSet; |
| 42 | + |
| 43 | + private transient PackagingDataCalculator packagingDataCalculator; |
| 44 | + private boolean calculatedPackageData = false; |
| 45 | + |
| 46 | + private boolean circular; |
| 47 | + |
| 48 | + private static final Method GET_SUPPRESSED_METHOD; |
| 49 | + |
| 50 | + static { |
| 51 | + Method method = null; |
| 52 | + try { |
| 53 | + method = Throwable.class.getMethod("getSuppressed"); |
| 54 | + } catch (NoSuchMethodException e) { |
| 55 | + // ignore, will get thrown in Java < 7 |
| 56 | + } |
| 57 | + GET_SUPPRESSED_METHOD = method; |
| 58 | + } |
| 59 | + |
| 60 | + public ThrowableProxy(Throwable throwable) { |
| 61 | + // use an identity set to detect cycles in the throwable chain |
| 62 | + this(throwable, Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>())); |
| 63 | + } |
| 64 | + |
| 65 | + // used for circular exceptions |
| 66 | + private ThrowableProxy(Throwable circular, boolean isCircular) { |
| 67 | + this.throwable = circular; |
| 68 | + this.className = circular.getClass().getName(); |
| 69 | + this.message = circular.getMessage(); |
| 70 | + this.stackTraceElementProxyArray = EMPTY_STEP; |
| 71 | + this.circular = true; |
| 72 | + } |
| 73 | + |
| 74 | + public ThrowableProxy(Throwable throwable, Set<Throwable> alreadyProcessedSet) { |
| 75 | + |
| 76 | + this.throwable = throwable; |
| 77 | + this.className = throwable.getClass().getName(); |
| 78 | + this.message = throwable.getMessage(); |
| 79 | + this.stackTraceElementProxyArray = ThrowableProxyUtil.steArrayToStepArray(throwable.getStackTrace()); |
| 80 | + this.circular = false; |
| 81 | + |
| 82 | + alreadyProcessedSet.add(throwable); |
| 83 | + |
| 84 | + Throwable nested = throwable.getCause(); |
| 85 | + if (nested != null) { |
| 86 | + if (alreadyProcessedSet.contains(nested)) { |
| 87 | + this.cause = new ThrowableProxy(nested, true); |
| 88 | + } else { |
| 89 | + this.cause = new ThrowableProxy(nested, alreadyProcessedSet); |
| 90 | + this.cause.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(nested.getStackTrace(), |
| 91 | + stackTraceElementProxyArray); |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + if (GET_SUPPRESSED_METHOD != null) { |
| 96 | + // this will only execute on Java 7 |
| 97 | + Throwable[] throwableSuppressed = extractSupressedThrowables(throwable); |
| 98 | + |
| 99 | + if (throwableSuppressed.length > 0) { |
| 100 | + List<ThrowableProxy> suppressedList = new ArrayList<ThrowableProxy>(throwableSuppressed.length); |
| 101 | + for (Throwable sup : throwableSuppressed) { |
| 102 | + if (alreadyProcessedSet.contains(sup)) { |
| 103 | + ThrowableProxy throwableProxy = new ThrowableProxy(sup, true); |
| 104 | + suppressedList.add(throwableProxy); |
| 105 | + } else { |
| 106 | + ThrowableProxy throwableProxy = new ThrowableProxy(sup, alreadyProcessedSet); |
| 107 | + throwableProxy.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(sup.getStackTrace(), |
| 108 | + stackTraceElementProxyArray); |
| 109 | + suppressedList.add(throwableProxy); |
| 110 | + } |
| 111 | + } |
| 112 | + this.suppressed = suppressedList.toArray(new ThrowableProxy[suppressedList.size()]); |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + private Throwable[] extractSupressedThrowables(Throwable t) { |
| 118 | + try { |
| 119 | + Object obj = GET_SUPPRESSED_METHOD.invoke(t); |
| 120 | + if (obj instanceof Throwable[]) { |
| 121 | + Throwable[] throwableSuppressed = (Throwable[]) obj; |
| 122 | + return throwableSuppressed; |
| 123 | + } else { |
| 124 | + return null; |
| 125 | + } |
| 126 | + } catch (IllegalAccessException e) { |
| 127 | + // ignore |
| 128 | + } catch (IllegalArgumentException e) { |
| 129 | + // ignore |
| 130 | + } catch (InvocationTargetException e) { |
| 131 | + // ignore |
| 132 | + } |
| 133 | + |
| 134 | + return null; |
| 135 | + |
| 136 | + } |
| 137 | + |
| 138 | + public Throwable getThrowable() { |
| 139 | + return throwable; |
| 140 | + } |
| 141 | + |
| 142 | + public String getMessage() { |
| 143 | + return message; |
| 144 | + } |
| 145 | + |
| 146 | + /* |
| 147 | + * (non-Javadoc) |
| 148 | + * |
| 149 | + * @see ch.qos.logback.classic.spi.IThrowableProxy#getClassName() |
| 150 | + */ |
| 151 | + public String getClassName() { |
| 152 | + return className; |
| 153 | + } |
| 154 | + |
| 155 | + public StackTraceElementProxy[] getStackTraceElementProxyArray() { |
| 156 | + return stackTraceElementProxyArray; |
| 157 | + } |
| 158 | + |
| 159 | + public boolean isCyclic() { |
| 160 | + return circular; |
| 161 | + } |
| 162 | + |
| 163 | + public int getCommonFrames() { |
| 164 | + return commonFrames; |
| 165 | + } |
| 166 | + |
| 167 | + /* |
| 168 | + * (non-Javadoc) |
| 169 | + * |
| 170 | + * @see ch.qos.logback.classic.spi.IThrowableProxy#getCause() |
| 171 | + */ |
| 172 | + public IThrowableProxy getCause() { |
| 173 | + return cause; |
| 174 | + } |
| 175 | + |
| 176 | + public IThrowableProxy[] getSuppressed() { |
| 177 | + return suppressed; |
| 178 | + } |
| 179 | + |
| 180 | + public PackagingDataCalculator getPackagingDataCalculator() { |
| 181 | + // if original instance (non-deserialized), and packagingDataCalculator |
| 182 | + // is not already initialized, then create an instance. |
| 183 | + // here we assume that (throwable == null) for deserialized instances |
| 184 | + if (throwable != null && packagingDataCalculator == null) { |
| 185 | + packagingDataCalculator = new PackagingDataCalculator(); |
| 186 | + } |
| 187 | + return packagingDataCalculator; |
| 188 | + } |
| 189 | + |
| 190 | + public void calculatePackagingData() { |
| 191 | + if (calculatedPackageData) { |
| 192 | + return; |
| 193 | + } |
| 194 | + PackagingDataCalculator pdc = this.getPackagingDataCalculator(); |
| 195 | + if (pdc != null) { |
| 196 | + calculatedPackageData = true; |
| 197 | + pdc.calculate(this); |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + public void fullDump() { |
| 202 | + StringBuilder builder = new StringBuilder(); |
| 203 | + for (StackTraceElementProxy step : stackTraceElementProxyArray) { |
| 204 | + String string = step.toString(); |
| 205 | + builder.append(CoreConstants.TAB).append(string); |
| 206 | + ThrowableProxyUtil.subjoinPackagingData(builder, step); |
| 207 | + builder.append(CoreConstants.LINE_SEPARATOR); |
| 208 | + } |
| 209 | + System.out.println(builder.toString()); |
| 210 | + } |
152 | 211 |
|
153 | 212 | }
|
0 commit comments