/**
* Copyright (C) 2014-2015 LinkedIn Corp. (pinot-core@linkedin.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.linkedin.pinot.common.response;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.net.InternetDomainName;
/**
* Service abstraction.
* A service is identified by its hostname and port.
* Internally, an ip address is also resolved.
*
* Nuances:
* -------
* A hostname "localhost" will not be resolved to the
* local hostname and will retain the hostname as "localhost".
* If the name passed is "127.0.0.1", then it is resolved to the
* local hostname.
*/
public class ServerInstance implements Comparable<ServerInstance> {
protected static final Logger LOGGER = LoggerFactory.getLogger(ServerInstance.class);
public static final String NAME_PORT_DELIMITER = ":";
/** Host-name where the service is running **/
private final String _hostname;
/** Service Port **/
private final int _port;
/** IP Address. Not used in equals/hash-code generation **/
private final InetAddress _ipAddress;
private final int _seq;
private final String _shortHostName;
/**
* Use this constructor if the name and port are embedded as string with ":" as delimiter
*
* @param namePortStr Name and Port settings
*/
public ServerInstance(String namePortStr) {
this(namePortStr.split(NAME_PORT_DELIMITER)[0], Integer.parseInt(namePortStr.split(NAME_PORT_DELIMITER)[1]));
}
public ServerInstance(String name, int port) {
this(name, port, 0);
}
public ServerInstance(String name, int port, int seq) {
InetAddress ipAddr = null;
try {
ipAddr = InetAddress.getByName(name);
} catch (UnknownHostException e) {
LOGGER.error("Unable to fetch IpAddresses for host:" + name, e);
ipAddr = null;
}
_ipAddress = ipAddr;
_hostname = _ipAddress != null ? _ipAddress.getHostName() : name;
_port = port;
_seq = seq;
_shortHostName = makeShortHostName(_hostname);
}
/**
* As per <a href="https://tools.ietf.org/html/rfc952">RFC-952</a> domain names should begin with a letter.
* That said, <a href="https://tools.ietf.org/html/rfc1123#page-13">RFC-1123</a> updated it say that it may also begin
* with a digit. Indeed, <a href="http://9292.nl/">this</a> is a valid domain name. Only the top-level domain (i.e. the
* last portion) has to be non-numeric. More clarification on this matter is in
* <a href="https://tools.ietf.org/html/rfc3696#section-2">RFC-3696</a>
*
* A potentially faster solution is
*
* if (first char is a digit) {
* it is probably ipv4;
* return name;
* } else {
* it could be ipv6 (in which case no dots), or a hostname
* return substring before the first dot.
* }
*
* It will fail if there are host names starting with a digit, but will work right otherwise.
*/
private String makeShortHostName(final String name) {
try {
InternetDomainName domainName = InternetDomainName.from(name);
return domainName.parts().get(0);
} catch (Exception e) {
return name;
}
}
public String getShortHostName() {
return _shortHostName;
}
public String getHostname() {
return _hostname;
}
public int getPort() {
return _port;
}
public InetAddress getIpAddress() {
return _ipAddress;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + (_hostname == null ? 0 : _hostname.hashCode());
result = (prime * result) + _port;
result = (prime * result) + _seq;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ServerInstance other = (ServerInstance) obj;
if (_hostname == null) {
if (other._hostname != null) {
return false;
}
} else if (!_hostname.equals(other._hostname)) {
return false;
}
if (_port != other._port) {
return false;
}
if (_seq != other._seq) {
return false;
}
return true;
}
@Override
public String toString() {
return _hostname + "_" + _port;
}
@Override
public int compareTo(ServerInstance o) {
return this.toString().compareTo(o.toString());
}
}