001/** 002 * 003 * Copyright © 2013-2017 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.util.dns; 018 019import java.net.InetAddress; 020import java.util.Collections; 021import java.util.Iterator; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Map.Entry; 026 027import org.jivesoftware.smack.SmackException.ConnectionException; 028import org.jivesoftware.smack.util.StringUtils; 029 030public class HostAddress { 031 private final String fqdn; 032 private final int port; 033 private final Map<InetAddress, Exception> exceptions = new LinkedHashMap<>(); 034 private final List<InetAddress> inetAddresses; 035 036 /** 037 * Creates a new HostAddress with the given FQDN. 038 * 039 * @param fqdn the optional fully qualified domain name (FQDN). 040 * @param port The port to connect on. 041 * @throws IllegalArgumentException If the port is out of valid range (0 - 65535). 042 */ 043 public HostAddress(String fqdn, int port, List<InetAddress> inetAddresses) { 044 if (port < 0 || port > 65535) 045 throw new IllegalArgumentException( 046 "Port must be a 16-bit unsiged integer (i.e. between 0-65535. Port was: " + port); 047 if (StringUtils.isNotEmpty(fqdn) && fqdn.charAt(fqdn.length() - 1) == '.') { 048 this.fqdn = fqdn.substring(0, fqdn.length() - 1); 049 } 050 else { 051 this.fqdn = fqdn; 052 } 053 this.port = port; 054 if (inetAddresses.isEmpty()) { 055 throw new IllegalArgumentException("Must provide at least one InetAddress"); 056 } 057 this.inetAddresses = inetAddresses; 058 } 059 060 public HostAddress(int port, InetAddress hostAddress) { 061 this(null, port, Collections.singletonList(hostAddress)); 062 } 063 064 /** 065 * Constructs a new failed HostAddress. This constructor is usually used when the DNS resolution of the domain name 066 * failed for some reason. 067 * 068 * @param fqdn the domain name of the host. 069 * @param e the exception causing the failure. 070 */ 071 public HostAddress(String fqdn, Exception e) { 072 this.fqdn = fqdn; 073 this.port = 5222; 074 inetAddresses = Collections.emptyList(); 075 setException(e); 076 } 077 078 public String getHost() { 079 if (fqdn != null) { 080 return fqdn; 081 } 082 083 // In this case, the HostAddress(int, InetAddress) constructor must been used. We have no FQDN. And 084 // inetAddresses.size() must be exactly one. 085 assert inetAddresses.size() == 1; 086 return inetAddresses.get(0).getHostAddress(); 087 } 088 089 /** 090 * Return the fully qualified domain name. This may return <code>null</code> in case there host address is only numeric, i.e. an IP address. 091 * 092 * @return the fully qualified domain name or <code>null</code> 093 */ 094 public String getFQDN() { 095 return fqdn; 096 } 097 098 public int getPort() { 099 return port; 100 } 101 102 public void setException(Exception exception) { 103 setException(null, exception); 104 } 105 106 public void setException(InetAddress inetAddress, Exception exception) { 107 Exception old = exceptions.put(inetAddress, exception); 108 assert (old == null); 109 } 110 111 /** 112 * Retrieve the Exception that caused a connection failure to this HostAddress. Every 113 * HostAddress found in {@link ConnectionException} will have an Exception set, 114 * which can be retrieved with this method. 115 * 116 * @return the Exception causing this HostAddress to fail 117 */ 118 public Map<InetAddress, Exception> getExceptions() { 119 return Collections.unmodifiableMap(exceptions); 120 } 121 122 public List<InetAddress> getInetAddresses() { 123 return Collections.unmodifiableList(inetAddresses); 124 } 125 126 @Override 127 public String toString() { 128 return getHost() + ":" + port; 129 } 130 131 @Override 132 public boolean equals(Object o) { 133 if (this == o) { 134 return true; 135 } 136 if (!(o instanceof HostAddress)) { 137 return false; 138 } 139 140 final HostAddress address = (HostAddress) o; 141 142 if (!getHost().equals(address.getHost())) { 143 return false; 144 } 145 return port == address.port; 146 } 147 148 @Override 149 public int hashCode() { 150 int result = 1; 151 result = 37 * result + getHost().hashCode(); 152 return result * 37 + port; 153 } 154 155 public String getErrorMessage() { 156 if (exceptions.isEmpty()) { 157 return "No error logged"; 158 } 159 StringBuilder sb = new StringBuilder(); 160 sb.append('\'').append(toString()).append("' failed because: "); 161 Iterator<Entry<InetAddress, Exception>> iterator = exceptions.entrySet().iterator(); 162 while (iterator.hasNext()) { 163 Entry<InetAddress, Exception> entry = iterator.next(); 164 InetAddress inetAddress = entry.getKey(); 165 if (inetAddress != null) { 166 sb.append(entry.getKey()).append(" exception: "); 167 } 168 sb.append(entry.getValue()); 169 if (iterator.hasNext()) { 170 sb.append(", "); 171 } 172 } 173 174 return sb.toString(); 175 } 176}