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 = 0 ;
42
+ for ( Throwable e : errors ) {
43
+ count ++;
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,57 @@ 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 );
94
- }
95
-
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 ++;
122
- }
123
- synchronized (s .lock ()) {
124
- s .println (bldr .toString ());
125
- }
73
+ return cause ;
126
74
}
127
75
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 (), "" );
76
+ @ SuppressWarnings ("unused" )
77
+ // useful when debugging but don't want to make part of publicly supported API
78
+ private static String getStackTraceAsString (StackTraceElement [] stack ) {
79
+ StringBuilder s = new StringBuilder ();
80
+ boolean firstLine = true ;
81
+ for (StackTraceElement e : stack ) {
82
+ if (e .toString ().startsWith ("java.lang.Thread.getStackTrace" )) {
83
+ // we'll ignore this one
84
+ continue ;
85
+ }
86
+ if (!firstLine ) {
87
+ s .append ("\n \t " );
88
+ }
89
+ s .append (e .toString ());
90
+ firstLine = false ;
136
91
}
92
+ return s .toString ();
137
93
}
138
94
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
- }
95
+ /* package-private */ static void attachCallingThreadStack (Throwable e , Throwable cause ) {
96
+ Set <Throwable > seenCauses = new HashSet <Throwable >();
156
97
157
- Object lock () {
158
- return printStream ;
98
+ while (e .getCause () != null ) {
99
+ e = e .getCause ();
100
+ if (seenCauses .contains (e .getCause ())) {
101
+ break ;
102
+ } else {
103
+ seenCauses .add (e .getCause ());
104
+ }
159
105
}
160
-
161
- void println (Object o ) {
162
- printStream .println (o );
106
+ // we now have 'e' as the last in the chain
107
+ try {
108
+ e .initCause (cause );
109
+ } catch (Throwable t ) {
110
+ // ignore
111
+ // the javadocs say that some Throwables (depending on how they're made) will never
112
+ // let me call initCause without blowing up even if it returns null
163
113
}
164
114
}
165
115
166
- private static class WrappedPrintWriter extends PrintStreamOrWriter {
167
- private final PrintWriter printWriter ;
116
+ /* package-private */ final static class CompositeExceptionCausalChain extends RuntimeException {
117
+ private static final long serialVersionUID = 3875212506787802066L ;
118
+ /* package-private */ static String MESSAGE = "Chain of Causes for CompositeException In Order Received =>" ;
168
119
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 );
120
+ @ Override
121
+ public String getMessage () {
122
+ return MESSAGE ;
179
123
}
180
124
}
125
+
181
126
}
0 commit comments