001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.exceptions;
021
022import java.io.EOFException;
023import java.io.IOException;
024import java.io.SyncFailedException;
025import java.lang.reflect.UndeclaredThrowableException;
026import java.net.ConnectException;
027import java.net.SocketTimeoutException;
028import java.nio.channels.ClosedChannelException;
029import java.util.Set;
030import java.util.concurrent.TimeoutException;
031
032import org.apache.hadoop.hbase.CallDroppedException;
033import org.apache.hadoop.hbase.CallQueueTooBigException;
034import org.apache.hadoop.hbase.DoNotRetryIOException;
035import org.apache.hadoop.hbase.MultiActionResultTooLarge;
036import org.apache.hadoop.hbase.NotServingRegionException;
037import org.apache.hadoop.hbase.RegionTooBusyException;
038import org.apache.hadoop.hbase.RetryImmediatelyException;
039import org.apache.yetus.audience.InterfaceAudience;
040import org.apache.yetus.audience.InterfaceStability;
041
042import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
043import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet;
044
045import org.apache.hadoop.hbase.ipc.CallTimeoutException;
046import org.apache.hadoop.hbase.ipc.FailedServerException;
047import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
048import org.apache.hadoop.ipc.RemoteException;
049
050@InterfaceAudience.Private
051@InterfaceStability.Evolving
052public final class ClientExceptionsUtil {
053
054  private ClientExceptionsUtil() {}
055
056  public static boolean isMetaClearingException(Throwable cur) {
057    cur = findException(cur);
058
059    if (cur == null) {
060      return true;
061    }
062    return !isSpecialException(cur) || (cur instanceof RegionMovedException)
063        || cur instanceof NotServingRegionException;
064  }
065
066  public static boolean isSpecialException(Throwable cur) {
067    return (cur instanceof RegionMovedException || cur instanceof RegionOpeningException
068        || cur instanceof RegionTooBusyException || cur instanceof RpcThrottlingException
069        || cur instanceof MultiActionResultTooLarge || cur instanceof RetryImmediatelyException
070        || cur instanceof CallQueueTooBigException || cur instanceof CallDroppedException
071        || cur instanceof NotServingRegionException || cur instanceof RequestTooBigException);
072  }
073
074
075  /**
076   * Look for an exception we know in the remote exception:
077   * - hadoop.ipc wrapped exceptions
078   * - nested exceptions
079   *
080   * Looks for: RegionMovedException / RegionOpeningException / RegionTooBusyException /
081   *            RpcThrottlingException
082   * @return null if we didn't find the exception, the exception otherwise.
083   */
084  public static Throwable findException(Object exception) {
085    if (exception == null || !(exception instanceof Throwable)) {
086      return null;
087    }
088    Throwable cur = (Throwable) exception;
089    while (cur != null) {
090      if (isSpecialException(cur)) {
091        return cur;
092      }
093      if (cur instanceof RemoteException) {
094        RemoteException re = (RemoteException) cur;
095        cur = re.unwrapRemoteException();
096
097        // unwrapRemoteException can return the exception given as a parameter when it cannot
098        //  unwrap it. In this case, there is no need to look further
099        // noinspection ObjectEquality
100        if (cur == re) {
101          return cur;
102        }
103
104        // When we receive RemoteException which wraps IOException which has a cause as
105        // RemoteException we can get into infinite loop here; so if the cause of the exception
106        // is RemoteException, we shouldn't look further.
107      } else if (cur.getCause() != null && !(cur.getCause() instanceof RemoteException)) {
108        cur = cur.getCause();
109      } else {
110        return cur;
111      }
112    }
113
114    return null;
115  }
116
117  /**
118   * Checks if the exception is CallQueueTooBig exception (maybe wrapped
119   * into some RemoteException).
120   * @param t exception to check
121   * @return true if it's a CQTBE, false otherwise
122   */
123  public static boolean isCallQueueTooBigException(Throwable t) {
124    t = findException(t);
125    return (t instanceof CallQueueTooBigException);
126  }
127
128  /**
129   * Checks if the exception is CallDroppedException (maybe wrapped
130   * into some RemoteException).
131   * @param t exception to check
132   * @return true if it's a CQTBE, false otherwise
133   */
134  public static boolean isCallDroppedException(Throwable t) {
135    t = findException(t);
136    return (t instanceof CallDroppedException);
137  }
138
139  // This list covers most connectivity exceptions but not all.
140  // For example, in SocketOutputStream a plain IOException is thrown at times when the channel is
141  // closed.
142  private static final ImmutableSet<Class<? extends Throwable>> CONNECTION_EXCEPTION_TYPES =
143    ImmutableSet.of(SocketTimeoutException.class, ConnectException.class,
144      ClosedChannelException.class, SyncFailedException.class, EOFException.class,
145      TimeoutException.class, TimeoutIOException.class, CallTimeoutException.class,
146      ConnectionClosingException.class, FailedServerException.class,
147      ConnectionClosedException.class);
148
149  /**
150   * For test only. Usually you should use the {@link #isConnectionException(Throwable)} method
151   * below.
152   */
153  @VisibleForTesting
154  public static Set<Class<? extends Throwable>> getConnectionExceptionTypes() {
155    return CONNECTION_EXCEPTION_TYPES;
156  }
157
158  /**
159   * Check if the exception is something that indicates that we cannot contact/communicate with the
160   * server.
161   * @param e exception to check
162   * @return true when exception indicates that the client wasn't able to make contact with server
163   */
164  public static boolean isConnectionException(Throwable e) {
165    if (e == null) {
166      return false;
167    }
168    for (Class<? extends Throwable> clazz : CONNECTION_EXCEPTION_TYPES) {
169      if (clazz.isAssignableFrom(e.getClass())) {
170        return true;
171      }
172    }
173    return false;
174  }
175
176  /**
177   * Translates exception for preemptive fast fail checks.
178   * @param t exception to check
179   * @return translated exception
180   * @throws IOException
181   */
182  public static Throwable translatePFFE(Throwable t) throws IOException {
183    if (t instanceof NoSuchMethodError) {
184      // We probably can't recover from this exception by retrying.
185      throw (NoSuchMethodError) t;
186    }
187
188    if (t instanceof NullPointerException) {
189      // The same here. This is probably a bug.
190      throw (NullPointerException) t;
191    }
192
193    if (t instanceof UndeclaredThrowableException) {
194      t = t.getCause();
195    }
196    if (t instanceof RemoteException) {
197      t = ((RemoteException) t).unwrapRemoteException();
198    }
199    if (t instanceof DoNotRetryIOException) {
200      throw (DoNotRetryIOException) t;
201    }
202    if (t instanceof Error) {
203      throw (Error) t;
204    }
205    return t;
206  }
207}