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.exceptions;
019
020import com.google.errorprone.annotations.RestrictedApi;
021import java.io.EOFException;
022import java.io.IOException;
023import java.io.SyncFailedException;
024import java.lang.reflect.UndeclaredThrowableException;
025import java.net.ConnectException;
026import java.net.SocketTimeoutException;
027import java.nio.channels.ClosedChannelException;
028import java.util.Set;
029import java.util.concurrent.TimeoutException;
030import org.apache.hadoop.hbase.CallDroppedException;
031import org.apache.hadoop.hbase.CallQueueTooBigException;
032import org.apache.hadoop.hbase.DoNotRetryIOException;
033import org.apache.hadoop.hbase.MultiActionResultTooLarge;
034import org.apache.hadoop.hbase.NotServingRegionException;
035import org.apache.hadoop.hbase.RegionTooBusyException;
036import org.apache.hadoop.hbase.RetryImmediatelyException;
037import org.apache.hadoop.hbase.ipc.CallTimeoutException;
038import org.apache.hadoop.hbase.ipc.FailedServerException;
039import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
040import org.apache.hadoop.ipc.RemoteException;
041import org.apache.yetus.audience.InterfaceAudience;
042import org.apache.yetus.audience.InterfaceStability;
043
044import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet;
045
046@InterfaceAudience.Private
047@InterfaceStability.Evolving
048public final class ClientExceptionsUtil {
049
050  private ClientExceptionsUtil() {
051  }
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   * Look for an exception we know in the remote exception: - hadoop.ipc wrapped exceptions - nested
073   * exceptions Looks for: RegionMovedException / RegionOpeningException / RegionTooBusyException /
074   * RpcThrottlingException
075   * @return null if we didn't find the exception, the exception otherwise.
076   */
077  public static Throwable findException(Object exception) {
078    if (exception == null || !(exception instanceof Throwable)) {
079      return null;
080    }
081    Throwable cur = (Throwable) exception;
082    while (cur != null) {
083      if (isSpecialException(cur)) {
084        return cur;
085      }
086      if (cur instanceof RemoteException) {
087        RemoteException re = (RemoteException) cur;
088        cur = re.unwrapRemoteException();
089
090        // unwrapRemoteException can return the exception given as a parameter when it cannot
091        // unwrap it. In this case, there is no need to look further
092        // noinspection ObjectEquality
093        if (cur == re) {
094          return cur;
095        }
096
097        // When we receive RemoteException which wraps IOException which has a cause as
098        // RemoteException we can get into infinite loop here; so if the cause of the exception
099        // is RemoteException, we shouldn't look further.
100      } else if (cur.getCause() != null && !(cur.getCause() instanceof RemoteException)) {
101        cur = cur.getCause();
102      } else {
103        return cur;
104      }
105    }
106
107    return null;
108  }
109
110  // This list covers most connectivity exceptions but not all.
111  // For example, in SocketOutputStream a plain IOException is thrown at times when the channel is
112  // closed.
113  private static final ImmutableSet<Class<? extends Throwable>> CONNECTION_EXCEPTION_TYPES =
114    ImmutableSet.of(SocketTimeoutException.class, ConnectException.class,
115      ClosedChannelException.class, SyncFailedException.class, EOFException.class,
116      TimeoutException.class, TimeoutIOException.class, CallTimeoutException.class,
117      ConnectionClosingException.class, FailedServerException.class,
118      ConnectionClosedException.class);
119
120  /**
121   * For test only. Usually you should use the {@link #isConnectionException(Throwable)} method
122   * below.
123   */
124  @RestrictedApi(explanation = "Should only be called in tests", link = "",
125      allowedOnPath = ".*/src/test/.*")
126  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "MS_EXPOSE_REP",
127      justification = "test only")
128  public static Set<Class<? extends Throwable>> getConnectionExceptionTypes() {
129    return CONNECTION_EXCEPTION_TYPES;
130  }
131
132  /**
133   * Check if the exception is something that indicates that we cannot contact/communicate with the
134   * server.
135   * @param e exception to check
136   * @return true when exception indicates that the client wasn't able to make contact with server
137   */
138  public static boolean isConnectionException(Throwable e) {
139    if (e == null) {
140      return false;
141    }
142    for (Class<? extends Throwable> clazz : CONNECTION_EXCEPTION_TYPES) {
143      if (clazz.isAssignableFrom(e.getClass())) {
144        return true;
145      }
146    }
147    return false;
148  }
149
150  /**
151   * Translates exception for preemptive fast fail checks.
152   * @param t exception to check
153   * @return translated exception
154   */
155  public static Throwable translatePFFE(Throwable t) throws IOException {
156    if (t instanceof NoSuchMethodError) {
157      // We probably can't recover from this exception by retrying.
158      throw (NoSuchMethodError) t;
159    }
160
161    if (t instanceof NullPointerException) {
162      // The same here. This is probably a bug.
163      throw (NullPointerException) t;
164    }
165
166    if (t instanceof UndeclaredThrowableException) {
167      t = t.getCause();
168    }
169    if (t instanceof RemoteException) {
170      t = ((RemoteException) t).unwrapRemoteException();
171    }
172    if (t instanceof DoNotRetryIOException) {
173      throw (DoNotRetryIOException) t;
174    }
175    if (t instanceof Error) {
176      throw (Error) t;
177    }
178    return t;
179  }
180}