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.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.ref.WeakReference;
/**
* 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;
/** A guaranteed serializable placeholder exception that will be used as
* cause and to capture the original stack trace */
private final Exception placeholder;
/** 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) {
super(getMessageOrError(exception));
if (!(exception instanceof SerializedThrowable)) {
this.cachedException = new WeakReference(exception);
this.originalErrorClassName = exception.getClass().getName();
this.fullStingifiedStackTrace = ExceptionUtils.stringifyException(exception);
this.placeholder = new Exception(
"Serialized representation of " + originalErrorClassName + ": " + getMessage());
this.placeholder.setStackTrace(exception.getStackTrace());
initCause(this.placeholder);
byte[] serialized;
try {
serialized = InstantiationUtil.serializeObject(exception);
}
catch (Throwable t) {
// could not serialize exception. send the stringified version instead
try {
serialized = InstantiationUtil.serializeObject(placeholder);
}
catch (IOException e) {
// this should really never happen, as we only serialize a a standard exception
throw new RuntimeException(e.getMessage(), e);
}
}
this.serializedException = serialized;
}
else {
// copy from that serialized throwable
SerializedThrowable other = (SerializedThrowable) exception;
this.serializedException = other.serializedException;
this.originalErrorClassName = other.originalErrorClassName;
this.fullStingifiedStackTrace = other.fullStingifiedStackTrace;
this.placeholder = other.placeholder;
this.cachedException = other.cachedException;
}
}
public Throwable deserializeError(ClassLoader userCodeClassloader) {
Throwable cached = cachedException == null ? null : cachedException.get();
if (cached == null) {
try {
cached = (Throwable) InstantiationUtil.deserializeObject(serializedException, userCodeClassloader);
cachedException = new WeakReference(cached);
}
catch (Exception e) {
return placeholder;
}
}
return cached;
}
public String getStrigifiedStackTrace() {
return fullStingifiedStackTrace;
}
// ------------------------------------------------------------------------
// Override the behavior of Throwable
// ------------------------------------------------------------------------
@Override
public Throwable getCause() {
return placeholder;
}
@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;
}
@Override
public StackTraceElement[] getStackTrace() {
return placeholder.getStackTrace();
}
// ------------------------------------------------------------------------
// 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)";
}
}
}