Logo Search packages:      
Sourcecode: mysql-connector-java version File versions  Download package

StandardSocketFactory.java

/*
 Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
 

  The MySQL Connector/J is licensed under the terms of the GPLv2
  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  this software, see the FLOSS License Exception
  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.

  This program is free software; you can redistribute it and/or modify it under the terms
  of the GNU General Public License as published by the Free Software Foundation; version 2
  of the License.

  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along with this
  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  Floor, Boston, MA 02110-1301  USA



 */
package com.mysql.jdbc;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * Socket factory for vanilla TCP/IP sockets (the standard)
 * 
 * @author Mark Matthews
 */
00045 public class StandardSocketFactory implements SocketFactory, SocketMetadata {

      public static final String TCP_NO_DELAY_PROPERTY_NAME = "tcpNoDelay";

      public static final String TCP_KEEP_ALIVE_DEFAULT_VALUE = "true";

      public static final String TCP_KEEP_ALIVE_PROPERTY_NAME = "tcpKeepAlive";

      public static final String TCP_RCV_BUF_PROPERTY_NAME = "tcpRcvBuf";

      public static final String TCP_SND_BUF_PROPERTY_NAME = "tcpSndBuf";

      public static final String TCP_TRAFFIC_CLASS_PROPERTY_NAME = "tcpTrafficClass";

      public static final String TCP_RCV_BUF_DEFAULT_VALUE = "0";

      public static final String TCP_SND_BUF_DEFAULT_VALUE = "0";

      public static final String TCP_TRAFFIC_CLASS_DEFAULT_VALUE = "0";

      public static final String TCP_NO_DELAY_DEFAULT_VALUE = "true";

      /** Use reflection for pre-1.4 VMs */

00069       private static Method setTraficClassMethod;

      static {
            try {
                  setTraficClassMethod = Socket.class.getMethod("setTrafficClass",
                              new Class[] { Integer.TYPE });
            } catch (SecurityException e) {
                  setTraficClassMethod = null;
            } catch (NoSuchMethodException e) {
                  setTraficClassMethod = null;
            }
      }

      /** The hostname to connect to */
00083       protected String host = null;

      /** The port number to connect to */
00086       protected int port = 3306;

      /** The underlying TCP/IP socket to use */
00089       protected Socket rawSocket = null;

      /**
       * Called by the driver after issuing the MySQL protocol handshake and
       * reading the results of the handshake.
       * 
       * @throws SocketException
       *             if a socket error occurs
       * @throws IOException
       *             if an I/O error occurs
       * 
       * @return The socket to use after the handshake
       */
00102       public Socket afterHandshake() throws SocketException, IOException {
            return this.rawSocket;
      }

      /**
       * Called by the driver before issuing the MySQL protocol handshake. Should
       * return the socket instance that should be used during the handshake.
       * 
       * @throws SocketException
       *             if a socket error occurs
       * @throws IOException
       *             if an I/O error occurs
       * 
       * @return the socket to use before the handshake
       */
00117       public Socket beforeHandshake() throws SocketException, IOException {
            return this.rawSocket;
      }

      /**
       * Configures socket properties based on properties from the connection
       * (tcpNoDelay, snd/rcv buf, traffic class, etc).
       * 
       * @param props
       * @throws SocketException
       * @throws IOException
       */
00129       private void configureSocket(Socket sock, Properties props) throws SocketException,
                  IOException {
            try {
                  sock.setTcpNoDelay(Boolean.valueOf(
                              props.getProperty(TCP_NO_DELAY_PROPERTY_NAME,
                                          TCP_NO_DELAY_DEFAULT_VALUE)).booleanValue());

                  String keepAlive = props.getProperty(TCP_KEEP_ALIVE_PROPERTY_NAME,
                              TCP_KEEP_ALIVE_DEFAULT_VALUE);

                  if (keepAlive != null && keepAlive.length() > 0) {
                        sock.setKeepAlive(Boolean.valueOf(keepAlive)
                                    .booleanValue());
                  }

                  int receiveBufferSize = Integer.parseInt(props.getProperty(
                              TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));

                  if (receiveBufferSize > 0) {
                        sock.setReceiveBufferSize(receiveBufferSize);
                  }

                  int sendBufferSize = Integer.parseInt(props.getProperty(
                              TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));

                  if (sendBufferSize > 0) {
                        sock.setSendBufferSize(sendBufferSize);
                  }

                  int trafficClass = Integer.parseInt(props.getProperty(
                              TCP_TRAFFIC_CLASS_PROPERTY_NAME,
                              TCP_TRAFFIC_CLASS_DEFAULT_VALUE));

                  if (trafficClass > 0 && setTraficClassMethod != null) {
                        setTraficClassMethod.invoke(sock,
                                    new Object[] { Integer.valueOf(trafficClass) });
                  }
            } catch (Throwable t) {
                  unwrapExceptionToProperClassAndThrowIt(t);
            }
      }

      /**
       * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
       */
00174       public Socket connect(String hostname, int portNumber, Properties props)
                  throws SocketException, IOException {

            if (props != null) {
                  this.host = hostname;

                  this.port = portNumber;

                  Method connectWithTimeoutMethod = null;
                  Method socketBindMethod = null;
                  Class socketAddressClass = null;

                  String localSocketHostname = props
                              .getProperty("localSocketAddress");

                  String connectTimeoutStr = props.getProperty("connectTimeout");

                  int connectTimeout = 0;

                  boolean wantsTimeout = (connectTimeoutStr != null
                              && connectTimeoutStr.length() > 0 && !connectTimeoutStr
                              .equals("0"));

                  boolean wantsLocalBind = (localSocketHostname != null && localSocketHostname
                              .length() > 0);

                  boolean needsConfigurationBeforeConnect = socketNeedsConfigurationBeforeConnect(props);
                  
                  if (wantsTimeout || wantsLocalBind || needsConfigurationBeforeConnect) {

                        if (connectTimeoutStr != null) {
                              try {
                                    connectTimeout = Integer.parseInt(connectTimeoutStr);
                              } catch (NumberFormatException nfe) {
                                    throw new SocketException("Illegal value '"
                                                + connectTimeoutStr + "' for connectTimeout");
                              }
                        }

                        try {
                              // Have to do this with reflection, otherwise older JVMs
                              // croak
                              socketAddressClass = Class
                                          .forName("java.net.SocketAddress");

                              connectWithTimeoutMethod = Socket.class.getMethod(
                                          "connect", new Class[] { socketAddressClass,
                                                      Integer.TYPE });

                              socketBindMethod = Socket.class.getMethod("bind",
                                          new Class[] { socketAddressClass });

                        } catch (NoClassDefFoundError noClassDefFound) {
                              // ignore, we give a better error below if needed
                        } catch (NoSuchMethodException noSuchMethodEx) {
                              // ignore, we give a better error below if needed
                        } catch (Throwable catchAll) {
                              // ignore, we give a better error below if needed
                        }

                        if (wantsLocalBind && socketBindMethod == null) {
                              throw new SocketException(
                                          "Can't specify \"localSocketAddress\" on JVMs older than 1.4");
                        }

                        if (wantsTimeout && connectWithTimeoutMethod == null) {
                              throw new SocketException(
                                          "Can't specify \"connectTimeout\" on JVMs older than 1.4");
                        }
                  }

                  if (this.host != null) {
                        if (!(wantsLocalBind || wantsTimeout || needsConfigurationBeforeConnect)) {
                              InetAddress[] possibleAddresses = InetAddress
                                          .getAllByName(this.host);

                              Throwable caughtWhileConnecting = null;

                              // Need to loop through all possible addresses, in case
                              // someone has IPV6 configured (SuSE, for example...)

                              for (int i = 0; i < possibleAddresses.length; i++) {
                                    try {
                                          this.rawSocket = new Socket(possibleAddresses[i],
                                                      port);

                                          configureSocket(this.rawSocket, props);

                                          break;
                                    } catch (Exception ex) {
                                          caughtWhileConnecting = ex;
                                    }
                              }

                              if (rawSocket == null) {
                                    unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
                              }
                        } else {
                              // must explicitly state this due to classloader issues
                              // when running on older JVMs :(
                              try {

                                    InetAddress[] possibleAddresses = InetAddress
                                                .getAllByName(this.host);

                                    Throwable caughtWhileConnecting = null;

                                    Object localSockAddr = null;

                                    Class inetSocketAddressClass = null;

                                    Constructor addrConstructor = null;

                                    try {
                                          inetSocketAddressClass = Class
                                                      .forName("java.net.InetSocketAddress");

                                          addrConstructor = inetSocketAddressClass
                                                      .getConstructor(new Class[] {
                                                                  InetAddress.class, Integer.TYPE });

                                          if (wantsLocalBind) {
                                                localSockAddr = addrConstructor
                                                            .newInstance(new Object[] {
                                                                        InetAddress
                                                                                    .getByName(localSocketHostname),
                                                                        new Integer(0 /*
                                                                                                 * use ephemeral
                                                                                                 * port
                                                                                                 */) });

                                          }
                                    } catch (Throwable ex) {
                                          unwrapExceptionToProperClassAndThrowIt(ex);
                                    }

                                    // Need to loop through all possible addresses, in case
                                    // someone has IPV6 configured (SuSE, for example...)

                                    for (int i = 0; i < possibleAddresses.length; i++) {

                                          try {
                                                this.rawSocket = new Socket();

                                                configureSocket(this.rawSocket, props);

                                                Object sockAddr = addrConstructor
                                                            .newInstance(new Object[] {
                                                                        possibleAddresses[i],
                                                                        Integer.valueOf(port) });
                                                // bind to the local port, null is 'ok', it
                                                // means
                                                // use the ephemeral port
                                                socketBindMethod.invoke(rawSocket,
                                                            new Object[] { localSockAddr });

                                                connectWithTimeoutMethod.invoke(rawSocket,
                                                            new Object[] { sockAddr,
                                                                        Integer.valueOf(connectTimeout) });

                                                break;
                                          } catch (Exception ex) {      
                                                this.rawSocket = null;

                                                caughtWhileConnecting = ex;
                                          }
                                    }

                                    if (this.rawSocket == null) {
                                          unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
                                    }

                              } catch (Throwable t) {
                                    unwrapExceptionToProperClassAndThrowIt(t);
                              }
                        }

                        return this.rawSocket;
                  }
            }

            throw new SocketException("Unable to create socket");
      }

      /**
       * Does the configureSocket() need to be called before the socket is
       * connect()d based on the properties supplied?
       * 
       */
00363       private boolean socketNeedsConfigurationBeforeConnect(Properties props) {
            int receiveBufferSize = Integer.parseInt(props.getProperty(
                        TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));

            if (receiveBufferSize > 0) {
                  return true;
            }

            int sendBufferSize = Integer.parseInt(props.getProperty(
                        TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));

            if (sendBufferSize > 0) {
                  return true;
            }

            int trafficClass = Integer.parseInt(props.getProperty(
                        TCP_TRAFFIC_CLASS_PROPERTY_NAME,
                        TCP_TRAFFIC_CLASS_DEFAULT_VALUE));

            if (trafficClass > 0 && setTraficClassMethod != null) {
                  return true;
            }

            return false;
      }

      private void unwrapExceptionToProperClassAndThrowIt(
                  Throwable caughtWhileConnecting) throws SocketException,
                  IOException {
            if (caughtWhileConnecting instanceof InvocationTargetException) {

                  // Replace it with the target, don't use 1.4 chaining as this still
                  // needs to run on older VMs
                  caughtWhileConnecting = ((InvocationTargetException) caughtWhileConnecting)
                              .getTargetException();
            }

            if (caughtWhileConnecting instanceof SocketException) {
                  throw (SocketException) caughtWhileConnecting;
            }

            if (caughtWhileConnecting instanceof IOException) {
                  throw (IOException) caughtWhileConnecting;
            }

            throw new SocketException(caughtWhileConnecting.toString());
      }

      public boolean isLocallyConnected(com.mysql.jdbc.ConnectionImpl conn) throws SQLException {
            long threadId = conn.getId();
            java.sql.Statement processListStmt = conn.getMetadataSafeStatement();
            ResultSet rs = null;
            
            try {
                  String processHost = null;
                  
                  rs = processListStmt.executeQuery("SHOW PROCESSLIST");
                  
                  while (rs.next()) {
                         long id = rs.getLong(1);

                 if (threadId == id) {
                   processHost = rs.getString(3);
                     
                     break;
                 }
                  }
             
             if (processHost != null) {
                 if (processHost.indexOf(":") != -1) {
                   processHost = processHost.split(":")[0];

                     try {
                         boolean isLocal;

                         isLocal = InetAddress.getByName(processHost).equals(
                                     rawSocket.getLocalAddress());

                         return isLocal;
                     } catch (UnknownHostException e) {
                         conn.getLog().logWarn(Messages.getString("Connection.CantDetectLocalConnect", new Object[] {host}), e);

                         return false;
                     }
                 }
             }
             
             return false;
            } finally {
                  processListStmt.close();
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index