15
15
*/
16
16
package rx .exceptions ;
17
17
18
+ import java .io .PrintStream ;
19
+ import java .io .PrintWriter ;
18
20
import java .util .ArrayList ;
19
21
import java .util .Collection ;
20
22
import java .util .Collections ;
21
- import java .util .HashSet ;
23
+ import java .util .LinkedHashSet ;
22
24
import java .util .List ;
23
25
import java .util .Set ;
24
26
25
27
/**
26
28
* Exception that is a composite of 1 or more other exceptions.
27
- * <p>
28
- * Use <code>getMessage()</code> to retrieve a concatenation of the composite 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()}
29
34
*/
30
35
public final class CompositeException extends RuntimeException {
31
36
32
37
private static final long serialVersionUID = 3026362227162912146L ;
33
38
34
39
private final List <Throwable > exceptions ;
35
40
private final String message ;
36
- private final Throwable cause ;
37
41
38
- public CompositeException (String messagePrefix , Collection <Throwable > errors ) {
42
+ public CompositeException (String messagePrefix , Collection <? extends Throwable > errors ) {
43
+ Set <Throwable > deDupedExceptions = new LinkedHashSet <Throwable >();
39
44
List <Throwable > _exceptions = new ArrayList <Throwable >();
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 );
45
+ for ( Throwable ex : errors ) {
46
+ if ( ex instanceof CompositeException ) {
47
+ deDupedExceptions . addAll ((( CompositeException ) ex ). getExceptions () );
48
+ } else {
49
+ deDupedExceptions . add ( ex );
50
+ }
46
51
}
52
+
53
+ _exceptions .addAll (deDupedExceptions );
47
54
this .exceptions = Collections .unmodifiableList (_exceptions );
48
-
49
- String msg = count + " exceptions occurred. See them in causal chain below." ;
50
- if (messagePrefix != null ) {
51
- msg = messagePrefix + " " + msg ;
52
- }
53
- this .message = msg ;
54
- this .cause = _cause ;
55
+ this .message = exceptions .size () + " exceptions occurred. See them in causal chain below." ;
55
56
}
56
57
57
- public CompositeException (Collection <Throwable > errors ) {
58
+ public CompositeException (Collection <? extends Throwable > errors ) {
58
59
this (null , errors );
59
60
}
60
61
@@ -75,80 +76,106 @@ public String getMessage() {
75
76
76
77
@ Override
77
78
public synchronized Throwable getCause () {
78
- return cause ;
79
+ return null ;
79
80
}
80
81
81
- private Collection <Throwable > removeDuplicatedCauses (Collection <Throwable > errors ) {
82
- Set <Throwable > duplicated = new HashSet <Throwable >();
83
- for (Throwable cause : errors ) {
84
- for (Throwable error : errors ) {
85
- if (cause == error || duplicated .contains (error )) {
86
- continue ;
87
- }
88
- while (error .getCause () != null ) {
89
- error = error .getCause ();
90
- if (error == cause ) {
91
- duplicated .add (cause );
92
- break ;
93
- }
94
- }
95
- }
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 " );
96
116
}
97
- if (!duplicated .isEmpty ()) {
98
- errors = new ArrayList <Throwable >(errors );
99
- errors .removeAll (duplicated );
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 ());
100
125
}
101
- return errors ;
102
126
}
103
127
104
- @ SuppressWarnings ("unused" )
105
- // useful when debugging but don't want to make part of publicly supported API
106
- private static String getStackTraceAsString (StackTraceElement [] stack ) {
107
- StringBuilder s = new StringBuilder ();
108
- boolean firstLine = true ;
109
- for (StackTraceElement e : stack ) {
110
- if (e .toString ().startsWith ("java.lang.Thread.getStackTrace" )) {
111
- // we'll ignore this one
112
- continue ;
113
- }
114
- if (!firstLine ) {
115
- s .append ("\n \t " );
116
- }
117
- s .append (e .toString ());
118
- firstLine = false ;
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 (), "" );
119
136
}
120
- return s .toString ();
121
137
}
122
138
123
- /* package-private */ static void attachCallingThreadStack (Throwable e , Throwable cause ) {
124
- Set <Throwable > seenCauses = new HashSet <Throwable >();
139
+ private abstract static class PrintStreamOrWriter {
140
+ /** Returns the object to be locked when using this StreamOrWriter */
141
+ abstract Object lock ();
125
142
126
- while (e .getCause () != null ) {
127
- e = e .getCause ();
128
- if (seenCauses .contains (e .getCause ())) {
129
- break ;
130
- } else {
131
- seenCauses .add (e .getCause ());
132
- }
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 ;
133
155
}
134
- // we now have 'e' as the last in the chain
135
- try {
136
- e . initCause ( cause ) ;
137
- } catch ( Throwable t ) {
138
- // ignore
139
- // the javadocs say that some Throwables (depending on how they're made) will never
140
- // let me call initCause without blowing up even if it returns null
156
+
157
+ Object lock () {
158
+ return printStream ;
159
+ }
160
+
161
+ void println ( Object o ) {
162
+ printStream . println ( o );
141
163
}
142
164
}
143
165
144
- /* package-private */ final static class CompositeExceptionCausalChain extends RuntimeException {
145
- private static final long serialVersionUID = 3875212506787802066L ;
146
- /* package-private */ static String MESSAGE = "Chain of Causes for CompositeException In Order Received =>" ;
166
+ private static class WrappedPrintWriter extends PrintStreamOrWriter {
167
+ private final PrintWriter printWriter ;
147
168
148
- @ Override
149
- public String getMessage () {
150
- return MESSAGE ;
169
+ WrappedPrintWriter (PrintWriter printWriter ) {
170
+ this .printWriter = printWriter ;
151
171
}
152
- }
153
172
173
+ Object lock () {
174
+ return printWriter ;
175
+ }
176
+
177
+ void println (Object o ) {
178
+ printWriter .println (o );
179
+ }
180
+ }
154
181
}
0 commit comments