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 org.apache.hadoop.hbase.HConstants;
021import org.apache.hadoop.hbase.NotServingRegionException;
022import org.apache.hadoop.hbase.ServerName;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.apache.yetus.audience.InterfaceStability;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028/**
029 * Subclass if the server knows the region is now on another server. This allows the client to call
030 * the new region server without calling the master.
031 */
032@InterfaceAudience.Private
033@InterfaceStability.Evolving
034public class RegionMovedException extends NotServingRegionException {
035  private static final Logger LOG = LoggerFactory.getLogger(RegionMovedException.class);
036  private static final long serialVersionUID = -7232903522310558396L;
037
038  private final String hostname;
039  private final int port;
040  private final long startCode;
041  private final long locationSeqNum;
042
043  private static final String HOST_FIELD = "hostname=";
044  private static final String PORT_FIELD = "port=";
045  private static final String STARTCODE_FIELD = "startCode=";
046  private static final String LOCATIONSEQNUM_FIELD = "locationSeqNum=";
047
048  public RegionMovedException(ServerName serverName, long locationSeqNum) {
049    this.hostname = serverName.getHostname();
050    this.port = serverName.getPort();
051    this.startCode = serverName.getStartcode();
052    this.locationSeqNum = locationSeqNum;
053  }
054
055  public String getHostname() {
056    return hostname;
057  }
058
059  public int getPort() {
060    return port;
061  }
062
063  public ServerName getServerName() {
064    return ServerName.valueOf(hostname, port, startCode);
065  }
066
067  public long getLocationSeqNum() {
068    return locationSeqNum;
069  }
070
071  /**
072   * For hadoop.ipc internal call. Do NOT use. We have to parse the hostname to recreate the
073   * exception. The input is the one generated by {@link #getMessage()}
074   */
075  public RegionMovedException(String s) {
076    int posHostname = s.indexOf(HOST_FIELD) + HOST_FIELD.length();
077    int posPort = s.indexOf(PORT_FIELD) + PORT_FIELD.length();
078    int posStartCode = s.indexOf(STARTCODE_FIELD) + STARTCODE_FIELD.length();
079    int posSeqNum = s.indexOf(LOCATIONSEQNUM_FIELD) + LOCATIONSEQNUM_FIELD.length();
080
081    String tmpHostname = null;
082    int tmpPort = -1;
083    long tmpStartCode = -1;
084    long tmpSeqNum = HConstants.NO_SEQNUM;
085    try {
086      // TODO: this whole thing is extremely brittle.
087      tmpHostname = s.substring(posHostname, s.indexOf(' ', posHostname));
088      tmpPort = Integer.parseInt(s.substring(posPort, s.indexOf(' ', posPort)));
089      tmpStartCode = Long.parseLong(s.substring(posStartCode, s.indexOf('.', posStartCode)));
090      tmpSeqNum = Long.parseLong(s.substring(posSeqNum, s.indexOf('.', posSeqNum)));
091    } catch (Exception ignored) {
092      LOG.warn(
093        "Can't parse the hostname, port and startCode from this string: " + s + ", continuing");
094    }
095
096    hostname = tmpHostname;
097    port = tmpPort;
098    startCode = tmpStartCode;
099    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}