org.apache.flink.runtime.util.SerializedThrowable Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.flink.runtime.util;
import org.apache.flink.util.ExceptionUtils;
import org.apache.flink.util.InstantiationUtil;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
/**
* Utility class for dealing with user-defined Throwable types that are serialized (for
* example during RPC/Actor communication), but cannot be resolved with the default
* class loader.
*
* This exception mimics the original exception with respect to message and stack trace,
* and contains the original exception in serialized form. The original exception
* can be re-obtained by supplying the appropriate class loader.
*/
public class SerializedThrowable extends Exception implements Serializable {
private static final long serialVersionUID = 7284183123441947635L;
/** The original exception in serialized form */
private final byte[] serializedException;
/** Name of the original error class */
private final String originalErrorClassName;
/** The original stack trace, to be printed */
private final String fullStingifiedStackTrace;
/** The original exception, not transported via serialization,
* because the class may not be part of the system class loader.
* In addition, we make sure our cached references to not prevent
* unloading the exception class. */
private transient WeakReference cachedException;
/**
* Create a new SerializedThrowable.
*
* @param exception The exception to serialize.
*/
public SerializedThrowable(Throwable exception) {
this(exception, new HashSet());
}
private SerializedThrowable(Throwable exception, Set alreadySeen) {
super(getMessageOrError(exception));
if (!(exception instanceof SerializedThrowable)) {
// serialize and memoize the original message
byte[] serialized;
try {
serialized = InstantiationUtil.serializeObject(exception);
}
catch (Throwable t) {
serialized = null;
}
this.serializedException = serialized;
this.cachedException = new WeakReference(exception);
// record the original exception's properties (name, stack prints)
this.originalErrorClassName = exception.getClass().getName();
this.fullStingifiedStackTrace = ExceptionUtils.stringifyException(exception);
// mimic the original exception's stack trace
setStackTrace(exception.getStackTrace());
// mimic the original exception's cause
if (exception.getCause() == null) {
initCause(null);
}
else {
// exception causes may by cyclic, so we truncate the cycle when we find it
if (alreadySeen.add(exception)) {
// we are not in a cycle, yet
initCause(new SerializedThrowable(exception.getCause(), alreadySeen));
}
}
}
else {
// copy from that serialized throwable
SerializedThrowable other = (SerializedThrowable) exception;
this.serializedException = other.serializedException;
this.originalErrorClassName = other.originalErrorClassName;
this.fullStingifiedStackTrace = other.fullStingifiedStackTrace;
this.cachedException = other.cachedException;
this.setStackTrace(other.getStackTrace());
this.initCause(other.getCause());
}
}
public Throwable deserializeError(ClassLoader classloader) {
if (serializedException == null) {
// failed to serialize the original exception
// return this SerializedThrowable as a stand in
return this;
}
Throwable cached = cachedException == null ? null : cachedException.get();
if (cached == null) {
try {
cached = InstantiationUtil.deserializeObject(serializedException, classloader);
cachedException = new WeakReference(cached);
}
catch (Throwable t) {
// something went wrong
// return this SerializedThrowable as a stand in
return this;
}
}
return cached;
}
public String getOriginalErrorClassName() {
return originalErrorClassName;
}
// ------------------------------------------------------------------------
// Override the behavior of Throwable
// ------------------------------------------------------------------------
@Override
public void printStackTrace(PrintStream s) {
s.print(fullStingifiedStackTrace);
s.flush();
}
@Override
public void printStackTrace(PrintWriter s) {
s.print(fullStingifiedStackTrace);
s.flush();
}
@Override
public String toString() {
String message = getLocalizedMessage();
return (message != null) ? (originalErrorClassName + ": " + message) : originalErrorClassName;
}
// ------------------------------------------------------------------------
// Static utilities
// ------------------------------------------------------------------------
public static Throwable get(Throwable serThrowable, ClassLoader loader) {
if (serThrowable instanceof SerializedThrowable) {
return ((SerializedThrowable)serThrowable).deserializeError(loader);
} else {
return serThrowable;
}
}
private static String getMessageOrError(Throwable error) {
try {
return error.getMessage();
}
catch (Throwable t) {
return "(failed to get message)";
}
}
}