1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java $ 3 * $Revision: 653041 $ 4 * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $ 5 * 6 * ==================================================================== 7 * Licensed to the Apache Software Foundation (ASF) under one 8 * or more contributor license agreements. See the NOTICE file 9 * distributed with this work for additional information 10 * regarding copyright ownership. The ASF licenses this file 11 * to you under the Apache License, Version 2.0 (the 12 * "License"); you may not use this file except in compliance 13 * with the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, 18 * software distributed under the License is distributed on an 19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 * KIND, either express or implied. See the License for the 21 * specific language governing permissions and limitations 22 * under the License. 23 * ==================================================================== 24 * 25 * This software consists of voluntary contributions made by many 26 * individuals on behalf of the Apache Software Foundation. For more 27 * information on the Apache Software Foundation, please see 28 * <http://www.apache.org/>. 29 * 30 */ 31 32 package org.apache.http.conn; 33 34 import java.io.IOException; 35 import java.net.InetAddress; 36 import java.net.InetSocketAddress; 37 import java.net.Socket; 38 import java.net.SocketTimeoutException; 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.Arrays; 43 44 import org.apache.http.conn.scheme.PlainSocketFactory; 45 import org.apache.http.conn.scheme.SocketFactory; 46 import org.apache.http.params.HttpConnectionParams; 47 import org.apache.http.params.HttpParams; 48 49 /** 50 * Socket factory that implements a simple multi-home fail-over on connect failure, 51 * provided the same hostname resolves to multiple {@link InetAddress}es. Please note 52 * the {@link #connectSocket(Socket, String, int, InetAddress, int, HttpParams)} 53 * method cannot be reliably interrupted by closing the socket returned by the 54 * {@link #createSocket()} method. 55 */ 56 public final class MultihomePlainSocketFactory implements SocketFactory { 57 58 /** 59 * The factory singleton. 60 */ 61 private static final 62 MultihomePlainSocketFactory DEFAULT_FACTORY = new MultihomePlainSocketFactory(); 63 64 /** 65 * Gets the singleton instance of this class. 66 * @return the one and only plain socket factory 67 */ 68 public static MultihomePlainSocketFactory getSocketFactory() { 69 return DEFAULT_FACTORY; 70 } 71 72 /** 73 * Restricted default constructor. 74 */ 75 private MultihomePlainSocketFactory() { 76 super(); 77 } 78 79 80 // non-javadoc, see interface org.apache.http.conn.SocketFactory 81 public Socket createSocket() { 82 return new Socket(); 83 } 84 85 /** 86 * Attempts to connects the socket to any of the {@link InetAddress}es the 87 * given host name resolves to. If connection to all addresses fail, the 88 * last I/O exception is propagated to the caller. 89 * 90 * @param sock socket to connect to any of the given addresses 91 * @param host Host name to connect to 92 * @param port the port to connect to 93 * @param localAddress local address 94 * @param localPort local port 95 * @param params HTTP parameters 96 * 97 * @throws IOException if an error occurs during the connection 98 * @throws SocketTimeoutException if timeout expires before connecting 99 */ 100 public Socket connectSocket(Socket sock, String host, int port, 101 InetAddress localAddress, int localPort, 102 HttpParams params) 103 throws IOException { 104 105 if (host == null) { 106 throw new IllegalArgumentException("Target host may not be null."); 107 } 108 if (params == null) { 109 throw new IllegalArgumentException("Parameters may not be null."); 110 } 111 112 if (sock == null) 113 sock = createSocket(); 114 115 if ((localAddress != null) || (localPort > 0)) { 116 117 // we need to bind explicitly 118 if (localPort < 0) 119 localPort = 0; // indicates "any" 120 121 InetSocketAddress isa = 122 new InetSocketAddress(localAddress, localPort); 123 sock.bind(isa); 124 } 125 126 int timeout = HttpConnectionParams.getConnectionTimeout(params); 127 128 InetAddress[] inetadrs = InetAddress.getAllByName(host); 129 List<InetAddress> addresses = new ArrayList<InetAddress>(inetadrs.length); 130 addresses.addAll(Arrays.asList(inetadrs)); 131 Collections.shuffle(addresses); 132 133 IOException lastEx = null; 134 for (InetAddress address: addresses) { 135 try { 136 sock.connect(new InetSocketAddress(address, port), timeout); 137 break; 138 } catch (SocketTimeoutException ex) { 139 throw ex; 140 } catch (IOException ex) { 141 // create new socket 142 sock = new Socket(); 143 // keep the last exception and retry 144 lastEx = ex; 145 } 146 } 147 if (lastEx != null) { 148 throw lastEx; 149 } 150 return sock; 151 } // connectSocket 152 153 154 /** 155 * Checks whether a socket connection is secure. 156 * This factory creates plain socket connections 157 * which are not considered secure. 158 * 159 * @param sock the connected socket 160 * 161 * @return <code>false</code> 162 * 163 * @throws IllegalArgumentException if the argument is invalid 164 */ 165 public final boolean isSecure(Socket sock) 166 throws IllegalArgumentException { 167 168 if (sock == null) { 169 throw new IllegalArgumentException("Socket may not be null."); 170 } 171 // This class check assumes that createSocket() calls the constructor 172 // directly. If it was using javax.net.SocketFactory, we couldn't make 173 // an assumption about the socket class here. 174 if (sock.getClass() != Socket.class) { 175 throw new IllegalArgumentException 176 ("Socket not created by this factory."); 177 } 178 // This check is performed last since it calls a method implemented 179 // by the argument object. getClass() is final in java.lang.Object. 180 if (sock.isClosed()) { 181 throw new IllegalArgumentException("Socket is closed."); 182 } 183 184 return false; 185 186 } // isSecure 187 188 189 /** 190 * Compares this factory with an object. 191 * There is only one instance of this class. 192 * 193 * @param obj the object to compare with 194 * 195 * @return iff the argument is this object 196 */ 197 @Override 198 public boolean equals(Object obj) { 199 return (obj == this); 200 } 201 202 /** 203 * Obtains a hash code for this object. 204 * All instances of this class have the same hash code. 205 * There is only one instance of this class. 206 */ 207 @Override 208 public int hashCode() { 209 return PlainSocketFactory.class.hashCode(); 210 } 211 212 } 213