PageRenderTime 60ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/MapMysql/src/com/mysql/jdbc/StandardSocketFactory.java

https://bitbucket.org/jairomontecinos/nasaworldwindjava
Java | 478 lines | 285 code | 104 blank | 89 comment | 62 complexity | e8936e78a314f8a40547d4cfd9e8a1fc MD5 | raw file
  1. /*
  2. Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
  3. The MySQL Connector/J is licensed under the terms of the GPLv2
  4. <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
  5. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  6. this software, see the FLOSS License Exception
  7. <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
  8. This program is free software; you can redistribute it and/or modify it under the terms
  9. of the GNU General Public License as published by the Free Software Foundation; version 2
  10. of the License.
  11. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  12. without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. See the GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License along with this
  15. program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  16. Floor, Boston, MA 02110-1301 USA
  17. */
  18. package com.mysql.jdbc;
  19. import java.io.IOException;
  20. import java.lang.reflect.Constructor;
  21. import java.lang.reflect.InvocationTargetException;
  22. import java.lang.reflect.Method;
  23. import java.net.InetAddress;
  24. import java.net.InetSocketAddress;
  25. import java.net.Socket;
  26. import java.net.SocketAddress;
  27. import java.net.SocketException;
  28. import java.net.UnknownHostException;
  29. import java.sql.ResultSet;
  30. import java.sql.SQLException;
  31. import java.util.Properties;
  32. /**
  33. * Socket factory for vanilla TCP/IP sockets (the standard)
  34. *
  35. * @author Mark Matthews
  36. */
  37. public class StandardSocketFactory implements SocketFactory, SocketMetadata {
  38. public static final String TCP_NO_DELAY_PROPERTY_NAME = "tcpNoDelay";
  39. public static final String TCP_KEEP_ALIVE_DEFAULT_VALUE = "true";
  40. public static final String TCP_KEEP_ALIVE_PROPERTY_NAME = "tcpKeepAlive";
  41. public static final String TCP_RCV_BUF_PROPERTY_NAME = "tcpRcvBuf";
  42. public static final String TCP_SND_BUF_PROPERTY_NAME = "tcpSndBuf";
  43. public static final String TCP_TRAFFIC_CLASS_PROPERTY_NAME = "tcpTrafficClass";
  44. public static final String TCP_RCV_BUF_DEFAULT_VALUE = "0";
  45. public static final String TCP_SND_BUF_DEFAULT_VALUE = "0";
  46. public static final String TCP_TRAFFIC_CLASS_DEFAULT_VALUE = "0";
  47. public static final String TCP_NO_DELAY_DEFAULT_VALUE = "true";
  48. /** Use reflection for pre-1.4 VMs */
  49. private static Method setTraficClassMethod;
  50. static {
  51. try {
  52. setTraficClassMethod = Socket.class.getMethod("setTrafficClass",
  53. new Class[] { Integer.TYPE });
  54. } catch (SecurityException e) {
  55. setTraficClassMethod = null;
  56. } catch (NoSuchMethodException e) {
  57. setTraficClassMethod = null;
  58. }
  59. }
  60. /** The hostname to connect to */
  61. protected String host = null;
  62. /** The port number to connect to */
  63. protected int port = 3306;
  64. /** The underlying TCP/IP socket to use */
  65. protected Socket rawSocket = null;
  66. /**
  67. * Called by the driver after issuing the MySQL protocol handshake and
  68. * reading the results of the handshake.
  69. *
  70. * @throws SocketException
  71. * if a socket error occurs
  72. * @throws IOException
  73. * if an I/O error occurs
  74. *
  75. * @return The socket to use after the handshake
  76. */
  77. public Socket afterHandshake() throws SocketException, IOException {
  78. return this.rawSocket;
  79. }
  80. /**
  81. * Called by the driver before issuing the MySQL protocol handshake. Should
  82. * return the socket instance that should be used during the handshake.
  83. *
  84. * @throws SocketException
  85. * if a socket error occurs
  86. * @throws IOException
  87. * if an I/O error occurs
  88. *
  89. * @return the socket to use before the handshake
  90. */
  91. public Socket beforeHandshake() throws SocketException, IOException {
  92. return this.rawSocket;
  93. }
  94. /**
  95. * Configures socket properties based on properties from the connection
  96. * (tcpNoDelay, snd/rcv buf, traffic class, etc).
  97. *
  98. * @param props
  99. * @throws SocketException
  100. * @throws IOException
  101. */
  102. private void configureSocket(Socket sock, Properties props) throws SocketException,
  103. IOException {
  104. try {
  105. sock.setTcpNoDelay(Boolean.valueOf(
  106. props.getProperty(TCP_NO_DELAY_PROPERTY_NAME,
  107. TCP_NO_DELAY_DEFAULT_VALUE)).booleanValue());
  108. String keepAlive = props.getProperty(TCP_KEEP_ALIVE_PROPERTY_NAME,
  109. TCP_KEEP_ALIVE_DEFAULT_VALUE);
  110. if (keepAlive != null && keepAlive.length() > 0) {
  111. sock.setKeepAlive(Boolean.valueOf(keepAlive)
  112. .booleanValue());
  113. }
  114. int receiveBufferSize = Integer.parseInt(props.getProperty(
  115. TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));
  116. if (receiveBufferSize > 0) {
  117. sock.setReceiveBufferSize(receiveBufferSize);
  118. }
  119. int sendBufferSize = Integer.parseInt(props.getProperty(
  120. TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));
  121. if (sendBufferSize > 0) {
  122. sock.setSendBufferSize(sendBufferSize);
  123. }
  124. int trafficClass = Integer.parseInt(props.getProperty(
  125. TCP_TRAFFIC_CLASS_PROPERTY_NAME,
  126. TCP_TRAFFIC_CLASS_DEFAULT_VALUE));
  127. if (trafficClass > 0 && setTraficClassMethod != null) {
  128. setTraficClassMethod.invoke(sock,
  129. new Object[] { Integer.valueOf(trafficClass) });
  130. }
  131. } catch (Throwable t) {
  132. unwrapExceptionToProperClassAndThrowIt(t);
  133. }
  134. }
  135. /**
  136. * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
  137. */
  138. public Socket connect(String hostname, int portNumber, Properties props)
  139. throws SocketException, IOException {
  140. if (props != null) {
  141. this.host = hostname;
  142. this.port = portNumber;
  143. Method connectWithTimeoutMethod = null;
  144. Method socketBindMethod = null;
  145. Class<?> socketAddressClass = null;
  146. String localSocketHostname = props
  147. .getProperty("localSocketAddress");
  148. String connectTimeoutStr = props.getProperty("connectTimeout");
  149. int connectTimeout = 0;
  150. boolean wantsTimeout = (connectTimeoutStr != null
  151. && connectTimeoutStr.length() > 0 && !connectTimeoutStr
  152. .equals("0"));
  153. boolean wantsLocalBind = (localSocketHostname != null && localSocketHostname
  154. .length() > 0);
  155. boolean needsConfigurationBeforeConnect = socketNeedsConfigurationBeforeConnect(props);
  156. if (wantsTimeout || wantsLocalBind || needsConfigurationBeforeConnect) {
  157. if (connectTimeoutStr != null) {
  158. try {
  159. connectTimeout = Integer.parseInt(connectTimeoutStr);
  160. } catch (NumberFormatException nfe) {
  161. throw new SocketException("Illegal value '"
  162. + connectTimeoutStr + "' for connectTimeout");
  163. }
  164. }
  165. try {
  166. // Have to do this with reflection, otherwise older JVMs
  167. // croak
  168. socketAddressClass = Class
  169. .forName("java.net.SocketAddress");
  170. connectWithTimeoutMethod = Socket.class.getMethod(
  171. "connect", new Class[] { socketAddressClass,
  172. Integer.TYPE });
  173. socketBindMethod = Socket.class.getMethod("bind",
  174. new Class[] { socketAddressClass });
  175. } catch (NoClassDefFoundError noClassDefFound) {
  176. // ignore, we give a better error below if needed
  177. } catch (NoSuchMethodException noSuchMethodEx) {
  178. // ignore, we give a better error below if needed
  179. } catch (Throwable catchAll) {
  180. // ignore, we give a better error below if needed
  181. }
  182. if (wantsLocalBind && socketBindMethod == null) {
  183. throw new SocketException(
  184. "Can't specify \"localSocketAddress\" on JVMs older than 1.4");
  185. }
  186. if (wantsTimeout && connectWithTimeoutMethod == null) {
  187. throw new SocketException(
  188. "Can't specify \"connectTimeout\" on JVMs older than 1.4");
  189. }
  190. }
  191. if (this.host != null) {
  192. if (!(wantsLocalBind || wantsTimeout || needsConfigurationBeforeConnect)) {
  193. InetAddress[] possibleAddresses = InetAddress
  194. .getAllByName(this.host);
  195. Throwable caughtWhileConnecting = null;
  196. // Need to loop through all possible addresses, in case
  197. // someone has IPV6 configured (SuSE, for example...)
  198. for (int i = 0; i < possibleAddresses.length; i++) {
  199. try {
  200. this.rawSocket = new Socket(possibleAddresses[i],
  201. port);
  202. configureSocket(this.rawSocket, props);
  203. break;
  204. } catch (Exception ex) {
  205. caughtWhileConnecting = ex;
  206. }
  207. }
  208. if (rawSocket == null) {
  209. unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
  210. }
  211. } else {
  212. // must explicitly state this due to classloader issues
  213. // when running on older JVMs :(
  214. try {
  215. InetAddress[] possibleAddresses = InetAddress
  216. .getAllByName(this.host);
  217. Throwable caughtWhileConnecting = null;
  218. Object localSockAddr = null;
  219. Class<?> inetSocketAddressClass = null;
  220. Constructor<?> addrConstructor = null;
  221. try {
  222. inetSocketAddressClass = Class
  223. .forName("java.net.InetSocketAddress");
  224. addrConstructor = inetSocketAddressClass
  225. .getConstructor(new Class[] {
  226. InetAddress.class, Integer.TYPE });
  227. if (wantsLocalBind) {
  228. localSockAddr = addrConstructor
  229. .newInstance(new Object[] {
  230. InetAddress
  231. .getByName(localSocketHostname),
  232. new Integer(0 /*
  233. * use ephemeral
  234. * port
  235. */) });
  236. }
  237. } catch (Throwable ex) {
  238. unwrapExceptionToProperClassAndThrowIt(ex);
  239. }
  240. // Need to loop through all possible addresses, in case
  241. // someone has IPV6 configured (SuSE, for example...)
  242. for (int i = 0; i < possibleAddresses.length; i++) {
  243. try {
  244. this.rawSocket = new Socket();
  245. configureSocket(this.rawSocket, props);
  246. Object sockAddr = addrConstructor
  247. .newInstance(new Object[] {
  248. possibleAddresses[i],
  249. Integer.valueOf(port) });
  250. // bind to the local port if not using the ephemeral port
  251. if (localSockAddr != null) {
  252. socketBindMethod.invoke(rawSocket,
  253. new Object[] { localSockAddr });
  254. }
  255. connectWithTimeoutMethod.invoke(rawSocket,
  256. new Object[] { sockAddr,
  257. Integer.valueOf(connectTimeout) });
  258. break;
  259. } catch (Exception ex) {
  260. this.rawSocket = null;
  261. caughtWhileConnecting = ex;
  262. }
  263. }
  264. if (this.rawSocket == null) {
  265. unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
  266. }
  267. } catch (Throwable t) {
  268. unwrapExceptionToProperClassAndThrowIt(t);
  269. }
  270. }
  271. return this.rawSocket;
  272. }
  273. }
  274. throw new SocketException("Unable to create socket");
  275. }
  276. /**
  277. * Does the configureSocket() need to be called before the socket is
  278. * connect()d based on the properties supplied?
  279. *
  280. */
  281. private boolean socketNeedsConfigurationBeforeConnect(Properties props) {
  282. int receiveBufferSize = Integer.parseInt(props.getProperty(
  283. TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));
  284. if (receiveBufferSize > 0) {
  285. return true;
  286. }
  287. int sendBufferSize = Integer.parseInt(props.getProperty(
  288. TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));
  289. if (sendBufferSize > 0) {
  290. return true;
  291. }
  292. int trafficClass = Integer.parseInt(props.getProperty(
  293. TCP_TRAFFIC_CLASS_PROPERTY_NAME,
  294. TCP_TRAFFIC_CLASS_DEFAULT_VALUE));
  295. if (trafficClass > 0 && setTraficClassMethod != null) {
  296. return true;
  297. }
  298. return false;
  299. }
  300. private void unwrapExceptionToProperClassAndThrowIt(
  301. Throwable caughtWhileConnecting) throws SocketException,
  302. IOException {
  303. if (caughtWhileConnecting instanceof InvocationTargetException) {
  304. // Replace it with the target, don't use 1.4 chaining as this still
  305. // needs to run on older VMs
  306. caughtWhileConnecting = ((InvocationTargetException) caughtWhileConnecting)
  307. .getTargetException();
  308. }
  309. if (caughtWhileConnecting instanceof SocketException) {
  310. throw (SocketException) caughtWhileConnecting;
  311. }
  312. if (caughtWhileConnecting instanceof IOException) {
  313. throw (IOException) caughtWhileConnecting;
  314. }
  315. throw new SocketException(caughtWhileConnecting.toString());
  316. }
  317. public static final String IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME = "com.mysql.jdbc.test.isLocalHostnameReplacement";
  318. public boolean isLocallyConnected(com.mysql.jdbc.ConnectionImpl conn)
  319. throws SQLException {
  320. long threadId = conn.getId();
  321. java.sql.Statement processListStmt = conn.getMetadataSafeStatement();
  322. ResultSet rs = null;
  323. try {
  324. String processHost = null;
  325. rs = processListStmt.executeQuery("SHOW PROCESSLIST");
  326. while (rs.next()) {
  327. long id = rs.getLong(1);
  328. if (threadId == id) {
  329. processHost = rs.getString(3);
  330. break;
  331. }
  332. }
  333. // "inject" for tests
  334. if (System.getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME) != null) {
  335. processHost = System
  336. .getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME);
  337. } else if (conn.getProperties().getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME) != null) {
  338. processHost = conn.getProperties().getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME);
  339. }
  340. if (processHost != null) {
  341. if (processHost.indexOf(":") != -1) {
  342. processHost = processHost.split(":")[0];
  343. try {
  344. boolean isLocal = false;
  345. InetAddress whereMysqlThinksIConnectedFrom = InetAddress.getByName(processHost);
  346. SocketAddress remoteSocketAddr = rawSocket.getRemoteSocketAddress();
  347. if (remoteSocketAddr instanceof InetSocketAddress) {
  348. InetAddress whereIConnectedTo = ((InetSocketAddress)remoteSocketAddr).getAddress();
  349. isLocal = whereMysqlThinksIConnectedFrom.equals(whereIConnectedTo);
  350. }
  351. return isLocal;
  352. } catch (UnknownHostException e) {
  353. conn.getLog().logWarn(
  354. Messages.getString(
  355. "Connection.CantDetectLocalConnect",
  356. new Object[] { host }), e);
  357. return false;
  358. }
  359. }
  360. }
  361. return false;
  362. } finally {
  363. processListStmt.close();
  364. }
  365. }
  366. }