// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.api;
import javax.annotation.concurrent.*;
/**
* Masquerades as another exception instance. Enables transferring exception stack traces between JVMs, without
* requiring the remote JVM to have every custom exception class on its classpath.
*/
@ThreadSafe
public class StackTrace extends Throwable {
private final String exceptionClass;
private final String toString;
public static StackTrace from(Throwable original) {
if (original == null) {
return null;
}
if (original instanceof StackTrace) {
return (StackTrace) original;
}
return new Builder()
.setExceptionClass(original.getClass().getName())
.setToString(original.toString())
.setMessage(original.getMessage())
.setStackTrace(original.getStackTrace())
.setCause(original.getCause())
.setSuppressed(original.getSuppressed())
.build();
}
private StackTrace(String exceptionClass, String toString, String message, StackTrace cause) {
super(message, cause);
this.exceptionClass = exceptionClass;
this.toString = toString;
}
@Override
public synchronized Throwable fillInStackTrace() {
// Our stack trace will be replaced immediately, so no need to generate it
return this;
}
public String getExceptionClass() {
return exceptionClass;
}
@Override
public String toString() {
return toString;
}
@NotThreadSafe
public static class Builder {
private String exceptionClass;
private String toString;
private String message;
private StackTraceElement[] stackTrace;
private Throwable cause;
private Throwable[] suppressed;
public Builder setExceptionClass(String exceptionClass) {
this.exceptionClass = exceptionClass;
return this;
}
public Builder setToString(String toString) {
this.toString = toString;
return this;
}
public Builder setMessage(String message) {
this.message = message;
return this;
}
public Builder setStackTrace(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
return this;
}
public Builder setCause(Throwable cause) {
this.cause = cause;
return this;
}
public Builder setSuppressed(Throwable[] suppressed) {
this.suppressed = suppressed;
return this;
}
public StackTrace build() {
StackTrace st = new StackTrace(exceptionClass, toString, message, StackTrace.from(cause));
st.setStackTrace(stackTrace);
for (Throwable t : suppressed) {
st.addSuppressed(StackTrace.from(t));
}
return st;
}
}
}