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;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.List;
023import java.util.Locale;
024import java.util.regex.Pattern;
025import org.apache.hadoop.hbase.net.Address;
026import org.apache.hadoop.hbase.util.Addressing;
027import org.apache.hadoop.hbase.util.Bytes;
028import org.apache.yetus.audience.InterfaceAudience;
029
030import org.apache.hbase.thirdparty.com.google.common.collect.Interner;
031import org.apache.hbase.thirdparty.com.google.common.collect.Interners;
032import org.apache.hbase.thirdparty.com.google.common.net.InetAddresses;
033
034/**
035 * Name of a particular incarnation of an HBase Server. A {@link ServerName} is used uniquely
036 * identifying a server instance in a cluster and is made of the combination of hostname, port, and
037 * startcode. The startcode distinguishes restarted servers on same hostname and port (startcode is
038 * usually timestamp of server startup). The {@link #toString()} format of ServerName is safe to use
039 * in the filesystem and as znode name up in ZooKeeper. Its format is:
040 * <code>&lt;hostname&gt; '{@link #SERVERNAME_SEPARATOR}' &lt;port&gt;
041 * '{@link #SERVERNAME_SEPARATOR}' &lt;startcode&gt;</code>. For example, if hostname is
042 * <code>www.example.org</code>, port is <code>1234</code>, and the startcode for the regionserver
043 * is <code>1212121212</code>, then the {@link #toString()} would be
044 * <code>www.example.org,1234,1212121212</code>.
045 * <p>
046 * You can obtain a versioned serialized form of this class by calling {@link #getVersionedBytes()}.
047 * To deserialize, call {@link #parseVersionedServerName(byte[])}.
048 * <p>
049 * Use {@link #getAddress()} to obtain the Server hostname + port (Endpoint/Socket Address).
050 * <p>
051 * Immutable.
052 */
053@InterfaceAudience.Public
054public class ServerName implements Comparable<ServerName>, Serializable {
055  private static final long serialVersionUID = 1367463982557264981L;
056
057  /**
058   * Version for this class. Its a short rather than a byte so I can for sure distinguish between
059   * this version of this class and the version previous to this which did not have a version.
060   */
061  private static final short VERSION = 0;
062  static final byte[] VERSION_BYTES = Bytes.toBytes(VERSION);
063
064  /**
065   * What to use if no startcode supplied.
066   */
067  public static final int NON_STARTCODE = -1;
068
069  /**
070   * This character is used as separator between server hostname, port and startcode.
071   */
072  public static final String SERVERNAME_SEPARATOR = ",";
073
074  public static final Pattern SERVERNAME_PATTERN =
075    Pattern.compile("[^" + SERVERNAME_SEPARATOR + "]+" + SERVERNAME_SEPARATOR
076      + Addressing.VALID_PORT_REGEX + SERVERNAME_SEPARATOR + Addressing.VALID_PORT_REGEX + "$");
077
078  /**
079   * What to use if server name is unknown.
080   */
081  public static final String UNKNOWN_SERVERNAME = "#unknown#";
082
083  private final String servername;
084  private final long startcode;
085  private transient Address address;
086
087  /**
088   * Cached versioned bytes of this ServerName instance.
089   * @see #getVersionedBytes()
090   */
091  private byte[] bytes;
092  public static final List<ServerName> EMPTY_SERVER_LIST = new ArrayList<>(0);
093
094  /**
095   * Intern ServerNames. The Set of ServerNames is mostly-fixed changing slowly as Servers restart.
096   * Rather than create a new instance everytime, try and return existing instance if there is one.
097   */
098  private static final Interner<ServerName> INTERN_POOL = Interners.newWeakInterner();
099
100  protected ServerName(final String hostname, final int port, final long startcode) {
101    this(Address.fromParts(hostname, port), startcode);
102  }
103
104  private ServerName(final Address address, final long startcode) {
105    // Use HostAndPort to host port and hostname. Does validation and can do ipv6
106    this.address = address;
107    this.startcode = startcode;
108    this.servername = getServerName(this.address.getHostname(), this.address.getPort(), startcode);
109  }
110
111  private ServerName(final String hostAndPort, final long startCode) {
112    this(Address.fromString(hostAndPort), startCode);
113  }
114
115  /**
116   * @param hostname the hostname string to get the actual hostname from
117   * @return hostname minus the domain, if there is one (will do pass-through on ip addresses)
118   * @deprecated Since 2.0. This is for internal use only.
119   */
120  @Deprecated
121  // Make this private in hbase-3.0.
122  static String getHostNameMinusDomain(final String hostname) {
123    if (InetAddresses.isInetAddress(hostname)) {
124      return hostname;
125    }
126    String[] parts = hostname.split("\\.");
127    if (parts.length == 0) {
128      return hostname;
129    }
130    return parts[0];
131  }
132
133  /**
134   * @deprecated Since 2.0. Use {@link #valueOf(String)}
135   */
136  @Deprecated
137  // This is unused. Get rid of it.
138  public static String parseHostname(final String serverName) {
139    if (serverName == null || serverName.length() <= 0) {
140      throw new IllegalArgumentException("Passed hostname is null or empty");
141    }
142    if (!Character.isLetterOrDigit(serverName.charAt(0))) {
143      throw new IllegalArgumentException("Bad passed hostname, serverName=" + serverName);
144    }
145    int index = serverName.indexOf(SERVERNAME_SEPARATOR);
146    return serverName.substring(0, index);
147  }
148
149  /**
150   * @deprecated Since 2.0. Use {@link #valueOf(String)}
151   */
152  @Deprecated
153  // This is unused. Get rid of it.
154  public static int parsePort(final String serverName) {
155    String[] split = serverName.split(SERVERNAME_SEPARATOR);
156    return Integer.parseInt(split[1]);
157  }
158
159  /**
160   * @deprecated Since 2.0. Use {@link #valueOf(String)}
161   */
162  @Deprecated
163  // This is unused. Get rid of it.
164  public static long parseStartcode(final String serverName) {
165    int index = serverName.lastIndexOf(SERVERNAME_SEPARATOR);
166    return Long.parseLong(serverName.substring(index + 1));
167  }
168
169  /**
170   * Retrieve an instance of ServerName. Callers should use the equals method to compare returned
171   * instances, though we may return a shared immutable object as an internal optimization.
172   */
173  public static ServerName valueOf(final String hostname, final int port, final long startcode) {
174    return INTERN_POOL.intern(new ServerName(hostname, port, startcode));
175  }
176
177  /**
178   * Retrieve an instance of ServerName. Callers should use the equals method to compare returned
179   * instances, though we may return a shared immutable object as an internal optimization.
180   */
181  public static ServerName valueOf(final String serverName) {
182    final String hostname = serverName.substring(0, serverName.indexOf(SERVERNAME_SEPARATOR));
183    final int port = Integer.parseInt(serverName.split(SERVERNAME_SEPARATOR)[1]);
184    final long statuscode =
185      Long.parseLong(serverName.substring(serverName.lastIndexOf(SERVERNAME_SEPARATOR) + 1));
186    return INTERN_POOL.intern(new ServerName(hostname, port, statuscode));
187  }
188
189  /**
190   * Retrieve an instance of ServerName. Callers should use the equals method to compare returned
191   * instances, though we may return a shared immutable object as an internal optimization.
192   */
193  public static ServerName valueOf(final String hostAndPort, final long startCode) {
194    return INTERN_POOL.intern(new ServerName(hostAndPort, startCode));
195  }
196
197  /**
198   * Retrieve an instance of {@link ServerName}. Callers should use the {@link #equals(Object)}
199   * method to compare returned instances, though we may return a shared immutable object as an
200   * internal optimization.
201   * @param address   the {@link Address} to use for getting the {@link ServerName}
202   * @param startcode the startcode to use for getting the {@link ServerName}
203   * @return the constructed {@link ServerName}
204   * @see #valueOf(String, int, long)
205   */
206  public static ServerName valueOf(final Address address, final long startcode) {
207    return valueOf(address.getHostname(), address.getPort(), startcode);
208  }
209
210  @Override
211  public String toString() {
212    return getServerName();
213  }
214
215  /**
216   * @return Return a SHORT version of {@link #toString()}, one that has the host only, minus the
217   *         domain, and the port only -- no start code; the String is for us internally mostly
218   *         tying threads to their server. Not for external use. It is lossy and will not work in
219   *         in compares, etc.
220   */
221  public String toShortString() {
222    return Addressing.createHostAndPortStr(getHostNameMinusDomain(this.address.getHostname()),
223      this.address.getPort());
224  }
225
226  /**
227   * @return {@link #getServerName()} as bytes with a short-sized prefix with the {@link #VERSION}
228   *         of this class.
229   */
230  public synchronized byte[] getVersionedBytes() {
231    if (this.bytes == null) {
232      this.bytes = Bytes.add(VERSION_BYTES, Bytes.toBytes(getServerName()));
233    }
234    return this.bytes;
235  }
236
237  public String getServerName() {
238    return servername;
239  }
240
241  public String getHostname() {
242    return this.address.getHostname();
243  }
244
245  public String getHostnameLowerCase() {
246    return this.address.getHostname().toLowerCase(Locale.ROOT);
247  }
248
249  public int getPort() {
250    return this.address.getPort();
251  }
252
253  public long getStartcode() {
254    return startcode;
255  }
256
257  /**
258   * For internal use only.
259   * @param hostName  the name of the host to use
260   * @param port      the port on the host to use
261   * @param startcode the startcode to use for formatting
262   * @return Server name made of the concatenation of hostname, port and startcode formatted as
263   *         <code>&lt;hostname&gt; ',' &lt;port&gt; ',' &lt;startcode&gt;</code>
264   * @deprecated Since 2.0. Use {@link ServerName#valueOf(String, int, long)} instead.
265   */
266  @Deprecated
267  // TODO: Make this private in hbase-3.0.
268  static String getServerName(String hostName, int port, long startcode) {
269    return hostName.toLowerCase(Locale.ROOT) + SERVERNAME_SEPARATOR + port + SERVERNAME_SEPARATOR
270      + startcode;
271  }
272
273  /**
274   * @param hostAndPort String in form of &lt;hostname&gt; ':' &lt;port&gt;
275   * @param startcode   the startcode to use
276   * @return Server name made of the concatenation of hostname, port and startcode formatted as
277   *         <code>&lt;hostname&gt; ',' &lt;port&gt; ',' &lt;startcode&gt;</code>
278   * @deprecated Since 2.0. Use {@link ServerName#valueOf(String, long)} instead.
279   */
280  @Deprecated
281  public static String getServerName(final String hostAndPort, final long startcode) {
282    int index = hostAndPort.indexOf(':');
283    if (index <= 0) {
284      throw new IllegalArgumentException("Expected <hostname> ':' <port>");
285    }
286    return getServerName(hostAndPort.substring(0, index),
287      Integer.parseInt(hostAndPort.substring(index + 1)), startcode);
288  }
289
290  /**
291   * @return Hostname and port formatted as described at
292   *         {@link Addressing#createHostAndPortStr(String, int)}
293   * @deprecated Since 2.0. Use {@link #getAddress()} instead.
294   */
295  @Deprecated
296  public String getHostAndPort() {
297    return this.address.toString();
298  }
299
300  public Address getAddress() {
301    return this.address;
302  }
303
304  /**
305   * @param serverName ServerName in form specified by {@link #getServerName()}
306   * @return The server start code parsed from <code>servername</code>
307   * @deprecated Since 2.0. Use instance of ServerName to pull out start code.
308   */
309  @Deprecated
310  public static long getServerStartcodeFromServerName(final String serverName) {
311    int index = serverName.lastIndexOf(SERVERNAME_SEPARATOR);
312    return Long.parseLong(serverName.substring(index + 1));
313  }
314
315  /**
316   * Utility method to excise the start code from a server name
317   * @param inServerName full server name
318   * @return server name less its start code
319   * @deprecated Since 2.0. Use {@link #getAddress()}
320   */
321  @Deprecated
322  public static String getServerNameLessStartCode(String inServerName) {
323    if (inServerName != null && inServerName.length() > 0) {
324      int index = inServerName.lastIndexOf(SERVERNAME_SEPARATOR);
325      if (index > 0) {
326        return inServerName.substring(0, index);
327      }
328    }
329    return inServerName;
330  }
331
332  @Override
333  public int compareTo(ServerName other) {
334    int compare;
335    if (other == null) {
336      return -1;
337    }
338    if (this.getHostname() == null) {
339      if (other.getHostname() != null) {
340        return 1;
341      }
342    } else {
343      if (other.getHostname() == null) {
344        return -1;
345      }
346      compare = this.getHostname().compareToIgnoreCase(other.getHostname());
347      if (compare != 0) {
348        return compare;
349      }
350    }
351    compare = this.getPort() - other.getPort();
352    if (compare != 0) {
353      return compare;
354    }
355    return Long.compare(this.getStartcode(), other.getStartcode());
356  }
357
358  @Override
359  public int hashCode() {
360    return getServerName().hashCode();
361  }
362
363  @Override
364  public boolean equals(Object o) {
365    if (this == o) {
366      return true;
367    }
368    if (o == null) {
369      return false;
370    }
371    if (!(o instanceof ServerName)) {
372      return false;
373    }
374    return this.compareTo((ServerName) o) == 0;
375  }
376
377  /**
378   * @param left  the first server address to compare
379   * @param right the second server address to compare
380   * @return {@code true} if {@code left} and {@code right} have the same hostname and port.
381   */
382  public static boolean isSameAddress(final ServerName left, final ServerName right) {
383    return left.getAddress().equals(right.getAddress());
384  }
385
386  /**
387   * Use this method instantiating a {@link ServerName} from bytes gotten from a call to
388   * {@link #getVersionedBytes()}. Will take care of the case where bytes were written by an earlier
389   * version of hbase.
390   * @param versionedBytes Pass bytes gotten from a call to {@link #getVersionedBytes()}
391   * @return A ServerName instance.
392   * @see #getVersionedBytes()
393   */
394  public static ServerName parseVersionedServerName(final byte[] versionedBytes) {
395    // Version is a short.
396    short version = Bytes.toShort(versionedBytes);
397    if (version == VERSION) {
398      int length = versionedBytes.length - Bytes.SIZEOF_SHORT;
399      return valueOf(Bytes.toString(versionedBytes, Bytes.SIZEOF_SHORT, length));
400    }
401    // Presume the bytes were written with an old version of hbase and that the
402    // bytes are actually a String of the form "'<hostname>' ':' '<port>'".
403    return valueOf(Bytes.toString(versionedBytes), NON_STARTCODE);
404  }
405
406  /**
407   * @param str Either an instance of {@link #toString()} or a "'&lt;hostname&gt;' ':'
408   *            '&lt;port&gt;'".
409   * @return A ServerName instance.
410   */
411  public static ServerName parseServerName(final String str) {
412    return SERVERNAME_PATTERN.matcher(str).matches() ? valueOf(str) : valueOf(str, NON_STARTCODE);
413  }
414
415  /** Returns true if the String follows the pattern of {@link #toString()}, false otherwise. */
416  public static boolean isFullServerName(final String str) {
417    if (str == null || str.isEmpty()) {
418      return false;
419    }
420    return SERVERNAME_PATTERN.matcher(str).matches();
421  }
422}