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;
031import org.apache.hadoop.hbase.CallDroppedException;
032import org.apache.hadoop.hbase.CallQueueTooBigException;
033import org.apache.hadoop.hbase.DoNotRetryIOException;
034import org.apache.hadoop.hbase.MultiActionResultTooLarge;
035import org.apache.hadoop.hbase.NotServingRegionException;
036import org.apache.hadoop.hbase.RegionTooBusyException;
037import org.apache.hadoop.hbase.RetryImmediatelyException;
038import org.apache.hadoop.hbase.ipc.CallTimeoutException;
039import org.apache.hadoop.hbase.ipc.FailedServerException;
040import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
041import org.apache.hadoop.ipc.RemoteException;
042import org.apache.yetus.audience.InterfaceAudience;
043import org.apache.yetus.audience.InterfaceStability;
044
045import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet;
046
047@InterfaceAudience.Private
048@InterfaceStability.Evolving
049public final class ClientExceptionsUtil {
050
051  private ClientExceptionsUtil() {}
052
053  public static boolean isMetaClearingException(Throwable cur) {
054    cur = findException(cur);
055
056    if (cur == null) {
057      return true;
058    }
059    return !isSpecialException(cur) || (cur instanceof RegionMovedException)
060        || cur instanceof NotServingRegionException;
061  }
062
063  public static boolean isSpecialException(Throwable cur) {
064    return (cur instanceof RegionMovedException || cur instanceof RegionOpeningException
065        || cur instanceof RegionTooBusyException || cur instanceof RpcThrottlingException
066        || cur instanceof MultiActionResultTooLarge || cur instanceof RetryImmediatelyException
067        || cur instanceof CallQueueTooBigException || cur instanceof CallDroppedException
068        || cur instanceof NotServingRegionException || cur instanceof RequestTooBigException);
069  }
070
071
072  /**
073   * Look for an exception we know in the remote exception:
074   * - hadoop.ipc wrapped exceptions
075   * - nested exceptions
076   *
077   * Looks for: RegionMovedException / RegionOpeningException / RegionTooBusyException /
078   *            RpcThrottlingException
079   * @return null if we didn't find the exception, the exception otherwise.
080   */
081  public static Throwable findException(Object exception) {
082    if (exception == null || !(exception instanceof Throwable)) {
083      return null;
084    }
085    Throwable cur = (Throwable) exception;
086    while (cur != null) {
087      if (isSpecialException(cur)) {
088        return cur;
089      }
090      if (cur instanceof RemoteException) {
091        RemoteException re = (RemoteException) cur;
092        cur = re.unwrapRemoteException();
093
094        // unwrapRemoteException can return the exception given as a parameter when it cannot
095        //  unwrap it. In this case, there is no need to look further
096        // noinspection ObjectEquality
097        if (cur == re) {
098          return cur;
099        }
100
101        // When we receive RemoteException which wraps IOException which has a cause as
102        // RemoteException we can get into infinite loop here; so if the cause of the exception
103        // is RemoteException, we shouldn't look further.
104      } else if (cur.getCause() != null && !(cur.getCause() instanceof RemoteException)) {
105        cur = cur.getCause();
106      } else {
107        return cur;
108      }
109    }
110
111    return null;
112  }
113
114  /**
115   * Checks if the exception is CallQueueTooBig exception (maybe wrapped
116   * into some RemoteException).
117   * @param t exception to check
118   * @return true if it's a CQTBE, false otherwise
119   */
120  public static boolean isCallQueueTooBigException(Throwable t) {
121    t = findException(t);
122    return (t instanceof CallQueueTooBigException);
123  }
124
125  /**
126   * Checks if the exception is CallDroppedException (maybe wrapped
127   * into some RemoteException).
128   * @param t exception to check
129   * @return true if it's a CQTBE, false otherwise
130   */
131  public static boolean isCallDroppedException(Throwable t) {
132    t = findException(t);
133    return (t instanceof CallDroppedException);
134  }
135
136  // This list covers most connectivity exceptions but not all.
137  // For example, in SocketOutputStream a plain IOException is thrown at times when the channel is
138  // closed.
139  private static final ImmutableSet<Class<? extends Throwable>> CONNECTION_EXCEPTION_TYPES =
140    ImmutableSet.of(SocketTimeoutException.class, ConnectException.class,
141      ClosedChannelException.class, SyncFailedException.class, EOFException.class,
142      TimeoutException.class, TimeoutIOException.class, CallTimeoutException.class,
143      ConnectionClosingException.class, FailedServerException.class,
144      ConnectionClosedException.class);
145
146  /**
147   * For test only. Usually you should use the {@link #isConnectionException(Throwable)} method
148   * below.
149   */
150  public static Set<Class<? extends Throwable>> getConnectionExceptionTypes() {
151    return CONNECTION_EXCEPTION_TYPES;
152  }
153
154  /**
155   * Check if the exception is something that indicates that we cannot contact/communicate with the
156   * server.
157   * @param e exception to check
158   * @return true when exception indicates that the client wasn't able to make contact with server
159   */
160  public static boolean isConnectionException(Throwable e) {
161    if (e == null) {
162      return false;
163    }
164    for (Class<? extends Throwable> clazz : CONNECTION_EXCEPTION_TYPES) {
165      if (clazz.isAssignableFrom(e.getClass())) {
166        return true;
167      }
168    }
169    return false;
170  }
171
172  /**
173   * Translates exception for preemptive fast fail checks.
174   * @param t exception to check
175   * @return translated exception
176   * @throws IOException
177   */
178  public static Throwable translatePFFE(Throwable t) throws IOException {
179    if (t instanceof NoSuchMethodError) {
180      // We probably can't recover from this exception by retrying.
181      throw (NoSuchMethodError) t;
182    }
183
184    if (t instanceof NullPointerException) {
185      // The same here. This is probably a bug.
186      throw (NullPointerException) t;
187    }
188
189    if (t instanceof UndeclaredThrowableException) {
190      t = t.getCause();
191    }
192    if (t instanceof RemoteException) {
193      t = ((RemoteException) t).unwrapRemoteException();
194    }
195    if (t instanceof DoNotRetryIOException) {
196      throw (DoNotRetryIOException) t;
197    }
198    if (t instanceof Error) {
199      throw (Error) t;
200    }
201    return t;
202  }
203}