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.
030 * This allows the client to call 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
049  public RegionMovedException(ServerName serverName, long locationSeqNum) {
050    this.hostname = serverName.getHostname();
051    this.port = serverName.getPort();
052    this.startCode = serverName.getStartcode();
053    this.locationSeqNum = locationSeqNum;
054  }
055
056  public String getHostname() {
057    return hostname;
058  }
059
060  public int getPort() {
061    return port;
062  }
063
064  public ServerName getServerName(){
065    return ServerName.valueOf(hostname, port, startCode);
066  }
067
068  public long getLocationSeqNum() {
069    return locationSeqNum;
070  }
071
072  /**
073   * For hadoop.ipc internal call. Do NOT use.
074   * We have to parse the hostname to recreate the exception.
075   * The input is the one generated by {@link #getMessage()}
076   */
077  public RegionMovedException(String s) {
078    int posHostname = s.indexOf(HOST_FIELD) + HOST_FIELD.length();
079    int posPort = s.indexOf(PORT_FIELD) + PORT_FIELD.length();
080    int posStartCode = s.indexOf(STARTCODE_FIELD) + STARTCODE_FIELD.length();
081    int posSeqNum = s.indexOf(LOCATIONSEQNUM_FIELD) + LOCATIONSEQNUM_FIELD.length();
082
083    String tmpHostname = null;
084    int tmpPort = -1;
085    long tmpStartCode = -1;
086    long tmpSeqNum = HConstants.NO_SEQNUM;
087    try {
088      // TODO: this whole thing is extremely brittle.
089      tmpHostname = s.substring(posHostname, s.indexOf(' ', posHostname));
090      tmpPort = Integer.parseInt(s.substring(posPort, s.indexOf(' ', posPort)));
091      tmpStartCode =  Long.parseLong(s.substring(posStartCode, s.indexOf('.', posStartCode)));
092      tmpSeqNum = Long.parseLong(s.substring(posSeqNum, s.indexOf('.', posSeqNum)));
093    } catch (Exception ignored) {
094      LOG.warn("Can't parse the hostname, port and startCode from this string: " +
095          s + ", continuing");
096    }
097
098    hostname = tmpHostname;
099    port = tmpPort;
100    startCode = tmpStartCode;
101    locationSeqNum = tmpSeqNum;
102  }
103
104  @Override
105  public String getMessage() {
106    // TODO: deserialization above depends on this. That is bad, but also means this
107    // should be modified carefully.
108    return "Region moved to: " + HOST_FIELD + hostname + " " + PORT_FIELD + port + " " +
109        STARTCODE_FIELD + startCode + ". As of "  + LOCATIONSEQNUM_FIELD + locationSeqNum + ".";
110  }
111}