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;
021
022import org.apache.hadoop.ipc.RemoteException;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.apache.hadoop.hbase.shaded.protobuf.generated.ErrorHandlingProtos.ForeignExceptionMessage;
025import org.apache.hadoop.hbase.util.ForeignExceptionUtil;
026
027/**
028 * A RemoteProcedureException is an exception from another thread or process.
029 * <p>
030 * RemoteProcedureExceptions are sent to 'remote' peers to signal an abort in the face of failures.
031 * When serialized for transmission we encode using Protobufs to ensure version compatibility.
032 * <p>
033 * RemoteProcedureException exceptions contain a Throwable as its cause.
034 * This can be a "regular" exception generated locally or a ProxyThrowable that is a representation
035 * of the original exception created on original 'remote' source.  These ProxyThrowables have their
036 * their stacks traces and messages overridden to reflect the original 'remote' exception.
037 */
038@InterfaceAudience.Private
039@SuppressWarnings("serial")
040public class RemoteProcedureException extends ProcedureException {
041
042  /**
043   * Name of the throwable's source such as a host or thread name.  Must be non-null.
044   */
045  private final String source;
046
047  /**
048   * Create a new RemoteProcedureException that can be serialized.
049   * It is assumed that this came form a local source.
050   * @param source
051   * @param cause
052   */
053  public RemoteProcedureException(String source, Throwable cause) {
054    super(cause);
055    assert source != null;
056    assert cause != null;
057    this.source = source;
058  }
059
060  public String getSource() {
061    return source;
062  }
063
064  public Exception unwrapRemoteException() {
065    final Throwable cause = getCause();
066    if (cause instanceof RemoteException) {
067      return ((RemoteException)cause).unwrapRemoteException();
068    }
069    if (cause instanceof Exception) {
070      return (Exception)cause;
071    }
072    return new Exception(cause);
073  }
074
075  // NOTE: Does not throw DoNotRetryIOE because it does not
076  // have access (DNRIOE is in the client module). Use
077  // MasterProcedureUtil.unwrapRemoteIOException if need to
078  // throw DNRIOE.
079  public IOException unwrapRemoteIOException() {
080    final Exception cause = unwrapRemoteException();
081    if (cause instanceof IOException) {
082      return (IOException)cause;
083    }
084    return new IOException(cause);
085  }
086
087  @Override
088  public String toString() {
089    String className = getCause().getClass().getName();
090    return className + " via " + getSource() + ":" + getLocalizedMessage();
091  }
092
093  /**
094   * Converts a RemoteProcedureException to an array of bytes.
095   * @param source the name of the external exception source
096   * @param t the "local" external exception (local)
097   * @return protobuf serialized version of RemoteProcedureException
098   */
099  public static byte[] serialize(String source, Throwable t) {
100    return toProto(source, t).toByteArray();
101  }
102
103  /**
104   * Takes a series of bytes and tries to generate an RemoteProcedureException instance for it.
105   * @param bytes
106   * @return the ForeignExcpetion instance
107   * @throws InvalidProtocolBufferException if there was deserialization problem this is thrown.
108   */
109  public static RemoteProcedureException deserialize(byte[] bytes) throws IOException {
110    return fromProto(ForeignExceptionMessage.parseFrom(bytes));
111  }
112
113  public ForeignExceptionMessage convert() {
114    return ForeignExceptionUtil.toProtoForeignException(getSource(), getCause());
115  }
116
117  public static ForeignExceptionMessage toProto(String source, Throwable t) {
118    return ForeignExceptionUtil.toProtoForeignException(source, t);
119  }
120
121  public static RemoteProcedureException fromProto(final ForeignExceptionMessage eem) {
122    return new RemoteProcedureException(eem.getSource(), ForeignExceptionUtil.toException(eem));
123  }
124}