blob: fdc7d4d98636c2fc452456de0955743a9b308ed9 [file] [log] [blame]
/*
* Copyright 2013 Google 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 com.google.gwt.core.shared;
import com.google.gwt.core.shared.impl.ThrowableTypeResolver;
/**
* A serializable copy of a {@link Throwable}, including its causes and stack trace. It overrides
* {@code #toString} to mimic original {@link Throwable#toString()} so that {@link #printStackTrace}
* will work as if it is coming from the original exception.
*
* <p>
* This class is especially useful for logging and testing as the emulated Throwable class does not
* serialize recursively and does not serialize the stack trace. This class, as an alternative, can
* be used to transfer the Throwable without losing any of these data, even if the underlying
* Throwable is not serializable.
* <p>
* Please note that, to get more useful stack traces from client side, this class needs to be used
* in conjunction with {@link com.google.gwt.core.server.StackTraceDeobfuscator}.
* <p>
* NOTE: Does not serialize suppressed exceptions to remain compatible with Java 6 and below.
*/
public final class SerializableThrowable extends Throwable {
/**
* Create a new {@link SerializableThrowable} from a provided throwable and its causes
* recursively.
*
* @return a new SerializableThrowable or the passed object itself if it is {@code null} or
* already a SerializableThrowable.
*/
public static SerializableThrowable fromThrowable(Throwable throwable) {
if (throwable instanceof SerializableThrowable) {
return (SerializableThrowable) throwable;
} else if (throwable != null) {
return createSerializable(throwable);
} else {
return null;
}
}
private String typeName;
private boolean exactTypeKnown;
private transient Throwable originalThrowable;
private StackTraceElement[] dummyFieldToIncludeTheTypeInSerialization;
/**
* Constructs a new SerializableThrowable with the specified detail message.
*/
public SerializableThrowable(String designatedType, String message) {
super(message);
this.typeName = designatedType;
}
@Override
public Throwable fillInStackTrace() {
// This is a no-op for optimization as we don't need stack traces to be auto-filled.
// TODO(goktug): Java 7 let's you disable this by constructor flag
return this;
}
/**
* Sets the designated Throwable's type name.
*
* @param typeName the class name of the underlying designated throwable.
* @param isExactType {@code false} if provided type name is not the exact type.
*
* @see #isExactDesignatedTypeKnown()
*/
public void setDesignatedType(String typeName, boolean isExactType) {
this.typeName = typeName;
this.exactTypeKnown = isExactType;
}
/**
* Returns the designated throwable's type name.
*
* @see #isExactDesignatedTypeKnown()
*/
public String getDesignatedType() {
return typeName;
}
/**
* Return {@code true} if provided type name is the exact type of the throwable that is designated
* by this instance. This can return {@code false} if the class metadata is not available in the
* runtime. In that case {@link #getDesignatedType()} will return the type resolved by best-effort
* and may not be the exact type; instead it can be one of the ancestors of the real type that
* this instance designates.
*/
public boolean isExactDesignatedTypeKnown() {
return exactTypeKnown;
}
/**
* Initializes the cause of this throwable.
* <p>
* This method will convert the cause to {@link SerializableThrowable} if it is not already.
*/
@Override
public Throwable initCause(Throwable cause) {
return super.initCause(fromThrowable(cause));
}
/**
* Returns the original throwable that this serializable throwable is derived from. Note that the
* original throwable is kept in a transient field; that is; it will not be transferred to server
* side. In that case this method will return {@code null}.
*/
public Throwable getOriginalThrowable() {
return originalThrowable;
}
@Override
public String toString() {
String type = exactTypeKnown ? typeName : (typeName + "(EXACT TYPE UNKNOWN)");
String msg = getMessage();
return msg == null ? type : (type + ": " + msg);
}
private static SerializableThrowable createSerializable(Throwable t) {
SerializableThrowable throwable = new SerializableThrowable(null, t.getMessage());
throwable.setStackTrace(t.getStackTrace());
throwable.initCause(t.getCause());
throwable.originalThrowable = t;
ThrowableTypeResolver.resolveDesignatedType(throwable, t);
return throwable;
}
}