001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.procedure2; 019 020import java.io.IOException; 021import org.apache.hadoop.hbase.util.ForeignExceptionUtil; 022import org.apache.hadoop.ipc.RemoteException; 023import org.apache.yetus.audience.InterfaceAudience; 024import org.apache.yetus.audience.InterfaceStability; 025 026import org.apache.hadoop.hbase.shaded.protobuf.generated.ErrorHandlingProtos.ForeignExceptionMessage; 027 028/** 029 * A RemoteProcedureException is an exception from another thread or process. 030 * <p> 031 * RemoteProcedureExceptions are sent to 'remote' peers to signal an abort in the face of failures. 032 * When serialized for transmission we encode using Protobufs to ensure version compatibility. 033 * <p> 034 * RemoteProcedureException exceptions contain a Throwable as its cause. This can be a "regular" 035 * exception generated locally or a ProxyThrowable that is a representation of the original 036 * exception created on original 'remote' source. These ProxyThrowables have their their stacks 037 * traces and messages overridden to reflect the original 'remote' exception. 038 */ 039@InterfaceAudience.Private 040@InterfaceStability.Evolving 041@SuppressWarnings("serial") 042public class RemoteProcedureException extends ProcedureException { 043 /** 044 * Name of the throwable's source such as a host or thread name. Must be non-null. 045 */ 046 private final String source; 047 048 /** 049 * Create a new RemoteProcedureException that can be serialized. It is assumed that this came form 050 * a local source. 051 * @param source the host or thread name of the source 052 * @param cause the actual cause of the exception 053 */ 054 public RemoteProcedureException(String source, Throwable cause) { 055 super(cause); 056 assert source != null; 057 assert cause != null; 058 this.source = source; 059 } 060 061 public String getSource() { 062 return source; 063 } 064 065 public Exception unwrapRemoteException() { 066 final Throwable cause = getCause(); 067 if (cause instanceof RemoteException) { 068 return ((RemoteException) cause).unwrapRemoteException(); 069 } 070 if (cause instanceof Exception) { 071 return (Exception) cause; 072 } 073 return new Exception(cause); 074 } 075 076 // NOTE: Does not throw DoNotRetryIOE because it does not 077 // have access (DNRIOE is in the client module). Use 078 // MasterProcedureUtil.unwrapRemoteIOException if need to 079 // throw DNRIOE. 080 public IOException unwrapRemoteIOException() { 081 final Exception cause = unwrapRemoteException(); 082 if (cause instanceof IOException) { 083 return (IOException) cause; 084 } 085 return new IOException(cause); 086 } 087 088 @Override 089 public String toString() { 090 String className = getCause().getClass().getName(); 091 return className + " via " + getSource() + ":" + getLocalizedMessage(); 092 } 093 094 /** 095 * Converts a RemoteProcedureException to an array of bytes. 096 * @param source the name of the external exception source 097 * @param t the "local" external exception (local) 098 * @return protobuf serialized version of RemoteProcedureException 099 */ 100 public static byte[] serialize(String source, Throwable t) { 101 return toProto(source, t).toByteArray(); 102 } 103 104 /** 105 * Takes a series of bytes and tries to generate an RemoteProcedureException instance for it. 106 * @param bytes the bytes to generate the {@link RemoteProcedureException} from 107 * @return the ForeignExcpetion instance 108 * @throws IOException if there was deserialization problem this is thrown. 109 */ 110 public static RemoteProcedureException deserialize(byte[] bytes) throws IOException { 111 return fromProto(ForeignExceptionMessage.parseFrom(bytes)); 112 } 113 114 public ForeignExceptionMessage convert() { 115 return ForeignExceptionUtil.toProtoForeignException(getSource(), getCause()); 116 } 117 118 public static ForeignExceptionMessage toProto(String source, Throwable t) { 119 return ForeignExceptionUtil.toProtoForeignException(source, t); 120 } 121 122 public static RemoteProcedureException fromProto(final ForeignExceptionMessage eem) { 123 return new RemoteProcedureException(eem.getSource(), ForeignExceptionUtil.toException(eem)); 124 } 125}