src/share/classes/com/sun/jndi/ldap/Connection.java

Print this page
rev 10284 : 8048175: Remove redundant use of reflection on core classes from JNDI
Reviewed-by: duke


  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.ldap;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.BufferedOutputStream;
  30 import java.io.InterruptedIOException;
  31 import java.io.IOException;
  32 import java.io.OutputStream;
  33 import java.io.InputStream;

  34 import java.net.Socket;
  35 import javax.net.ssl.SSLSocket;
  36 
  37 import javax.naming.CommunicationException;
  38 import javax.naming.ServiceUnavailableException;
  39 import javax.naming.NamingException;
  40 import javax.naming.InterruptedNamingException;
  41 
  42 import javax.naming.ldap.Control;
  43 
  44 import java.lang.reflect.Method;
  45 import java.lang.reflect.Constructor;
  46 import java.lang.reflect.InvocationTargetException;
  47 import java.util.Arrays;
  48 import sun.misc.IOUtils;
  49 //import javax.net.SocketFactory;
  50 
  51 /**
  52   * A thread that creates a connection to an LDAP server.
  53   * After the connection, the thread reads from the connection.
  54   * A caller can invoke methods on the instance to read LDAP responses
  55   * and to send LDAP requests.
  56   * <p>
  57   * There is a one-to-one correspondence between an LdapClient and
  58   * a Connection. Access to Connection and its methods is only via
  59   * LdapClient with two exceptions: SASL authentication and StartTLS.
  60   * SASL needs to access Connection's socket IO streams (in order to do encryption
  61   * of the security layer). StartTLS needs to do replace IO streams
  62   * and close the IO  streams on nonfatal close. The code for SASL
  63   * authentication can be treated as being the same as from LdapClient
  64   * because the SASL code is only ever called from LdapClient, from
  65   * inside LdapClient's synchronized authenticate() method. StartTLS is called
  66   * directly by the application but should only occur when the underlying
  67   * connection is quiet.
  68   * <p>
  69   * In terms of synchronization, worry about data structures


 202         //
 203         try {
 204             sock = createSocket(host, port, socketFactory, connectTimeout);
 205 
 206             if (debug) {
 207                 System.err.println("Connection: opening socket: " + host + "," + port);
 208             }
 209 
 210             inStream = new BufferedInputStream(sock.getInputStream());
 211             outStream = new BufferedOutputStream(sock.getOutputStream());
 212 
 213         } catch (InvocationTargetException e) {
 214             Throwable realException = e.getTargetException();
 215             // realException.printStackTrace();
 216 
 217             CommunicationException ce =
 218                 new CommunicationException(host + ":" + port);
 219             ce.setRootCause(realException);
 220             throw ce;
 221         } catch (Exception e) {
 222             // Class.forName() seems to do more error checking
 223             // and will throw IllegalArgumentException and such.
 224             // That's why we need to have a catch all here and
 225             // ignore generic exceptions.
 226             // Also catches all IO errors generated by socket creation.
 227             CommunicationException ce =
 228                 new CommunicationException(host + ":" + port);
 229             ce.setRootCause(e);
 230             throw ce;
 231         }
 232 
 233         worker = Obj.helper.createThread(this);
 234         worker.setDaemon(true);
 235         worker.start();
 236     }
 237 
 238     /*
 239      * Create an InetSocketAddress using the specified hostname and port number.
 240      */
 241     private Object createInetSocketAddress(String host, int port)
 242             throws NoSuchMethodException {
 243 
 244         try {
 245             Class<?> inetSocketAddressClass =
 246                 Class.forName("java.net.InetSocketAddress");
 247 
 248             Constructor<?> inetSocketAddressCons =
 249                 inetSocketAddressClass.getConstructor(new Class<?>[]{
 250                 String.class, int.class});
 251 
 252             return inetSocketAddressCons.newInstance(new Object[]{
 253                 host, new Integer(port)});
 254 
 255         } catch (ClassNotFoundException |
 256                  InstantiationException |
 257                  InvocationTargetException |
 258                  IllegalAccessException e) {
 259             throw new NoSuchMethodException();
 260 
 261         }
 262     }
 263 
 264     /*
 265      * Create a Socket object using the specified socket factory and time limit.
 266      *
 267      * If a timeout is supplied and unconnected sockets are supported then
 268      * an unconnected socket is created and the timeout is applied when
 269      * connecting the socket. If a timeout is supplied but unconnected sockets
 270      * are not supported then the timeout is ignored and a connected socket
 271      * is created.
 272      */
 273     private Socket createSocket(String host, int port, String socketFactory,
 274             int connectTimeout) throws Exception {
 275 
 276         Socket socket = null;
 277 
 278         if (socketFactory != null) {
 279 
 280             // create the factory
 281 
 282             Class<?> socketFactoryClass = Obj.helper.loadClass(socketFactory);


 283             Method getDefault =
 284                 socketFactoryClass.getMethod("getDefault", new Class<?>[]{});
 285             Object factory = getDefault.invoke(null, new Object[]{});
 286 
 287             // create the socket
 288 
 289             Method createSocket = null;
 290 
 291             if (connectTimeout > 0) {
 292 
 293                 try {
 294                     createSocket = socketFactoryClass.getMethod("createSocket",
 295                         new Class<?>[]{});
 296 
 297                     Method connect = Socket.class.getMethod("connect",
 298                         new Class<?>[]{Class.forName("java.net.SocketAddress"),
 299                         int.class});
 300                     Object endpoint = createInetSocketAddress(host, port);
 301 
 302                     // unconnected socket
 303                     socket =
 304                         (Socket)createSocket.invoke(factory, new Object[]{});
 305 
 306                     if (debug) {
 307                         System.err.println("Connection: creating socket with " +
 308                             "a timeout using supplied socket factory");
 309                     }
 310 
 311                     // connected socket
 312                     connect.invoke(socket, new Object[]{
 313                         endpoint, new Integer(connectTimeout)});
 314 
 315                 } catch (NoSuchMethodException e) {
 316                     // continue (but ignore connectTimeout)
 317                 }
 318             }
 319 

 320             if (socket == null) {
 321                 createSocket = socketFactoryClass.getMethod("createSocket",
 322                     new Class<?>[]{String.class, int.class});
 323 
 324                 if (debug) {
 325                     System.err.println("Connection: creating socket using " +
 326                         "supplied socket factory");
 327                 }
 328                 // connected socket
 329                 socket = (Socket) createSocket.invoke(factory,
 330                     new Object[]{host, new Integer(port)});
 331             }
 332         } else {
 333 
 334             if (connectTimeout > 0) {
 335 
 336                 try {
 337                     Constructor<Socket> socketCons =
 338                         Socket.class.getConstructor(new Class<?>[]{});
 339 
 340                     Method connect = Socket.class.getMethod("connect",
 341                         new Class<?>[]{Class.forName("java.net.SocketAddress"),
 342                         int.class});
 343                     Object endpoint = createInetSocketAddress(host, port);
 344 
 345                     socket = socketCons.newInstance(new Object[]{});
 346 
 347                     if (debug) {
 348                         System.err.println("Connection: creating socket with " +
 349                             "a timeout");
 350                     }
 351                     connect.invoke(socket, new Object[]{
 352                         endpoint, new Integer(connectTimeout)});
 353 
 354                 } catch (NoSuchMethodException e) {
 355                     // continue (but ignore connectTimeout)
 356                 }
 357             }
 358 
 359             if (socket == null) {
 360                 if (debug) {
 361                     System.err.println("Connection: creating socket");
 362                 }
 363                 // connected socket
 364                 socket = new Socket(host, port);
 365             }
 366         }
 367 
 368         // For LDAP connect timeouts on LDAP over SSL connections must treat
 369         // the SSL handshake following socket connection as part of the timeout.
 370         // So explicitly set a socket read timeout, trigger the SSL handshake,
 371         // then reset the timeout.
 372         if (connectTimeout > 0 && socket instanceof SSLSocket) {
 373             SSLSocket sslSocket = (SSLSocket) socket;
 374             int socketTimeout = sslSocket.getSoTimeout();
 375 
 376             sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
 377             sslSocket.startHandshake();




  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.ldap;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.BufferedOutputStream;
  30 import java.io.InterruptedIOException;
  31 import java.io.IOException;
  32 import java.io.OutputStream;
  33 import java.io.InputStream;
  34 import java.net.InetSocketAddress;
  35 import java.net.Socket;
  36 import javax.net.ssl.SSLSocket;
  37 
  38 import javax.naming.CommunicationException;
  39 import javax.naming.ServiceUnavailableException;
  40 import javax.naming.NamingException;
  41 import javax.naming.InterruptedNamingException;
  42 
  43 import javax.naming.ldap.Control;
  44 
  45 import java.lang.reflect.Method;

  46 import java.lang.reflect.InvocationTargetException;
  47 import java.util.Arrays;
  48 import sun.misc.IOUtils;
  49 import javax.net.SocketFactory;
  50 
  51 /**
  52   * A thread that creates a connection to an LDAP server.
  53   * After the connection, the thread reads from the connection.
  54   * A caller can invoke methods on the instance to read LDAP responses
  55   * and to send LDAP requests.
  56   * <p>
  57   * There is a one-to-one correspondence between an LdapClient and
  58   * a Connection. Access to Connection and its methods is only via
  59   * LdapClient with two exceptions: SASL authentication and StartTLS.
  60   * SASL needs to access Connection's socket IO streams (in order to do encryption
  61   * of the security layer). StartTLS needs to do replace IO streams
  62   * and close the IO  streams on nonfatal close. The code for SASL
  63   * authentication can be treated as being the same as from LdapClient
  64   * because the SASL code is only ever called from LdapClient, from
  65   * inside LdapClient's synchronized authenticate() method. StartTLS is called
  66   * directly by the application but should only occur when the underlying
  67   * connection is quiet.
  68   * <p>
  69   * In terms of synchronization, worry about data structures


 202         //
 203         try {
 204             sock = createSocket(host, port, socketFactory, connectTimeout);
 205 
 206             if (debug) {
 207                 System.err.println("Connection: opening socket: " + host + "," + port);
 208             }
 209 
 210             inStream = new BufferedInputStream(sock.getInputStream());
 211             outStream = new BufferedOutputStream(sock.getOutputStream());
 212 
 213         } catch (InvocationTargetException e) {
 214             Throwable realException = e.getTargetException();
 215             // realException.printStackTrace();
 216 
 217             CommunicationException ce =
 218                 new CommunicationException(host + ":" + port);
 219             ce.setRootCause(realException);
 220             throw ce;
 221         } catch (Exception e) {
 222             // We need to have a catch all here and


 223             // ignore generic exceptions.
 224             // Also catches all IO errors generated by socket creation.
 225             CommunicationException ce =
 226                 new CommunicationException(host + ":" + port);
 227             ce.setRootCause(e);
 228             throw ce;
 229         }
 230 
 231         worker = Obj.helper.createThread(this);
 232         worker.setDaemon(true);
 233         worker.start();
 234     }
 235 
 236     /*
 237      * Create an InetSocketAddress using the specified hostname and port number.
 238      */
 239     private InetSocketAddress createInetSocketAddress(String host, int port) {
 240             return new InetSocketAddress(host, port);



















 241     }
 242 
 243     /*
 244      * Create a Socket object using the specified socket factory and time limit.
 245      *
 246      * If a timeout is supplied and unconnected sockets are supported then
 247      * an unconnected socket is created and the timeout is applied when
 248      * connecting the socket. If a timeout is supplied but unconnected sockets
 249      * are not supported then the timeout is ignored and a connected socket
 250      * is created.
 251      */
 252     private Socket createSocket(String host, int port, String socketFactory,
 253             int connectTimeout) throws Exception {
 254 
 255         Socket socket = null;
 256 
 257         if (socketFactory != null) {
 258 
 259             // create the factory
 260 
 261             Class<? extends SocketFactory> socketFactoryClass =
 262                     (Class<? extends SocketFactory>) Obj.helper.loadClass
 263                     (socketFactory);
 264             Method getDefault =
 265                 socketFactoryClass.getMethod("getDefault", new Class<?>[]{});
 266             SocketFactory factory = (SocketFactory) getDefault.invoke(null, new Object[]{});
 267 
 268             // create the socket
 269 


 270             if (connectTimeout > 0) {
 271 
 272                 InetSocketAddress endpoint =
 273                         createInetSocketAddress(host, port);






 274 
 275                 // unconnected socket
 276                 socket = factory.createSocket();

 277 
 278                 if (debug) {
 279                     System.err.println("Connection: creating socket with " +
 280                             "a timeout using supplied socket factory");
 281                 }
 282 
 283                 // connected socket
 284                 socket.connect(endpoint, connectTimeout);





 285             }
 286 
 287             // continue (but ignore connectTimeout)
 288             if (socket == null) {



 289                 if (debug) {
 290                     System.err.println("Connection: creating socket using " +
 291                         "supplied socket factory");
 292                 }
 293                 // connected socket
 294                 socket = factory.createSocket(host, port);

 295             }
 296         } else {
 297 
 298             if (connectTimeout > 0) {
 299 
 300                     InetSocketAddress endpoint = createInetSocketAddress(host, port);







 301 
 302                     socket = new Socket();
 303 
 304                     if (debug) {
 305                         System.err.println("Connection: creating socket with " +
 306                             "a timeout");
 307                     }
 308                     socket.connect(endpoint, connectTimeout);
 309             }
 310 

 311             // continue (but ignore connectTimeout)


 312 
 313             if (socket == null) {
 314                 if (debug) {
 315                     System.err.println("Connection: creating socket");
 316                 }
 317                 // connected socket
 318                 socket = new Socket(host, port);
 319             }
 320         }
 321 
 322         // For LDAP connect timeouts on LDAP over SSL connections must treat
 323         // the SSL handshake following socket connection as part of the timeout.
 324         // So explicitly set a socket read timeout, trigger the SSL handshake,
 325         // then reset the timeout.
 326         if (connectTimeout > 0 && socket instanceof SSLSocket) {
 327             SSLSocket sslSocket = (SSLSocket) socket;
 328             int socketTimeout = sslSocket.getSoTimeout();
 329 
 330             sslSocket.setSoTimeout(connectTimeout); // reuse full timeout value
 331             sslSocket.startHandshake();