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;
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.ipc.RemoteException;
25  
26  /**
27   * Subclass if the server knows the region is now on another server.
28   * This allows the client to call the new region server without calling the master.
29   */
30  @InterfaceAudience.Private
31  @InterfaceStability.Evolving
32  public class RegionMovedException extends NotServingRegionException {
33    private static final Log LOG = LogFactory.getLog(RegionMovedException.class);
34    private static final long serialVersionUID = -7232903522310558396L;
35  
36    private final String hostname;
37    private final int port;
38    private final long startCode;
39    private final long locationSeqNum;
40  
41    private static final String HOST_FIELD = "hostname=";
42    private static final String PORT_FIELD = "port=";
43    private static final String STARTCODE_FIELD = "startCode=";
44    private static final String LOCATIONSEQNUM_FIELD = "locationSeqNum=";
45  
46  
47    public RegionMovedException(ServerName serverName, long locationSeqNum) {
48      this.hostname = serverName.getHostname();
49      this.port = serverName.getPort();
50      this.startCode = serverName.getStartcode();
51      this.locationSeqNum = locationSeqNum;
52    }
53  
54    public String getHostname() {
55      return hostname;
56    }
57  
58    public int getPort() {
59      return port;
60    }
61  
62    public ServerName getServerName(){
63      return new ServerName(hostname, port, startCode);
64    }
65  
66    public long getLocationSeqNum() {
67      return locationSeqNum;
68    }
69  
70    /**
71     * For hadoop.ipc internal call. Do NOT use.
72     * We have to parse the hostname to recreate the exception.
73     * The input is the one generated by {@link #getMessage()}
74     */
75    public RegionMovedException(String s) {
76      int posHostname = s.indexOf(HOST_FIELD) + HOST_FIELD.length();
77      int posPort = s.indexOf(PORT_FIELD) + PORT_FIELD.length();
78      int posStartCode = s.indexOf(STARTCODE_FIELD) + STARTCODE_FIELD.length();
79      int posSeqNum = s.indexOf(LOCATIONSEQNUM_FIELD) + LOCATIONSEQNUM_FIELD.length();
80  
81      String tmpHostname = null;
82      int tmpPort = -1;
83      long tmpStartCode = -1;
84      long tmpSeqNum = HConstants.NO_SEQNUM;
85      try {
86        // TODO: this whole thing is extremely brittle.
87        tmpHostname = s.substring(posHostname, s.indexOf(' ', posHostname));
88        tmpPort = Integer.parseInt(s.substring(posPort, s.indexOf(' ', posPort)));
89        tmpStartCode =  Long.parseLong(s.substring(posStartCode, s.indexOf('.', posStartCode)));
90        tmpSeqNum = Long.parseLong(s.substring(posSeqNum, s.indexOf('.', posSeqNum)));
91      } catch (Exception ignored) {
92        LOG.warn("Can't parse the hostname, port and startCode from this string: " +
93            s + ", continuing");
94      }
95  
96      hostname = tmpHostname;
97      port = tmpPort;
98      startCode = tmpStartCode;
99      locationSeqNum = tmpSeqNum;
100   }
101 
102   @Override
103   public String getMessage() {
104     // TODO: deserialization above depends on this. That is bad, but also means this
105     // should be modified carefully.
106     return "Region moved to: " + HOST_FIELD + hostname + " " + PORT_FIELD + port + " " +
107         STARTCODE_FIELD + startCode + ". As of "  + LOCATIONSEQNUM_FIELD + locationSeqNum + ".";
108   }
109 
110   /**
111    * Look for a RegionMovedException in the exception:
112    *  - hadoop.ipc wrapped exceptions
113    *  - nested exceptions
114    * Returns null if we didn't find the exception or if it was not readable.
115    */
116   public static RegionMovedException find(Object exception) {
117     if (exception == null || !(exception instanceof Throwable)){
118       return null;
119     }
120 
121     Throwable cur = (Throwable)exception;
122     RegionMovedException res = null;
123 
124     while (res == null && cur != null) {
125       if (cur instanceof RegionMovedException) {
126         res = (RegionMovedException) cur;
127       } else {
128         if (cur instanceof RemoteException) {
129           RemoteException re = (RemoteException) cur;
130           Exception e = re.unwrapRemoteException(RegionMovedException.class);
131           if (e == null){
132             e = re.unwrapRemoteException();
133           }
134           // unwrapRemoteException can return the exception given as a parameter when it cannot
135           //  unwrap it. In this case, there is no need to look further
136           // noinspection ObjectEquality
137           if (e != re){
138             res =  find(e);
139           }
140         }
141         cur = cur.getCause();
142       }
143     }
144 
145     if (res != null && (res.getPort() < 0 || res.getHostname() == null)){
146       // We failed to parse the exception. Let's act as we don't find the exception.
147       return null;
148     } else {
149       return res;
150     }
151   }
152 }