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 * @param inetAddresses list of addresses. 042 * @throws IllegalArgumentException If the port is out of valid range (0 - 65535). 043 */ 044 public HostAddress(String fqdn, int port, List<InetAddress> inetAddresses) { 045 if (port < 0 || port > 65535) 046 throw new IllegalArgumentException( 047 "Port must be a 16-bit unsigned integer (i.e. between 0-65535. Port was: " + port); 048 if (StringUtils.isNotEmpty(fqdn) && fqdn.charAt(fqdn.length() - 1) == '.') { 049 this.fqdn = fqdn.substring(0, fqdn.length() - 1); 050 } 051 else { 052 this.fqdn = fqdn; 053 } 054 this.port = port; 055 if (inetAddresses.isEmpty()) { 056 throw new IllegalArgumentException("Must provide at least one InetAddress"); 057 } 058 this.inetAddresses = inetAddresses; 059 } 060 061 public HostAddress(int port, InetAddress hostAddress) { 062 this(null, port, Collections.singletonList(hostAddress)); 063 } 064 065 /** 066 * Constructs a new failed HostAddress. This constructor is usually used when the DNS resolution of the domain name 067 * failed for some reason. 068 * 069 * @param fqdn the domain name of the host. 070 * @param e the exception causing the failure. 071 */ 072 public HostAddress(String fqdn, Exception e) { 073 this.fqdn = fqdn; 074 this.port = 5222; 075 inetAddresses = Collections.emptyList(); 076 setException(e); 077 } 078 079 public String getHost() { 080 if (fqdn != null) { 081 return fqdn; 082 } 083 084 // In this case, the HostAddress(int, InetAddress) constructor must been used. We have no FQDN. And 085 // inetAddresses.size() must be exactly one. 086 assert inetAddresses.size() == 1; 087 return inetAddresses.get(0).getHostAddress(); 088 } 089 090 /** 091 * 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. 092 * 093 * @return the fully qualified domain name or <code>null</code> 094 */ 095 public String getFQDN() { 096 return fqdn; 097 } 098 099 public int getPort() { 100 return port; 101 } 102 103 public void setException(Exception exception) { 104 setException(null, exception); 105 } 106 107 public void setException(InetAddress inetAddress, Exception exception) { 108 Exception old = exceptions.put(inetAddress, exception); 109 assert (old == null); 110 } 111 112 /** 113 * Retrieve the Exception that caused a connection failure to this HostAddress. Every 114 * HostAddress found in {@link ConnectionException} will have an Exception set, 115 * which can be retrieved with this method. 116 * 117 * @return the Exception causing this HostAddress to fail 118 */ 119 public Map<InetAddress, Exception> getExceptions() { 120 return Collections.unmodifiableMap(exceptions); 121 } 122 123 public List<InetAddress> getInetAddresses() { 124 return Collections.unmodifiableList(inetAddresses); 125 } 126 127 @Override 128 public String toString() { 129 return getHost() + ":" + port; 130 } 131 132 @Override 133 public boolean equals(Object o) { 134 if (this == o) { 135 return true; 136 } 137 if (!(o instanceof HostAddress)) { 138 return false; 139 } 140 141 final HostAddress address = (HostAddress) o; 142 143 if (!getHost().equals(address.getHost())) { 144 return false; 145 } 146 return port == address.port; 147 } 148 149 @Override 150 public int hashCode() { 151 int result = 1; 152 result = 37 * result + getHost().hashCode(); 153 return result * 37 + port; 154 } 155 156 public String getErrorMessage() { 157 if (exceptions.isEmpty()) { 158 return "No error logged"; 159 } 160 StringBuilder sb = new StringBuilder(); 161 sb.append('\'').append(toString()).append("' failed because: "); 162 Iterator<Entry<InetAddress, Exception>> iterator = exceptions.entrySet().iterator(); 163 while (iterator.hasNext()) { 164 Entry<InetAddress, Exception> entry = iterator.next(); 165 InetAddress inetAddress = entry.getKey(); 166 if (inetAddress != null) { 167 sb.append(entry.getKey()).append(" exception: "); 168 } 169 sb.append(entry.getValue()); 170 if (iterator.hasNext()) { 171 sb.append(", "); 172 } 173 } 174 175 return sb.toString(); 176 } 177}