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