Skip to content

Commit 398aa4b

Browse files
committed
retain 1.6 compatibility
Signed-off-by: Ceki Gulcu <ceki@qos.ch>
1 parent 90307bf commit 398aa4b

File tree

9 files changed

+225
-140
lines changed

9 files changed

+225
-140
lines changed

logback-access/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<parent>
99
<groupId>ch.qos.logback</groupId>
1010
<artifactId>logback-parent</artifactId>
11-
<version>1.2.10</version>
11+
<version>1.2.11</version>
1212
</parent>
1313

1414
<artifactId>logback-access</artifactId>

logback-classic/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<parent>
99
<groupId>ch.qos.logback</groupId>
1010
<artifactId>logback-parent</artifactId>
11-
<version>1.2.10</version>
11+
<version>1.2.11</version>
1212
</parent>
1313

1414
<artifactId>logback-classic</artifactId>

logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java

Lines changed: 185 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import ch.qos.logback.core.CoreConstants;
1717

18+
import java.lang.reflect.InvocationTargetException;
19+
import java.lang.reflect.Method;
1820
import java.util.ArrayList;
1921
import java.util.Collections;
2022
import java.util.IdentityHashMap;
@@ -23,131 +25,188 @@
2325

2426
public class ThrowableProxy implements IThrowableProxy {
2527

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+
}
152211

153212
}

logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.util.List;
2323

2424
import ch.qos.logback.core.CoreConstants;
25+
import ch.qos.logback.classic.util.EnvUtil;
26+
2527
import org.junit.After;
2628
import org.junit.Before;
2729
import org.junit.Test;
@@ -122,6 +124,25 @@ public void nested() {
122124
verify(t);
123125
}
124126

127+
128+
@Test
129+
public void cyclicCause() {
130+
131+
Exception e = new Exception("foo");
132+
Exception e2 = new Exception(e);
133+
e.initCause(e2);
134+
convertToStringResult(e);
135+
}
136+
137+
@Test
138+
public void cyclicSuppressed() {
139+
140+
Exception e = new Exception("foo");
141+
Exception e2 = new Exception(e);
142+
e.addSuppressed(e2);
143+
convertToStringResult(e);
144+
}
145+
125146
@Test
126147
public void withArgumentOfOne() throws Exception {
127148
final Throwable t = TestHelper.makeNestedException(0);
@@ -217,10 +238,15 @@ void someMethod() throws Exception {
217238
void verify(Throwable t) {
218239
t.printStackTrace(pw);
219240

220-
ILoggingEvent le = createLoggingEvent(t);
241+
String result = convertToStringResult(t);
242+
assertEquals(sw.toString(), result);
243+
}
244+
245+
private String convertToStringResult(Throwable t) {
246+
ILoggingEvent le = createLoggingEvent(t);
221247
String result = tpc.convert(le);
222248
System.out.println(result);
223249
result = result.replace("common frames omitted", "more");
224-
assertEquals(sw.toString(), result);
225-
}
250+
return result;
251+
}
226252
}

0 commit comments

Comments
 (0)