io.reactivex.rxjava3.exceptions.CompositeException Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson-all Show documentation
Show all versions of redisson-all Show documentation
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
/*
* Copyright (c) 2016-present, RxJava Contributors.
*
* 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 io.reactivex.rxjava3.exceptions;
import java.io.*;
import java.util.*;
import io.reactivex.rxjava3.annotations.NonNull;
/**
* Represents 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 composite 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;
private Throwable cause;
/**
* Constructs a CompositeException with the given array of Throwables as the
* list of suppressed exceptions.
* @param exceptions the Throwables to have as initially suppressed exceptions
*
* @throws IllegalArgumentException if exceptions
is empty.
*/
public CompositeException(@NonNull Throwable... exceptions) {
this(exceptions == null ?
Collections.singletonList(new NullPointerException("exceptions was null")) : Arrays.asList(exceptions));
}
/**
* Constructs a CompositeException with the given array of Throwables as the
* list of suppressed exceptions.
* @param errors the Throwables to have as initially suppressed exceptions
*
* @throws IllegalArgumentException if errors
is empty.
*/
public CompositeException(@NonNull Iterable extends Throwable> errors) {
Set deDupedExceptions = new LinkedHashSet<>();
if (errors != null) {
for (Throwable ex : errors) {
if (ex instanceof CompositeException) {
deDupedExceptions.addAll(((CompositeException) ex).getExceptions());
} else
if (ex != null) {
deDupedExceptions.add(ex);
} else {
deDupedExceptions.add(new NullPointerException("Throwable was null!"));
}
}
} else {
deDupedExceptions.add(new NullPointerException("errors was null"));
}
if (deDupedExceptions.isEmpty()) {
throw new IllegalArgumentException("errors is empty");
}
List localExceptions = new ArrayList<>(deDupedExceptions);
this.exceptions = Collections.unmodifiableList(localExceptions);
this.message = exceptions.size() + " exceptions occurred. ";
}
/**
* 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
*/
@NonNull
public List getExceptions() {
return exceptions;
}
@Override
@NonNull
public String getMessage() {
return message;
}
@Override
@NonNull
public synchronized Throwable getCause() { // NOPMD
if (cause == null) {
String separator = System.getProperty("line.separator");
if (exceptions.size() > 1) {
Map seenCauses = new IdentityHashMap<>();
StringBuilder aggregateMessage = new StringBuilder();
aggregateMessage.append("Multiple exceptions (").append(exceptions.size()).append(")").append(separator);
for (Throwable inner : exceptions) {
int depth = 0;
while (inner != null) {
for (int i = 0; i < depth; i++) {
aggregateMessage.append(" ");
}
aggregateMessage.append("|-- ");
aggregateMessage.append(inner.getClass().getCanonicalName()).append(": ");
String innerMessage = inner.getMessage();
if (innerMessage != null && innerMessage.contains(separator)) {
aggregateMessage.append(separator);
for (String line : innerMessage.split(separator)) {
for (int i = 0; i < depth + 2; i++) {
aggregateMessage.append(" ");
}
aggregateMessage.append(line).append(separator);
}
} else {
aggregateMessage.append(innerMessage);
aggregateMessage.append(separator);
}
for (int i = 0; i < depth + 2; i++) {
aggregateMessage.append(" ");
}
StackTraceElement[] st = inner.getStackTrace();
if (st.length > 0) {
aggregateMessage.append("at ").append(st[0]).append(separator);
}
if (!seenCauses.containsKey(inner)) {
seenCauses.put(inner, true);
inner = inner.getCause();
depth++;
} else {
inner = inner.getCause();
if (inner != null) {
for (int i = 0; i < depth + 2; i++) {
aggregateMessage.append(" ");
}
aggregateMessage.append("|-- ");
aggregateMessage.append("(cause not expanded again) ");
aggregateMessage.append(inner.getClass().getCanonicalName()).append(": ");
aggregateMessage.append(inner.getMessage());
aggregateMessage.append(separator);
}
break;
}
}
}
cause = new ExceptionOverview(aggregateMessage.toString().trim());
} else {
cause = exceptions.get(0);
}
}
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 output
* stream to print to
*/
private void printStackTrace(PrintStreamOrWriter output) {
output.append(this).append("\n");
for (StackTraceElement myStackElement : getStackTrace()) {
output.append("\tat ").append(myStackElement).append("\n");
}
int i = 1;
for (Throwable ex : exceptions) {
output.append(" ComposedException ").append(i).append(" :\n");
appendStackTrace(output, ex, "\t");
i++;
}
output.append("\n");
}
private void appendStackTrace(PrintStreamOrWriter output, Throwable ex, String prefix) {
output.append(prefix).append(ex).append('\n');
for (StackTraceElement stackElement : ex.getStackTrace()) {
output.append("\t\tat ").append(stackElement).append('\n');
}
if (ex.getCause() != null) {
output.append("\tCaused by: ");
appendStackTrace(output, ex.getCause(), "");
}
}
abstract static class PrintStreamOrWriter {
/**
* Prints the object's string representation via the underlying PrintStream or PrintWriter.
* @param o the object to print
* @return this
*/
abstract PrintStreamOrWriter append(Object o);
}
/**
* Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation.
*/
static final class WrappedPrintStream extends PrintStreamOrWriter {
private final PrintStream printStream;
WrappedPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
@Override
WrappedPrintStream append(Object o) {
printStream.print(o);
return this;
}
}
/**
* Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation.
*/
static final class WrappedPrintWriter extends PrintStreamOrWriter {
private final PrintWriter printWriter;
WrappedPrintWriter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
@Override
WrappedPrintWriter append(Object o) {
printWriter.print(o);
return this;
}
}
/**
* Contains a formatted message with a simplified representation of the exception graph
* contained within the CompositeException.
*/
static final class ExceptionOverview extends RuntimeException {
private static final long serialVersionUID = 3875212506787802066L;
ExceptionOverview(String message) {
super(message);
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
/**
* Returns the number of suppressed exceptions.
* @return the number of suppressed exceptions
*/
public int size() {
return exceptions.size();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy