View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.exceptions;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.apache.hadoop.classification.InterfaceAudience;
23  import org.apache.hadoop.classification.InterfaceStability;
24  import org.apache.hadoop.hbase.HConstants;
25  import org.apache.hadoop.hbase.NotServingRegionException;
26  import org.apache.hadoop.hbase.ServerName;
27  import org.apache.hadoop.ipc.RemoteException;
28  
29  /**
30   * Subclass if the server knows the region is now on another server.
31   * This allows the client to call the new region server without calling the master.
32   */
33  @InterfaceAudience.Private
34  @InterfaceStability.Evolving
35  public class RegionMovedException extends NotServingRegionException {
36    private static final Log LOG = LogFactory.getLog(RegionMovedException.class);
37    private static final long serialVersionUID = -7232903522310558396L;
38  
39    private final String hostname;
40    private final int port;
41    private final long startCode;
42    private final long locationSeqNum;
43  
44    private static final String HOST_FIELD = "hostname=";
45    private static final String PORT_FIELD = "port=";
46    private static final String STARTCODE_FIELD = "startCode=";
47    private static final String LOCATIONSEQNUM_FIELD = "locationSeqNum=";
48  
49  
50    public RegionMovedException(ServerName serverName, long locationSeqNum) {
51      this.hostname = serverName.getHostname();
52      this.port = serverName.getPort();
53      this.startCode = serverName.getStartcode();
54      this.locationSeqNum = locationSeqNum;
55    }
56  
57    public String getHostname() {
58      return hostname;
59    }
60  
61    public int getPort() {
62      return port;
63    }
64  
65    public ServerName getServerName(){
66      return new ServerName(hostname, port, startCode);
67    }
68  
69    public long getLocationSeqNum() {
70      return locationSeqNum;
71    }
72  
73    /**
74     * For hadoop.ipc internal call. Do NOT use.
75     * We have to parse the hostname to recreate the exception.
76     * The input is the one generated by {@link #getMessage()}
77     */
78    public RegionMovedException(String s) {
79      int posHostname = s.indexOf(HOST_FIELD) + HOST_FIELD.length();
80      int posPort = s.indexOf(PORT_FIELD) + PORT_FIELD.length();
81      int posStartCode = s.indexOf(STARTCODE_FIELD) + STARTCODE_FIELD.length();
82      int posSeqNum = s.indexOf(LOCATIONSEQNUM_FIELD) + LOCATIONSEQNUM_FIELD.length();
83  
84      String tmpHostname = null;
85      int tmpPort = -1;
86      long tmpStartCode = -1;
87      long tmpSeqNum = HConstants.NO_SEQNUM;
88      try {
89        // TODO: this whole thing is extremely brittle.
90        tmpHostname = s.substring(posHostname, s.indexOf(' ', posHostname));
91        tmpPort = Integer.parseInt(s.substring(posPort, s.indexOf(' ', posPort)));
92        tmpStartCode =  Long.parseLong(s.substring(posStartCode, s.indexOf('.', posStartCode)));
93        tmpSeqNum = Long.parseLong(s.substring(posSeqNum, s.indexOf('.', posSeqNum)));
94      } catch (Exception ignored) {
95        LOG.warn("Can't parse the hostname, port and startCode from this string: " +
96            s + ", continuing");
97      }
98  
99      hostname = tmpHostname;
100     port = tmpPort;
101     startCode = tmpStartCode;
102     locationSeqNum = tmpSeqNum;
103   }
104 
105   @Override
106   public String getMessage() {
107     // TODO: deserialization above depends on this. That is bad, but also means this
108     // should be modified carefully.
109     return "Region moved to: " + HOST_FIELD + hostname + " " + PORT_FIELD + port + " " +
110         STARTCODE_FIELD + startCode + ". As of "  + LOCATIONSEQNUM_FIELD + locationSeqNum + ".";
111   }
112 
113   /**
114    * Look for a RegionMovedException in the exception:
115    *  - hadoop.ipc wrapped exceptions
116    *  - nested exceptions
117    * Returns null if we didn't find the exception or if it was not readable.
118    */
119   public static RegionMovedException find(Object exception) {
120     if (exception == null || !(exception instanceof Throwable)){
121       return null;
122     }
123 
124     Throwable cur = (Throwable)exception;
125     RegionMovedException res = null;
126 
127     while (res == null && cur != null) {
128       if (cur instanceof RegionMovedException) {
129         res = (RegionMovedException) cur;
130       } else {
131         if (cur instanceof RemoteException) {
132           RemoteException re = (RemoteException) cur;
133           Exception e = re.unwrapRemoteException(RegionMovedException.class);
134           if (e == null){
135             e = re.unwrapRemoteException();
136           }
137           // unwrapRemoteException can return the exception given as a parameter when it cannot
138           //  unwrap it. In this case, there is no need to look further
139           // noinspection ObjectEquality
140           if (e != re){
141             res =  find(e);
142           }
143         }
144         cur = cur.getCause();
145       }
146     }
147 
148     if (res != null && (res.getPort() < 0 || res.getHostname() == null)){
149       // We failed to parse the exception. Let's act as we don't find the exception.
150       return null;
151     } else {
152       return res;
153     }
154   }
155 }