15
15
*/
16
16
package rx .exceptions ;
17
17
18
- import java .io .PrintStream ;
19
- import java .io .PrintWriter ;
20
18
import java .util .ArrayList ;
21
19
import java .util .Collection ;
22
20
import java .util .Collections ;
23
- import java .util .LinkedHashSet ;
21
+ import java .util .HashSet ;
24
22
import java .util .List ;
25
23
import java .util .Set ;
26
24
27
25
/**
28
26
* Exception that is a composite of 1 or more other exceptions.
29
- * A CompositeException does not modify the structure of any exception it wraps, but at print-time
30
- * iterates through the list of contained Throwables to print them all.
31
- *
32
- * Its invariant is to contains an immutable, ordered (by insertion order), unique list of non-composite exceptions.
33
- * This list may be queried by {@code #getExceptions()}
27
+ * <p>
28
+ * Use <code>getMessage()</code> to retrieve a concatenation of the composite exceptions.
34
29
*/
35
30
public final class CompositeException extends RuntimeException {
36
31
37
32
private static final long serialVersionUID = 3026362227162912146L ;
38
33
39
34
private final List <Throwable > exceptions ;
40
35
private final String message ;
36
+ private final Throwable cause ;
41
37
42
38
public CompositeException (String messagePrefix , Collection <Throwable > errors ) {
43
- Set <Throwable > deDupedExceptions = new LinkedHashSet <Throwable >();
44
39
List <Throwable > _exceptions = new ArrayList <Throwable >();
45
- for ( Throwable ex : errors ) {
46
- if ( ex instanceof CompositeException ) {
47
- deDupedExceptions . addAll ((( CompositeException ) ex ). getExceptions () );
48
- } else {
49
- deDupedExceptions . add ( ex );
50
- }
40
+ CompositeExceptionCausalChain _cause = new CompositeExceptionCausalChain ();
41
+ int count = errors . size ();
42
+ errors = removeDuplicatedCauses ( errors );
43
+ for ( Throwable e : errors ) {
44
+ attachCallingThreadStack ( _cause , e );
45
+ _exceptions . add ( e );
51
46
}
52
-
53
- _exceptions .addAll (deDupedExceptions );
54
47
this .exceptions = Collections .unmodifiableList (_exceptions );
55
- this .message = exceptions .size () + " exceptions occurred. See them in causal chain below." ;
48
+ this .message = count + " exceptions occurred. See them in causal chain below." ;
49
+ this .cause = _cause ;
56
50
}
57
51
58
52
public CompositeException (Collection <Throwable > errors ) {
@@ -76,106 +70,80 @@ public String getMessage() {
76
70
77
71
@ Override
78
72
public synchronized Throwable getCause () {
79
- return null ;
80
- }
81
-
82
- /**
83
- * All of the following printStackTrace functionality is derived from JDK Throwable printStackTrace.
84
- * In particular, the PrintStreamOrWriter abstraction is copied wholesale.
85
- *
86
- * Changes from the official JDK implementation:
87
- * * No infinite loop detection
88
- * * Smaller critical section holding printStream lock
89
- * * Explicit knowledge about exceptions List that this loops through
90
- */
91
- @ Override
92
- public void printStackTrace () {
93
- printStackTrace (System .err );
73
+ return cause ;
94
74
}
95
75
96
- @ Override
97
- public void printStackTrace (PrintStream s ) {
98
- printStackTrace (new WrappedPrintStream (s ));
99
- }
100
-
101
- @ Override
102
- public void printStackTrace (PrintWriter s ) {
103
- printStackTrace (new WrappedPrintWriter (s ));
104
- }
105
-
106
- /**
107
- * Special handling for printing out a CompositeException
108
- * Loop through all inner exceptions and print them out
109
- * @param s stream to print to
110
- */
111
- private void printStackTrace (PrintStreamOrWriter s ) {
112
- StringBuilder bldr = new StringBuilder ();
113
- bldr .append (this ).append ("\n " );
114
- for (StackTraceElement myStackElement : getStackTrace ()) {
115
- bldr .append ("\t at " ).append (myStackElement ).append ("\n " );
116
- }
117
- int i = 1 ;
118
- for (Throwable ex : exceptions ) {
119
- bldr .append (" ComposedException " ).append (i ).append (" :" ).append ("\n " );
120
- appendStackTrace (bldr , ex , "\t " );
121
- i ++;
76
+ private Collection <Throwable > removeDuplicatedCauses (Collection <Throwable > errors ) {
77
+ Set <Throwable > duplicated = new HashSet <Throwable >();
78
+ for (Throwable cause : errors ) {
79
+ for (Throwable error : errors ) {
80
+ if (cause == error || duplicated .contains (error )) {
81
+ continue ;
82
+ }
83
+ while (error .getCause () != null ) {
84
+ error = error .getCause ();
85
+ if (error == cause ) {
86
+ duplicated .add (cause );
87
+ break ;
88
+ }
89
+ }
90
+ }
122
91
}
123
- synchronized (s .lock ()) {
124
- s .println (bldr .toString ());
92
+ if (!duplicated .isEmpty ()) {
93
+ errors = new ArrayList <Throwable >(errors );
94
+ errors .removeAll (duplicated );
125
95
}
96
+ return errors ;
126
97
}
127
98
128
- private void appendStackTrace (StringBuilder bldr , Throwable ex , String prefix ) {
129
- bldr .append (prefix ).append (ex ).append ("\n " );
130
- for (StackTraceElement stackElement : ex .getStackTrace ()) {
131
- bldr .append ("\t \t at " ).append (stackElement ).append ("\n " );
132
- }
133
- if (ex .getCause () != null ) {
134
- bldr .append ("\t Caused by: " );
135
- appendStackTrace (bldr , ex .getCause (), "" );
99
+ @ SuppressWarnings ("unused" )
100
+ // useful when debugging but don't want to make part of publicly supported API
101
+ private static String getStackTraceAsString (StackTraceElement [] stack ) {
102
+ StringBuilder s = new StringBuilder ();
103
+ boolean firstLine = true ;
104
+ for (StackTraceElement e : stack ) {
105
+ if (e .toString ().startsWith ("java.lang.Thread.getStackTrace" )) {
106
+ // we'll ignore this one
107
+ continue ;
108
+ }
109
+ if (!firstLine ) {
110
+ s .append ("\n \t " );
111
+ }
112
+ s .append (e .toString ());
113
+ firstLine = false ;
136
114
}
115
+ return s .toString ();
137
116
}
138
117
139
- private abstract static class PrintStreamOrWriter {
140
- /** Returns the object to be locked when using this StreamOrWriter */
141
- abstract Object lock ();
142
-
143
- /** Prints the specified string as a line on this StreamOrWriter */
144
- abstract void println (Object o );
145
- }
146
-
147
- /**
148
- * Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation
149
- */
150
- private static class WrappedPrintStream extends PrintStreamOrWriter {
151
- private final PrintStream printStream ;
152
-
153
- WrappedPrintStream (PrintStream printStream ) {
154
- this .printStream = printStream ;
155
- }
118
+ /* package-private */ static void attachCallingThreadStack (Throwable e , Throwable cause ) {
119
+ Set <Throwable > seenCauses = new HashSet <Throwable >();
156
120
157
- Object lock () {
158
- return printStream ;
121
+ while (e .getCause () != null ) {
122
+ e = e .getCause ();
123
+ if (seenCauses .contains (e .getCause ())) {
124
+ break ;
125
+ } else {
126
+ seenCauses .add (e .getCause ());
127
+ }
159
128
}
160
-
161
- void println (Object o ) {
162
- printStream .println (o );
129
+ // we now have 'e' as the last in the chain
130
+ try {
131
+ e .initCause (cause );
132
+ } catch (Throwable t ) {
133
+ // ignore
134
+ // the javadocs say that some Throwables (depending on how they're made) will never
135
+ // let me call initCause without blowing up even if it returns null
163
136
}
164
137
}
165
138
166
- private static class WrappedPrintWriter extends PrintStreamOrWriter {
167
- private final PrintWriter printWriter ;
139
+ /* package-private */ final static class CompositeExceptionCausalChain extends RuntimeException {
140
+ private static final long serialVersionUID = 3875212506787802066L ;
141
+ /* package-private */ static String MESSAGE = "Chain of Causes for CompositeException In Order Received =>" ;
168
142
169
- WrappedPrintWriter (PrintWriter printWriter ) {
170
- this .printWriter = printWriter ;
171
- }
172
-
173
- Object lock () {
174
- return printWriter ;
175
- }
176
-
177
- void println (Object o ) {
178
- printWriter .println (o );
143
+ @ Override
144
+ public String getMessage () {
145
+ return MESSAGE ;
179
146
}
180
147
}
148
+
181
149
}
0 commit comments