/* * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap; import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.InterruptedIOException;import java.io.IOException;import java.io.OutputStream;import java.io.InputStream;import java.net.Socket; import javax.naming.CommunicationException;import javax.naming.ServiceUnavailableException;import javax.naming.NamingException;import javax.naming.InterruptedNamingException; import javax.naming.ldap.Control; import java.lang.reflect.Method;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.util.Arrays;import sun.misc.IOUtils;//import javax.net.SocketFactory; /** * A thread that creates a connection to an LDAP server. * After the connection, the thread reads from the connection. * A caller can invoke methods on the instance to read LDAP responses * and to send LDAP requests. * <p> * There is a one-to-one correspondence between an LdapClient and * a Connection. Access to Connection and its methods is only via * LdapClient with two exceptions: SASL authentication and StartTLS. * SASL needs to access Connection's socket IO streams (in order to do encryption * of the security layer). StartTLS needs to do replace IO streams * and close the IO streams on nonfatal close. The code for SASL * authentication can be treated as being the same as from LdapClient * because the SASL code is only ever called from LdapClient, from * inside LdapClient's synchronized authenticate() method. StartTLS is called * directly by the application but should only occur when the underlying * connection is quiet. * <p> * In terms of synchronization, worry about data structures * used by the Connection thread because that usage might contend * with calls by the main threads (i.e., those that call LdapClient). * Main threads need to worry about contention with each other. * Fields that Connection thread uses: * inStream - synced access and update; initialized in constructor; * referenced outside class unsync'ed (by LdapSasl) only * when connection is quiet * traceFile, traceTagIn, traceTagOut - no sync; debugging only * parent - no sync; initialized in constructor; no updates * pendingRequests - sync * pauseLock - per-instance lock; * paused - sync via pauseLock (pauseReader()) * Members used by main threads (LdapClient): * host, port - unsync; read-only access for StartTLS and debug messages * setBound(), setV3() - no sync; called only by LdapClient.authenticate(), * which is a sync method called only when connection is "quiet" * getMsgId() - sync * writeRequest(), removeRequest(),findRequest(), abandonOutstandingReqs() - * access to shared pendingRequests is sync * writeRequest(), abandonRequest(), ldapUnbind() - access to outStream sync * cleanup() - sync * readReply() - access to sock sync * unpauseReader() - (indirectly via writeRequest) sync on pauseLock * Members used by SASL auth (main thread): * inStream, outStream - no sync; used to construct new stream; accessed * only when conn is "quiet" and not shared * replaceStreams() - sync method * Members used by StartTLS: * inStream, outStream - no sync; used to record the existing streams; * accessed only when conn is "quiet" and not shared * replaceStreams() - sync method * <p> * Handles anonymous, simple, and SASL bind for v3; anonymous and simple * for v2. * %%% made public for access by LdapSasl %%% * * @author Vincent Ryan * @author Rosanna Lee * @author Jagane Sundar */public final class Connection implements Runnable { private static final boolean debug = false; private static final int dump = 0; // > 0 r, > 1 rw final private Thread worker; // Initialized in constructor private boolean v3 = true; // Set in setV3() final public String host; // used by LdapClient for generating exception messages // used by StartTlsResponse when creating an SSL socket final public int port; // used by LdapClient for generating exception messages // used by StartTlsResponse when creating an SSL socket private boolean bound = false; // Set in setBound() // All three are initialized in constructor and read-only afterwards private OutputStream traceFile = null; private String traceTagIn = null; private String traceTagOut = null; // Initialized in constructor; read and used externally (LdapSasl); // Updated in replaceStreams() during "quiet", unshared, period public InputStream inStream; // must be public; used by LdapSasl // Initialized in constructor; read and used externally (LdapSasl); // Updated in replaceOutputStream() during "quiet", unshared, period public OutputStream outStream; // must be public; used by LdapSasl // Initialized in constructor; read and used externally (TLS) to // get new IO streams; closed during cleanup public Socket sock; // for TLS // For processing "disconnect" unsolicited notification // Initialized in constructor final private LdapClient parent; // Incremented and returned in sync getMsgId() private int outMsgId = 0; // // The list of ldapRequests pending on this binding // // Accessed only within sync methods private LdapRequest pendingRequests = null; volatile IOException closureReason = null; volatile boolean useable = true; // is Connection still useable private int readTimeout; // true means v3; false means v2 // Called in LdapClient.authenticate() (which is synchronized) // when connection is "quiet" and not shared; no need to synchronize void setV3(boolean v) { v3 = v; } // A BIND request has been successfully made on this connection // When cleaning up, remember to do an UNBIND // Called in LdapClient.authenticate() (which is synchronized) // when connection is "quiet" and not shared; no need to synchronize void setBound() { bound = true; } //////////////////////////////////////////////////////////////////////////// // // Create an LDAP Binding object and bind to a particular server // //////////////////////////////////////////////////////////////////////////// Connection(LdapClient parent, String host, int port, String socketFactory, int connectTimeout, int readTimeout, OutputStream trace) throws NamingException { this.host = host; this.port = port; this.parent = parent; this.readTimeout = readTimeout; if (trace != null) { traceFile = trace; traceTagIn = "<- " + host + ":" + port + "\n\n"; traceTagOut = "-> " + host + ":" + port + "\n\n"; } // // Connect to server // try { sock = createSocket(host, port, socketFactory, connectTimeout); if (debug) { System.err.println("Connection: opening socket: " + host + "," + port); } inStream = new BufferedInputStream(sock.getInputStream()); outStream = new BufferedOutputStream(sock.getOutputStream()); } catch (InvocationTargetException e) { Throwable realException = e.getTargetException(); // realException.printStackTrace(); CommunicationException ce = new CommunicationException(host + ":" + port); ce.setRootCause(realException); throw ce; } catch (Exception e) { // Class.forName() seems to do more error checking // and will throw IllegalArgumentException and such. // That's why we need to have a catch all here and // ignore generic exceptions. // Also catches all IO errors generated by socket creation. CommunicationException ce = new CommunicationException(host + ":" + port); ce.setRootCause(e); throw ce; } worker = Obj.helper.createThread(this); worker.setDaemon(true); worker.start(); } /* * Create an InetSocketAddress using the specified hostname and port number. */ private Object createInetSocketAddress(String host, int port) throws NoSuchMethodException { try { Class inetSocketAddressClass = Class.forName("java.net.InetSocketAddress"); Constructor inetSocketAddressCons = inetSocketAddressClass.getConstructor(new Class[]{ String.class, int.class}); return inetSocketAddressCons.newInstance(new Object[]{ host, new Integer(port)}); } catch (ClassNotFoundException e) { throw new NoSuchMethodException(); } catch (InstantiationException e) { throw new NoSuchMethodException(); } catch (InvocationTargetException e) { throw new NoSuchMethodException(); } catch (IllegalAccessException e) { throw new NoSuchMethodException(); } } /* * Create a Socket object using the specified socket factory and time limit. * * If a timeout is supplied and unconnected sockets are supported then * an unconnected socket is created and the timeout is applied when * connecting the socket. If a timeout is supplied but unconnected sockets * are not supported then the timeout is ignored and a connected socket * is created. */ private Socket createSocket(String host, int port, String socketFactory, int connectTimeout) throws Exception { Socket socket = null; if (socketFactory != null) { // create the factory Class socketFactoryClass = Obj.helper.loadClass(socketFactory); Method getDefault = socketFactoryClass.getMethod("getDefault", new Class[]{}); Object factory = getDefault.invoke(null, new Object[]{}); // create the socket Method createSocket = null; if (connectTimeout > 0) { try { createSocket = socketFactoryClass.getMethod("createSocket", new Class[]{}); Method connect = Socket.class.getMethod("connect", new Class[]{Class.forName("java.net.SocketAddress"), int.class}); Object endpoint = createInetSocketAddress(host, port); // unconnected socket socket = (Socket)createSocket.invoke(factory, new Object[]{}); if (debug) { System.err.println("Connection: creating socket with " + "a timeout using supplied socket factory"); } // connected socket connect.invoke(socket, new Object[]{ endpoint, new Integer(connectTimeout)}); } catch (NoSuchMethodException e) { // continue (but ignore connectTimeout) } } if (socket == null) { createSocket = socketFactoryClass.getMethod("createSocket", new Class[]{String.class, int.class}); if (debug) { System.err.println("Connection: creating socket using " + "supplied socket factory"); } // connected socket socket = (Socket) createSocket.invoke(factory, new Object[]{host, new Integer(port)}); } } else { if (connectTimeout > 0) { try { Constructor socketCons = Socket.class.getConstructor(new Class[]{}); Method connect = Socket.class.getMethod("connect", new Class[]{Class.forName("java.net.SocketAddress"), int.class}); Object endpoint = createInetSocketAddress(host, port); socket = (Socket) socketCons.newInstance(new Object[]{}); if (debug) { System.err.println("Connection: creating socket with " + "a timeout"); } connect.invoke(socket, new Object[]{ endpoint, new Integer(connectTimeout)}); } catch (NoSuchMethodException e) { // continue (but ignore connectTimeout) } } if (socket == null) { if (debug) { System.err.println("Connection: creating socket"); } // connected socket socket = new Socket(host, port); } } return socket; } //////////////////////////////////////////////////////////////////////////// // // Methods to IO to the LDAP server // //////////////////////////////////////////////////////////////////////////// synchronized int getMsgId() { return ++outMsgId; } LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException { return writeRequest(ber, msgId, false /* pauseAfterReceipt */, -1); } LdapRequest writeRequest(BerEncoder ber, int msgId, boolean pauseAfterReceipt) throws IOException { return writeRequest(ber, msgId, pauseAfterReceipt, -1); } LdapRequest writeRequest(BerEncoder ber, int msgId, boolean pauseAfterReceipt, int replyQueueCapacity) throws IOException { LdapRequest req = new LdapRequest(msgId, pauseAfterReceipt, replyQueueCapacity); addRequest(req); if (traceFile != null) { Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0, ber.getDataLen()); } // unpause reader so that it can get response // NOTE: Must do this before writing request, otherwise might // create a race condition where the writer unblocks its own response unpauseReader(); if (debug) { System.err.println("Writing request to: " + outStream); } try { synchronized (this) { outStream.write(ber.getBuf(), 0, ber.getDataLen()); outStream.flush(); } } catch (IOException e) { cleanup(null, true); throw (closureReason = e); // rethrow } return req; } /** * Reads a reply; waits until one is ready. */ BerDecoder readReply(LdapRequest ldr) throws IOException, NamingException { BerDecoder rber; boolean waited = false; while (((rber = ldr.getReplyBer()) == null) && !waited) { try { // If socket closed, don't even try synchronized (this) { if (sock == null) { throw new ServiceUnavailableException(host + ":" + port + "; socket closed"); } } synchronized (ldr) { // check if condition has changed since our last check rber = ldr.getReplyBer(); if (rber == null) { if (readTimeout > 0) { // Socket read timeout is specified // will be woken up before readTimeout only if reply is // available ldr.wait(readTimeout); waited = true; } else { ldr.wait(15 * 1000); // 15 second timeout } } else { break; } } } catch (InterruptedException ex) { throw new InterruptedNamingException( "Interrupted during LDAP operation"); } } if ((rber == null) && waited) { removeRequest(ldr); throw new NamingException("LDAP response read timed out, timeout used:" + readTimeout + "ms." ); } return rber; } //////////////////////////////////////////////////////////////////////////// // // Methods to add, find, delete, and abandon requests made to server // //////////////////////////////////////////////////////////////////////////// private synchronized void addRequest(LdapRequest ldapRequest) { LdapRequest ldr = pendingRequests; if (ldr == null) { pendingRequests = ldapRequest; ldapRequest.next = null; } else { ldapRequest.next = pendingRequests; pendingRequests = ldapRequest; } } synchronized LdapRequest findRequest(int msgId) { LdapRequest ldr = pendingRequests; while (ldr != null) { if (ldr.msgId == msgId) { return ldr; } ldr = ldr.next;Lines 1–500 of 1013. Load more
/* * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.InterruptedIOException; import java.io.IOException; import java.io.OutputStream; import java.io.InputStream; import java.net.Socket; import javax.naming.CommunicationException; import javax.naming.ServiceUnavailableException; import javax.naming.NamingException; import javax.naming.InterruptedNamingException; import javax.naming.ldap.Control; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import sun.misc.IOUtils; //import javax.net.SocketFactory; /** * A thread that creates a connection to an LDAP server. * After the connection, the thread reads from the connection. * A caller can invoke methods on the instance to read LDAP responses * and to send LDAP requests. * <p> * There is a one-to-one correspondence between an LdapClient and * a Connection. Access to Connection and its methods is only via * LdapClient with two exceptions: SASL authentication and StartTLS. * SASL needs to access Connection's socket IO streams (in order to do encryption * of the security layer). StartTLS needs to do replace IO streams * and close the IO streams on nonfatal close. The code for SASL * authentication can be treated as being the same as from LdapClient * because the SASL code is only ever called from LdapClient, from * inside LdapClient's synchronized authenticate() method. StartTLS is called * directly by the application but should only occur when the underlying * connection is quiet. * <p> * In terms of synchronization, worry about data structures * used by the Connection thread because that usage might contend * with calls by the main threads (i.e., those that call LdapClient). * Main threads need to worry about contention with each other. * Fields that Connection thread uses: * inStream - synced access and update; initialized in constructor; * referenced outside class unsync'ed (by LdapSasl) only * when connection is quiet * traceFile, traceTagIn, traceTagOut - no sync; debugging only * parent - no sync; initialized in constructor; no updates * pendingRequests - sync * pauseLock - per-instance lock; * paused - sync via pauseLock (pauseReader()) * Members used by main threads (LdapClient): * host, port - unsync; read-only access for StartTLS and debug messages * setBound(), setV3() - no sync; called only by LdapClient.authenticate(), * which is a sync method called only when connection is "quiet" * getMsgId() - sync * writeRequest(), removeRequest(),findRequest(), abandonOutstandingReqs() - * access to shared pendingRequests is sync * writeRequest(), abandonRequest(), ldapUnbind() - access to outStream sync * cleanup() - sync * readReply() - access to sock sync * unpauseReader() - (indirectly via writeRequest) sync on pauseLock * Members used by SASL auth (main thread): * inStream, outStream - no sync; used to construct new stream; accessed * only when conn is "quiet" and not shared * replaceStreams() - sync method * Members used by StartTLS: * inStream, outStream - no sync; used to record the existing streams; * accessed only when conn is "quiet" and not shared * replaceStreams() - sync method * <p> * Handles anonymous, simple, and SASL bind for v3; anonymous and simple * for v2. * %%% made public for access by LdapSasl %%% * * @author Vincent Ryan * @author Rosanna Lee * @author Jagane Sundar */ public final class Connection implements Runnable { private static final boolean debug = false; private static final int dump = 0; // > 0 r, > 1 rw final private Thread worker; // Initialized in constructor private boolean v3 = true; // Set in setV3() final public String host; // used by LdapClient for generating exception messages // used by StartTlsResponse when creating an SSL socket final public int port; // used by LdapClient for generating exception messages // used by StartTlsResponse when creating an SSL socket private boolean bound = false; // Set in setBound() // All three are initialized in constructor and read-only afterwards private OutputStream traceFile = null; private String traceTagIn = null; private String traceTagOut = null; // Initialized in constructor; read and used externally (LdapSasl); // Updated in replaceStreams() during "quiet", unshared, period public InputStream inStream; // must be public; used by LdapSasl // Initialized in constructor; read and used externally (LdapSasl); // Updated in replaceOutputStream() during "quiet", unshared, period public OutputStream outStream; // must be public; used by LdapSasl // Initialized in constructor; read and used externally (TLS) to // get new IO streams; closed during cleanup public Socket sock; // for TLS // For processing "disconnect" unsolicited notification // Initialized in constructor final private LdapClient parent; // Incremented and returned in sync getMsgId() private int outMsgId = 0; // // The list of ldapRequests pending on this binding // // Accessed only within sync methods private LdapRequest pendingRequests = null; volatile IOException closureReason = null; volatile boolean useable = true; // is Connection still useable private int readTimeout; // true means v3; false means v2 // Called in LdapClient.authenticate() (which is synchronized) // when connection is "quiet" and not shared; no need to synchronize void setV3(boolean v) { v3 = v; } // A BIND request has been successfully made on this connection // When cleaning up, remember to do an UNBIND // Called in LdapClient.authenticate() (which is synchronized) // when connection is "quiet" and not shared; no need to synchronize void setBound() { bound = true; } //////////////////////////////////////////////////////////////////////////// // // Create an LDAP Binding object and bind to a particular server // //////////////////////////////////////////////////////////////////////////// Connection(LdapClient parent, String host, int port, String socketFactory, int connectTimeout, int readTimeout, OutputStream trace) throws NamingException { this.host = host; this.port = port; this.parent = parent; this.readTimeout = readTimeout; if (trace != null) { traceFile = trace; traceTagIn = "<- " + host + ":" + port + "\n\n"; traceTagOut = "-> " + host + ":" + port + "\n\n"; } // // Connect to server // try { sock = createSocket(host, port, socketFactory, connectTimeout); if (debug) { System.err.println("Connection: opening socket: " + host + "," + port); } inStream = new BufferedInputStream(sock.getInputStream()); outStream = new BufferedOutputStream(sock.getOutputStream()); } catch (InvocationTargetException e) { Throwable realException = e.getTargetException(); // realException.printStackTrace(); CommunicationException ce = new CommunicationException(host + ":" + port); ce.setRootCause(realException); throw ce; } catch (Exception e) { // Class.forName() seems to do more error checking // and will throw IllegalArgumentException and such. // That's why we need to have a catch all here and // ignore generic exceptions. // Also catches all IO errors generated by socket creation. CommunicationException ce = new CommunicationException(host + ":" + port); ce.setRootCause(e); throw ce; } worker = Obj.helper.createThread(this); worker.setDaemon(true); worker.start(); } /* * Create an InetSocketAddress using the specified hostname and port number. */ private Object createInetSocketAddress(String host, int port) throws NoSuchMethodException { try { Class inetSocketAddressClass = Class.forName("java.net.InetSocketAddress"); Constructor inetSocketAddressCons = inetSocketAddressClass.getConstructor(new Class[]{ String.class, int.class}); return inetSocketAddressCons.newInstance(new Object[]{ host, new Integer(port)}); } catch (ClassNotFoundException e) { throw new NoSuchMethodException(); } catch (InstantiationException e) { throw new NoSuchMethodException(); } catch (InvocationTargetException e) { throw new NoSuchMethodException(); } catch (IllegalAccessException e) { throw new NoSuchMethodException(); } } /* * Create a Socket object using the specified socket factory and time limit. * * If a timeout is supplied and unconnected sockets are supported then * an unconnected socket is created and the timeout is applied when * connecting the socket. If a timeout is supplied but unconnected sockets * are not supported then the timeout is ignored and a connected socket * is created. */ private Socket createSocket(String host, int port, String socketFactory, int connectTimeout) throws Exception { Socket socket = null; if (socketFactory != null) { // create the factory Class socketFactoryClass = Obj.helper.loadClass(socketFactory); Method getDefault = socketFactoryClass.getMethod("getDefault", new Class[]{}); Object factory = getDefault.invoke(null, new Object[]{}); // create the socket Method createSocket = null; if (connectTimeout > 0) { try { createSocket = socketFactoryClass.getMethod("createSocket", new Class[]{}); Method connect = Socket.class.getMethod("connect", new Class[]{Class.forName("java.net.SocketAddress"), int.class}); Object endpoint = createInetSocketAddress(host, port); // unconnected socket socket = (Socket)createSocket.invoke(factory, new Object[]{}); if (debug) { System.err.println("Connection: creating socket with " + "a timeout using supplied socket factory"); } // connected socket connect.invoke(socket, new Object[]{ endpoint, new Integer(connectTimeout)}); } catch (NoSuchMethodException e) { // continue (but ignore connectTimeout) } } if (socket == null) { createSocket = socketFactoryClass.getMethod("createSocket", new Class[]{String.class, int.class}); if (debug) { System.err.println("Connection: creating socket using " + "supplied socket factory"); } // connected socket socket = (Socket) createSocket.invoke(factory, new Object[]{host, new Integer(port)}); } } else { if (connectTimeout > 0) { try { Constructor socketCons = Socket.class.getConstructor(new Class[]{}); Method connect = Socket.class.getMethod("connect", new Class[]{Class.forName("java.net.SocketAddress"), int.class}); Object endpoint = createInetSocketAddress(host, port); socket = (Socket) socketCons.newInstance(new Object[]{}); if (debug) { System.err.println("Connection: creating socket with " + "a timeout"); } connect.invoke(socket, new Object[]{ endpoint, new Integer(connectTimeout)}); } catch (NoSuchMethodException e) { // continue (but ignore connectTimeout) } } if (socket == null) { if (debug) { System.err.println("Connection: creating socket"); } // connected socket socket = new Socket(host, port); } } return socket; } //////////////////////////////////////////////////////////////////////////// // // Methods to IO to the LDAP server // //////////////////////////////////////////////////////////////////////////// synchronized int getMsgId() { return ++outMsgId; } LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException { return writeRequest(ber, msgId, false /* pauseAfterReceipt */, -1); } LdapRequest writeRequest(BerEncoder ber, int msgId, boolean pauseAfterReceipt) throws IOException { return writeRequest(ber, msgId, pauseAfterReceipt, -1); } LdapRequest writeRequest(BerEncoder ber, int msgId, boolean pauseAfterReceipt, int replyQueueCapacity) throws IOException { LdapRequest req = new LdapRequest(msgId, pauseAfterReceipt, replyQueueCapacity); addRequest(req); if (traceFile != null) { Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0, ber.getDataLen()); } // unpause reader so that it can get response // NOTE: Must do this before writing request, otherwise might // create a race condition where the writer unblocks its own response unpauseReader(); if (debug) { System.err.println("Writing request to: " + outStream); } try { synchronized (this) { outStream.write(ber.getBuf(), 0, ber.getDataLen()); outStream.flush(); } } catch (IOException e) { cleanup(null, true); throw (closureReason = e); // rethrow } return req; } /** * Reads a reply; waits until one is ready. */ BerDecoder readReply(LdapRequest ldr) throws IOException, NamingException { BerDecoder rber; boolean waited = false; while (((rber = ldr.getReplyBer()) == null) && !waited) { try { // If socket closed, don't even try synchronized (this) { if (sock == null) { throw new ServiceUnavailableException(host + ":" + port + "; socket closed"); } } synchronized (ldr) { // check if condition has changed since our last check rber = ldr.getReplyBer(); if (rber == null) { if (readTimeout > 0) { // Socket read timeout is specified // will be woken up before readTimeout only if reply is // available ldr.wait(readTimeout); waited = true; } else { ldr.wait(15 * 1000); // 15 second timeout } } else { break; } } } catch (InterruptedException ex) { throw new InterruptedNamingException( "Interrupted during LDAP operation"); } } if ((rber == null) && waited) { removeRequest(ldr); throw new NamingException("LDAP response read timed out, timeout used:" + readTimeout + "ms." ); } return rber; } //////////////////////////////////////////////////////////////////////////// // // Methods to add, find, delete, and abandon requests made to server // //////////////////////////////////////////////////////////////////////////// private synchronized void addRequest(LdapRequest ldapRequest) { LdapRequest ldr = pendingRequests; if (ldr == null) { pendingRequests = ldapRequest; ldapRequest.next = null; } else { ldapRequest.next = pendingRequests; pendingRequests = ldapRequest; } } synchronized LdapRequest findRequest(int msgId) { LdapRequest ldr = pendingRequests; while (ldr != null) { if (ldr.msgId == msgId) { return ldr; } ldr = ldr.next;