1 | /* |
2 | Copyright (C) 2002-2004 MySQL AB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of version 2 of the GNU General Public License as |
6 | published by the Free Software Foundation. |
7 | |
8 | There are special exceptions to the terms and conditions of the GPL |
9 | as it is applied to this software. View the full text of the |
10 | exception in file EXCEPTIONS-CONNECTOR-J in the directory of this |
11 | software distribution. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | |
22 | |
23 | |
24 | */ |
25 | package com.mysql.jdbc; |
26 | |
27 | import java.io.IOException; |
28 | |
29 | import java.lang.reflect.Constructor; |
30 | import java.lang.reflect.InvocationTargetException; |
31 | import java.lang.reflect.Method; |
32 | |
33 | import java.net.InetAddress; |
34 | import java.net.Socket; |
35 | import java.net.SocketException; |
36 | |
37 | import java.util.Properties; |
38 | |
39 | /** |
40 | * Socket factory for vanilla TCP/IP sockets (the standard) |
41 | * |
42 | * @author Mark Matthews |
43 | */ |
44 | public class StandardSocketFactory implements SocketFactory { |
45 | // ~ Instance fields |
46 | // -------------------------------------------------------- |
47 | |
48 | /** The hostname to connect to */ |
49 | protected String host = null; |
50 | |
51 | /** The port number to connect to */ |
52 | protected int port = 3306; |
53 | |
54 | /** The underlying TCP/IP socket to use */ |
55 | protected Socket rawSocket = null; |
56 | |
57 | // ~ Methods |
58 | // ---------------------------------------------------------------- |
59 | |
60 | /** |
61 | * Called by the driver after issuing the MySQL protocol handshake and |
62 | * reading the results of the handshake. |
63 | * |
64 | * @throws SocketException |
65 | * if a socket error occurs |
66 | * @throws IOException |
67 | * if an I/O error occurs |
68 | * |
69 | * @return The socket to use after the handshake |
70 | */ |
71 | public Socket afterHandshake() throws SocketException, IOException { |
72 | return this.rawSocket; |
73 | } |
74 | |
75 | /** |
76 | * Called by the driver before issuing the MySQL protocol handshake. Should |
77 | * return the socket instance that should be used during the handshake. |
78 | * |
79 | * @throws SocketException |
80 | * if a socket error occurs |
81 | * @throws IOException |
82 | * if an I/O error occurs |
83 | * |
84 | * @return the socket to use before the handshake |
85 | */ |
86 | public Socket beforeHandshake() throws SocketException, IOException { |
87 | return this.rawSocket; |
88 | } |
89 | |
90 | /** |
91 | * @see com.mysql.jdbc.SocketFactory#createSocket(Properties) |
92 | */ |
93 | public Socket connect(String hostname, int portNumber, Properties props) |
94 | throws SocketException, IOException { |
95 | |
96 | if (props != null) { |
97 | this.host = hostname; |
98 | |
99 | this.port = portNumber; |
100 | |
101 | boolean hasConnectTimeoutMethod = false; |
102 | |
103 | Method connectWithTimeoutMethod = null; |
104 | |
105 | try { |
106 | // Have to do this with reflection, otherwise older JVMs croak |
107 | Class socketAddressClass = Class |
108 | .forName("java.net.SocketAddress"); |
109 | |
110 | connectWithTimeoutMethod = Socket.class.getMethod("connect", |
111 | new Class[] { socketAddressClass, Integer.TYPE }); |
112 | |
113 | hasConnectTimeoutMethod = true; |
114 | } catch (NoClassDefFoundError noClassDefFound) { |
115 | hasConnectTimeoutMethod = false; |
116 | } catch (NoSuchMethodException noSuchMethodEx) { |
117 | hasConnectTimeoutMethod = false; |
118 | } catch (Throwable catchAll) { |
119 | hasConnectTimeoutMethod = false; |
120 | } |
121 | |
122 | int connectTimeout = 0; |
123 | |
124 | String connectTimeoutStr = props.getProperty("connectTimeout"); |
125 | |
126 | if (connectTimeoutStr != null) { |
127 | try { |
128 | connectTimeout = Integer.parseInt(connectTimeoutStr); |
129 | } catch (NumberFormatException nfe) { |
130 | throw new SocketException("Illegal value '" |
131 | + connectTimeoutStr + "' for connectTimeout"); |
132 | } |
133 | } |
134 | |
135 | if (this.host != null) { |
136 | if (!hasConnectTimeoutMethod || (connectTimeout == 0)) { |
137 | InetAddress[] possibleAddresses = InetAddress |
138 | .getAllByName(this.host); |
139 | |
140 | Exception caughtWhileConnecting = null; |
141 | |
142 | // Need to loop through all possible addresses, in case |
143 | // someone has IPV6 configured (SuSE, for example...) |
144 | |
145 | for (int i = 0; i < possibleAddresses.length; i++) { |
146 | try { |
147 | rawSocket = new Socket(possibleAddresses[i], port); |
148 | |
149 | break; |
150 | } catch (Exception ex) { |
151 | caughtWhileConnecting = ex; |
152 | } |
153 | } |
154 | |
155 | if (rawSocket == null) { |
156 | throw new SocketException(caughtWhileConnecting |
157 | .toString()); |
158 | } |
159 | } else { |
160 | // must explicitly state this due to classloader issues |
161 | // when running on older JVMs :( |
162 | try { |
163 | Class inetSocketAddressClass = Class |
164 | .forName("java.net.InetSocketAddress"); |
165 | Constructor addrConstructor = inetSocketAddressClass |
166 | .getConstructor(new Class[] { String.class, |
167 | Integer.TYPE }); |
168 | |
169 | InetAddress[] possibleAddresses = InetAddress |
170 | .getAllByName(this.host); |
171 | |
172 | Exception caughtWhileConnecting = null; |
173 | |
174 | // Need to loop through all possible addresses, in case |
175 | // someone has IPV6 configured (SuSE, for example...) |
176 | |
177 | for (int i = 0; i < possibleAddresses.length; i++) { |
178 | |
179 | try { |
180 | Object sockAddr = addrConstructor |
181 | .newInstance(new Object[] { this.host, |
182 | new Integer(port) }); |
183 | |
184 | rawSocket = new Socket(); |
185 | connectWithTimeoutMethod.invoke(rawSocket, |
186 | new Object[] { sockAddr, |
187 | new Integer(connectTimeout) }); |
188 | |
189 | break; |
190 | } catch (Exception ex) { |
191 | rawSocket = null; |
192 | |
193 | caughtWhileConnecting = ex; |
194 | } |
195 | } |
196 | |
197 | if (rawSocket == null) { |
198 | throw new SocketException(caughtWhileConnecting |
199 | .toString()); |
200 | } |
201 | |
202 | } catch (Throwable t) { |
203 | if (!(t instanceof SocketException)) { |
204 | throw new SocketException(t.toString()); |
205 | } |
206 | |
207 | throw (SocketException) t; |
208 | } |
209 | } |
210 | |
211 | try { |
212 | this.rawSocket.setTcpNoDelay(true); |
213 | } catch (Exception ex) { |
214 | /* Ignore */ |
215 | ; |
216 | } |
217 | |
218 | return this.rawSocket; |
219 | } |
220 | } |
221 | |
222 | throw new SocketException("Unable to create socket"); |
223 | } |
224 | } |