Skip to content

Commit

Permalink
INTERNAL: stacktrace all exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviarla committed Nov 21, 2024
1 parent 132254c commit 1f48bc1
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/main/java/net/spy/memcached/ExceptionMessageFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ private static boolean isBulkOperation(Operation op, Collection<Operation> ops)
}

public static String createCompositeMessage(List<Exception> exceptions) {
if (exceptions == null || exceptions.isEmpty()) {
throw new IllegalArgumentException("At least one exception must be specified");
}

StringBuilder rv = new StringBuilder();
rv.append("Multiple exceptions (");
rv.append(exceptions.size());
Expand Down
96 changes: 95 additions & 1 deletion src/main/java/net/spy/memcached/internal/CompositeException.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package net.spy.memcached.internal;

import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import net.spy.memcached.ExceptionMessageFactory;
Expand All @@ -10,13 +12,105 @@ public class CompositeException extends ExecutionException {

private static final long serialVersionUID = -599478797582490012L;
private final ArrayList<Exception> exceptions = new ArrayList<>();
private Throwable cause;

CompositeException(List<Exception> exceptions) {
public CompositeException(List<Exception> exceptions) {
super(ExceptionMessageFactory.createCompositeMessage(exceptions));
this.exceptions.addAll(exceptions);
}

public List<Exception> getExceptions() {
return exceptions;
}

public int size() {
return exceptions.size();
}

@Override
public synchronized Throwable getCause() {
if (cause == null) {
String separator = System.lineSeparator();
if (exceptions.size() > 1) {
Map<Throwable, Boolean> 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);
}

StackTraceElement[] st = inner.getStackTrace();
for (StackTraceElement stackTraceElement : st) {
for (int j = 0; j < depth + 2; j++) {
aggregateMessage.append(" ");
}
aggregateMessage.append("at ").append(stackTraceElement).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;
}

static final class ExceptionOverview extends RuntimeException {

private static final long serialVersionUID = -641960514509105302L;

ExceptionOverview(String message) {
super(message);
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package net.spy.memcached.internal;

import java.util.ArrayList;
import java.util.List;

import net.spy.memcached.ops.OperationErrorType;
import net.spy.memcached.ops.OperationException;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertTrue;

class CompositeExceptionTest {
@Test
void printStackTraceOfAllExceptions() {
List<Exception> exceptions = new ArrayList<>();
exceptions.add(new OperationException(OperationErrorType.SERVER, "msg1"));
exceptions.add(new OperationException(OperationErrorType.CLIENT, "msg2"));
Exception e = throwError1();
exceptions.add(e);
CompositeException compositeException = new CompositeException(exceptions);
compositeException.printStackTrace();
assertTrue(compositeException.getCause().getMessage()
.contains("net.spy.memcached.ops.OperationException: msg1"));
assertTrue(compositeException.getCause().getMessage()
.contains("net.spy.memcached.ops.OperationException: msg2"));
assertTrue(compositeException.getCause().getMessage()
.contains("net.spy.memcached.ops.OperationException: msg3"));
assertTrue(compositeException.getCause().getMessage()
.contains("at net.spy.memcached.internal.CompositeExceptionTest.throwError2"));
assertTrue(compositeException.getCause().getMessage()
.contains("at net.spy.memcached.internal.CompositeExceptionTest.throwError1"));
}

private Exception throwError1() {
return throwError2();
}
private Exception throwError2() {
return new OperationException(OperationErrorType.SERVER, "msg3");
}
}

0 comments on commit 1f48bc1

Please sign in to comment.