rx.exceptions.CompositeException Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxjava-core Show documentation
Show all versions of rxjava-core Show documentation
rxjava-core developed by Netflix
The newest version!
/**
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.exceptions;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* An Exception that is a composite of one or more other Exceptions. A {@code CompositeException} does not
* modify the structure of any exception it wraps, but at print-time it iterates through the list of
* Throwables contained in the composit in order to print them all.
*
* Its invariant is to contain an immutable, ordered (by insertion order), unique list of non-composite
* exceptions. You can retrieve individual exceptions in this list with {@link #getExceptions()}.
*
* The {@link #printStackTrace()} implementation handles the StackTrace in a customized way instead of using
* {@code getCause()} so that it can avoid circular references.
*
* If you invoke {@link #getCause()}, it will lazily create the causal chain but will stop if it finds any
* Throwable in the chain that it has already seen.
*/
public final class CompositeException extends RuntimeException {
private static final long serialVersionUID = 3026362227162912146L;
private final List exceptions;
private final String message;
public CompositeException(String messagePrefix, Collection extends Throwable> errors) {
Set deDupedExceptions = new LinkedHashSet();
List _exceptions = new ArrayList();
for (Throwable ex : errors) {
if (ex instanceof CompositeException) {
deDupedExceptions.addAll(((CompositeException) ex).getExceptions());
} else {
deDupedExceptions.add(ex);
}
}
_exceptions.addAll(deDupedExceptions);
this.exceptions = Collections.unmodifiableList(_exceptions);
this.message = exceptions.size() + " exceptions occurred. ";
}
public CompositeException(Collection extends Throwable> errors) {
this(null, errors);
}
/**
* Retrieves the list of exceptions that make up the {@code CompositeException}
*
* @return the exceptions that make up the {@code CompositeException}, as a {@link List} of {@link Throwable}s
*/
public List getExceptions() {
return exceptions;
}
@Override
public String getMessage() {
return message;
}
private Throwable cause = null;
@Override
public synchronized Throwable getCause() {
if (cause == null) {
// we lazily generate this causal chain if this is called
CompositeExceptionCausalChain _cause = new CompositeExceptionCausalChain();
Set seenCauses = new HashSet();
Throwable chain = _cause;
for (Throwable e : exceptions) {
if (seenCauses.contains(e)) {
// already seen this outer Throwable so skip
continue;
}
seenCauses.add(e);
List listOfCauses = getListOfCauses(e);
// check if any of them have been seen before
for(Throwable child : listOfCauses) {
if (seenCauses.contains(child)) {
// already seen this outer Throwable so skip
e = new RuntimeException("Duplicate found in causal chain so cropping to prevent loop ...");
continue;
}
seenCauses.add(child);
}
// we now have 'e' as the last in the chain
try {
chain.initCause(e);
} catch (Throwable t) {
// ignore
// the javadocs say that some Throwables (depending on how they're made) will never
// let me call initCause without blowing up even if it returns null
}
chain = chain.getCause();
}
cause = _cause;
}
return cause;
}
/**
* All of the following {@code printStackTrace} functionality is derived from JDK {@link Throwable}
* {@code printStackTrace}. In particular, the {@code PrintStreamOrWriter} abstraction is copied wholesale.
*
* Changes from the official JDK implementation:
* - no infinite loop detection
* - smaller critical section holding {@link PrintStream} lock
* - explicit knowledge about the exceptions {@link List} that this loops through
*
*/
@Override
public void printStackTrace() {
printStackTrace(System.err);
}
@Override
public void printStackTrace(PrintStream s) {
printStackTrace(new WrappedPrintStream(s));
}
@Override
public void printStackTrace(PrintWriter s) {
printStackTrace(new WrappedPrintWriter(s));
}
/**
* Special handling for printing out a {@code CompositeException}.
* Loops through all inner exceptions and prints them out.
*
* @param s
* stream to print to
*/
private void printStackTrace(PrintStreamOrWriter s) {
StringBuilder bldr = new StringBuilder();
bldr.append(this).append("\n");
for (StackTraceElement myStackElement : getStackTrace()) {
bldr.append("\tat ").append(myStackElement).append("\n");
}
int i = 1;
for (Throwable ex : exceptions) {
bldr.append(" ComposedException ").append(i).append(" :").append("\n");
appendStackTrace(bldr, ex, "\t");
i++;
}
synchronized (s.lock()) {
s.println(bldr.toString());
}
}
private void appendStackTrace(StringBuilder bldr, Throwable ex, String prefix) {
bldr.append(prefix).append(ex).append("\n");
for (StackTraceElement stackElement : ex.getStackTrace()) {
bldr.append("\t\tat ").append(stackElement).append("\n");
}
if (ex.getCause() != null) {
bldr.append("\tCaused by: ");
appendStackTrace(bldr, ex.getCause(), "");
}
}
private abstract static class PrintStreamOrWriter {
/** Returns the object to be locked when using this StreamOrWriter */
abstract Object lock();
/** Prints the specified string as a line on this StreamOrWriter */
abstract void println(Object o);
}
/**
* Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation
*/
private static class WrappedPrintStream extends PrintStreamOrWriter {
private final PrintStream printStream;
WrappedPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
Object lock() {
return printStream;
}
void println(Object o) {
printStream.println(o);
}
}
private static class WrappedPrintWriter extends PrintStreamOrWriter {
private final PrintWriter printWriter;
WrappedPrintWriter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
Object lock() {
return printWriter;
}
void println(Object o) {
printWriter.println(o);
}
}
/* package-private */final static class CompositeExceptionCausalChain extends RuntimeException {
private static final long serialVersionUID = 3875212506787802066L;
/* package-private */static String MESSAGE = "Chain of Causes for CompositeException In Order Received =>";
@Override
public String getMessage() {
return MESSAGE;
}
}
private final List getListOfCauses(Throwable ex) {
List list = new ArrayList();
Throwable root = ex.getCause();
if (root == null) {
return list;
} else {
while(true) {
list.add(root);
if (root.getCause() == null) {
return list;
} else {
root = root.getCause();
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy