Save This Page
Home » openjdk-7 » com.sun.jndi » ldap » [javadoc | source]
    1   /*
    2    * Copyright 1999-2005 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any 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 java.util.Vector;
   36   import java.util.Hashtable;
   37   
   38   import javax.naming.CommunicationException;
   39   import javax.naming.AuthenticationException;
   40   import javax.naming.AuthenticationNotSupportedException;
   41   import javax.naming.ServiceUnavailableException;
   42   import javax.naming.NamingException;
   43   import javax.naming.InterruptedNamingException;
   44   
   45   import javax.naming.ldap.Control;
   46   
   47   import java.lang.reflect.Method;
   48   import java.lang.reflect.Constructor;
   49   import java.lang.reflect.InvocationTargetException;
   50   //import javax.net.SocketFactory;
   51   
   52   /**
   53     * A thread that creates a connection to an LDAP server.
   54     * After the connection, the thread reads from the connection.
   55     * A caller can invoke methods on the instance to read LDAP responses
   56     * and to send LDAP requests.
   57     * <p>
   58     * There is a one-to-one correspondence between an LdapClient and
   59     * a Connection. Access to Connection and its methods is only via
   60     * LdapClient with two exceptions: SASL authentication and StartTLS.
   61     * SASL needs to access Connection's socket IO streams (in order to do encryption
   62     * of the security layer). StartTLS needs to do replace IO streams
   63     * and close the IO  streams on nonfatal close. The code for SASL
   64     * authentication can be treated as being the same as from LdapClient
   65     * because the SASL code is only ever called from LdapClient, from
   66     * inside LdapClient's synchronized authenticate() method. StartTLS is called
   67     * directly by the application but should only occur when the underlying
   68     * connection is quiet.
   69     * <p>
   70     * In terms of synchronization, worry about data structures
   71     * used by the Connection thread because that usage might contend
   72     * with calls by the main threads (i.e., those that call LdapClient).
   73     * Main threads need to worry about contention with each other.
   74     * Fields that Connection thread uses:
   75     *     inStream - synced access and update; initialized in constructor;
   76     *           referenced outside class unsync'ed (by LdapSasl) only
   77     *           when connection is quiet
   78     *     traceFile, traceTagIn, traceTagOut - no sync; debugging only
   79     *     parent - no sync; initialized in constructor; no updates
   80     *     pendingRequests - sync
   81     *     pauseLock - per-instance lock;
   82     *     paused - sync via pauseLock (pauseReader())
   83     * Members used by main threads (LdapClient):
   84     *     host, port - unsync; read-only access for StartTLS and debug messages
   85     *     setBound(), setV3() - no sync; called only by LdapClient.authenticate(),
   86     *             which is a sync method called only when connection is "quiet"
   87     *     getMsgId() - sync
   88     *     writeRequest(), removeRequest(),findRequest(), abandonOutstandingReqs() -
   89     *             access to shared pendingRequests is sync
   90     *     writeRequest(),  abandonRequest(), ldapUnbind() - access to outStream sync
   91     *     cleanup() - sync
   92     *     readReply() - access to sock sync
   93     *     unpauseReader() - (indirectly via writeRequest) sync on pauseLock
   94     * Members used by SASL auth (main thread):
   95     *     inStream, outStream - no sync; used to construct new stream; accessed
   96     *             only when conn is "quiet" and not shared
   97     *     replaceStreams() - sync method
   98     * Members used by StartTLS:
   99     *     inStream, outStream - no sync; used to record the existing streams;
  100     *             accessed only when conn is "quiet" and not shared
  101     *     replaceStreams() - sync method
  102     * <p>
  103     * Handles anonymous, simple, and SASL bind for v3; anonymous and simple
  104     * for v2.
  105     * %%% made public for access by LdapSasl %%%
  106     *
  107     * @author Vincent Ryan
  108     * @author Rosanna Lee
  109     * @author Jagane Sundar
  110     */
  111   public final class Connection implements Runnable {
  112   
  113       private static final boolean debug = false;
  114       private static final int dump = 0; // > 0 r, > 1 rw
  115   
  116   
  117       final private Thread worker;    // Initialized in constructor
  118   
  119       private boolean v3 = true;       // Set in setV3()
  120   
  121       final public String host;  // used by LdapClient for generating exception messages
  122                            // used by StartTlsResponse when creating an SSL socket
  123       final public int port;     // used by LdapClient for generating exception messages
  124                            // used by StartTlsResponse when creating an SSL socket
  125   
  126       private boolean bound = false;   // Set in setBound()
  127   
  128       // All three are initialized in constructor and read-only afterwards
  129       private OutputStream traceFile = null;
  130       private String traceTagIn = null;
  131       private String traceTagOut = null;
  132   
  133       // Initialized in constructor; read and used externally (LdapSasl);
  134       // Updated in replaceStreams() during "quiet", unshared, period
  135       public InputStream inStream;   // must be public; used by LdapSasl
  136   
  137       // Initialized in constructor; read and used externally (LdapSasl);
  138       // Updated in replaceOutputStream() during "quiet", unshared, period
  139       public OutputStream outStream; // must be public; used by LdapSasl
  140   
  141       // Initialized in constructor; read and used externally (TLS) to
  142       // get new IO streams; closed during cleanup
  143       public Socket sock;            // for TLS
  144   
  145       // For processing "disconnect" unsolicited notification
  146       // Initialized in constructor
  147       final private LdapClient parent;
  148   
  149       // Incremented and returned in sync getMsgId()
  150       private int outMsgId = 0;
  151   
  152       //
  153       // The list of ldapRequests pending on this binding
  154       //
  155       // Accessed only within sync methods
  156       private LdapRequest pendingRequests = null;
  157   
  158       volatile IOException closureReason = null;
  159       volatile boolean useable = true;  // is Connection still useable
  160   
  161       private int readTimeout;
  162   
  163       // true means v3; false means v2
  164       // Called in LdapClient.authenticate() (which is synchronized)
  165       // when connection is "quiet" and not shared; no need to synchronize
  166       void setV3(boolean v) {
  167           v3 = v;
  168       }
  169   
  170       // A BIND request has been successfully made on this connection
  171       // When cleaning up, remember to do an UNBIND
  172       // Called in LdapClient.authenticate() (which is synchronized)
  173       // when connection is "quiet" and not shared; no need to synchronize
  174       void setBound() {
  175           bound = true;
  176       }
  177   
  178       ////////////////////////////////////////////////////////////////////////////
  179       //
  180       // Create an LDAP Binding object and bind to a particular server
  181       //
  182       ////////////////////////////////////////////////////////////////////////////
  183   
  184       Connection(LdapClient parent, String host, int port, String socketFactory,
  185           int connectTimeout, int readTimeout, OutputStream trace) throws NamingException {
  186   
  187           this.host = host;
  188           this.port = port;
  189           this.parent = parent;
  190           this.readTimeout = readTimeout;
  191   
  192           if (trace != null) {
  193               traceFile = trace;
  194               traceTagIn = "<- " + host + ":" + port + "\n\n";
  195               traceTagOut = "-> " + host + ":" + port + "\n\n";
  196           }
  197   
  198           //
  199           // Connect to server
  200           //
  201           try {
  202               sock = createSocket(host, port, socketFactory, connectTimeout);
  203   
  204               if (debug) {
  205                   System.err.println("Connection: opening socket: " + host + "," + port);
  206               }
  207   
  208               inStream = new BufferedInputStream(sock.getInputStream());
  209               outStream = new BufferedOutputStream(sock.getOutputStream());
  210   
  211           } catch (InvocationTargetException e) {
  212               Throwable realException = e.getTargetException();
  213               // realException.printStackTrace();
  214   
  215               CommunicationException ce =
  216                   new CommunicationException(host + ":" + port);
  217               ce.setRootCause(realException);
  218               throw ce;
  219           } catch (Exception e) {
  220               // Class.forName() seems to do more error checking
  221               // and will throw IllegalArgumentException and such.
  222               // That's why 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 Object createInetSocketAddress(String host, int port)
  240               throws NoSuchMethodException {
  241   
  242           try {
  243               Class inetSocketAddressClass =
  244                   Class.forName("java.net.InetSocketAddress");
  245   
  246               Constructor inetSocketAddressCons =
  247                   inetSocketAddressClass.getConstructor(new Class[]{
  248                   String.class, int.class});
  249   
  250               return inetSocketAddressCons.newInstance(new Object[]{
  251                   host, new Integer(port)});
  252   
  253           } catch (ClassNotFoundException e) {
  254               throw new NoSuchMethodException();
  255   
  256           } catch (InstantiationException e) {
  257               throw new NoSuchMethodException();
  258   
  259           } catch (InvocationTargetException e) {
  260               throw new NoSuchMethodException();
  261   
  262           } catch (IllegalAccessException e) {
  263               throw new NoSuchMethodException();
  264           }
  265       }
  266   
  267       /*
  268        * Create a Socket object using the specified socket factory and time limit.
  269        *
  270        * If a timeout is supplied and unconnected sockets are supported then
  271        * an unconnected socket is created and the timeout is applied when
  272        * connecting the socket. If a timeout is supplied but unconnected sockets
  273        * are not supported then the timeout is ignored and a connected socket
  274        * is created.
  275        */
  276       private Socket createSocket(String host, int port, String socketFactory,
  277               int connectTimeout) throws Exception {
  278   
  279           Socket socket = null;
  280   
  281           if (socketFactory != null) {
  282   
  283               // create the factory
  284   
  285               Class socketFactoryClass = Obj.helper.loadClass(socketFactory);
  286               Method getDefault =
  287                   socketFactoryClass.getMethod("getDefault", new Class[]{});
  288               Object factory = getDefault.invoke(null, new Object[]{});
  289   
  290               // create the socket
  291   
  292               Method createSocket = null;
  293   
  294               if (connectTimeout > 0) {
  295   
  296                   try {
  297                       createSocket = socketFactoryClass.getMethod("createSocket",
  298                           new Class[]{});
  299   
  300                       Method connect = Socket.class.getMethod("connect",
  301                           new Class[]{Class.forName("java.net.SocketAddress"),
  302                           int.class});
  303                       Object endpoint = createInetSocketAddress(host, port);
  304   
  305                       // unconnected socket
  306                       socket =
  307                           (Socket)createSocket.invoke(factory, new Object[]{});
  308   
  309                       if (debug) {
  310                           System.err.println("Connection: creating socket with " +
  311                               "a timeout using supplied socket factory");
  312                       }
  313   
  314                       // connected socket
  315                       connect.invoke(socket, new Object[]{
  316                           endpoint, new Integer(connectTimeout)});
  317   
  318                   } catch (NoSuchMethodException e) {
  319                       // continue (but ignore connectTimeout)
  320                   }
  321               }
  322   
  323               if (socket == null) {
  324                   createSocket = socketFactoryClass.getMethod("createSocket",
  325                       new Class[]{String.class, int.class});
  326   
  327                   if (debug) {
  328                       System.err.println("Connection: creating socket using " +
  329                           "supplied socket factory");
  330                   }
  331                   // connected socket
  332                   socket = (Socket) createSocket.invoke(factory,
  333                       new Object[]{host, new Integer(port)});
  334               }
  335           } else {
  336   
  337               if (connectTimeout > 0) {
  338   
  339                   try {
  340                       Constructor socketCons =
  341                           Socket.class.getConstructor(new Class[]{});
  342   
  343                       Method connect = Socket.class.getMethod("connect",
  344                           new Class[]{Class.forName("java.net.SocketAddress"),
  345                           int.class});
  346                       Object endpoint = createInetSocketAddress(host, port);
  347   
  348                       socket = (Socket) socketCons.newInstance(new Object[]{});
  349   
  350                       if (debug) {
  351                           System.err.println("Connection: creating socket with " +
  352                               "a timeout");
  353                       }
  354                       connect.invoke(socket, new Object[]{
  355                           endpoint, new Integer(connectTimeout)});
  356   
  357                   } catch (NoSuchMethodException e) {
  358                       // continue (but ignore connectTimeout)
  359                   }
  360               }
  361   
  362               if (socket == null) {
  363                   if (debug) {
  364                       System.err.println("Connection: creating socket");
  365                   }
  366                   // connected socket
  367                   socket = new Socket(host, port);
  368               }
  369           }
  370   
  371           return socket;
  372       }
  373   
  374       ////////////////////////////////////////////////////////////////////////////
  375       //
  376       // Methods to IO to the LDAP server
  377       //
  378       ////////////////////////////////////////////////////////////////////////////
  379   
  380       synchronized int getMsgId() {
  381           return ++outMsgId;
  382       }
  383   
  384       LdapRequest writeRequest(BerEncoder ber, int msgId) throws IOException {
  385           return writeRequest(ber, msgId, false /* pauseAfterReceipt */);
  386       }
  387   
  388       LdapRequest writeRequest(BerEncoder ber, int msgId, boolean pauseAfterReceipt)
  389           throws IOException {
  390   
  391           LdapRequest req = new LdapRequest(msgId, pauseAfterReceipt);
  392           addRequest(req);
  393   
  394           if (traceFile != null) {
  395               Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0, ber.getDataLen());
  396           }
  397   
  398   
  399           // unpause reader so that it can get response
  400           // NOTE: Must do this before writing request, otherwise might
  401           // create a race condition where the writer unblocks its own response
  402           unpauseReader();
  403   
  404           if (debug) {
  405               System.err.println("Writing request to: " + outStream);
  406           }
  407   
  408           try {
  409               synchronized (this) {
  410                   outStream.write(ber.getBuf(), 0, ber.getDataLen());
  411                   outStream.flush();
  412               }
  413           } catch (IOException e) {
  414               cleanup(null, true);
  415               throw (closureReason = e); // rethrow
  416           }
  417   
  418           return req;
  419       }
  420   
  421       /**
  422        * Reads a reply; waits until one is ready.
  423        */
  424       BerDecoder readReply(LdapRequest ldr)
  425               throws IOException, NamingException {
  426           BerDecoder rber;
  427           boolean waited = false;
  428   
  429           while (((rber = ldr.getReplyBer()) == null) && !waited) {
  430               try {
  431                   // If socket closed, don't even try
  432                   synchronized (this) {
  433                       if (sock == null) {
  434                           throw new ServiceUnavailableException(host + ":" + port +
  435                               "; socket closed");
  436                       }
  437                   }
  438                   synchronized (ldr) {
  439                       // check if condition has changed since our last check
  440                       rber = ldr.getReplyBer();
  441                       if (rber == null) {
  442                           if (readTimeout > 0) {  // Socket read timeout is specified
  443   
  444                               // will be woken up before readTimeout only if reply is
  445                               // available
  446                               ldr.wait(readTimeout);
  447                               waited = true;
  448                           } else {
  449                               ldr.wait(15 * 1000); // 15 second timeout
  450                           }
  451                       } else {
  452                           break;
  453                       }
  454                   }
  455               } catch (InterruptedException ex) {
  456                   throw new InterruptedNamingException(
  457                       "Interrupted during LDAP operation");
  458               }
  459           }
  460   
  461           if ((rber == null) && waited) {
  462               removeRequest(ldr);
  463               throw new NamingException("LDAP response read timed out, timeout used:"
  464                               + readTimeout + "ms." );
  465   
  466           }
  467           return rber;
  468       }
  469   
  470   
  471       ////////////////////////////////////////////////////////////////////////////
  472       //
  473       // Methods to add, find, delete, and abandon requests made to server
  474       //
  475       ////////////////////////////////////////////////////////////////////////////
  476   
  477       private synchronized void addRequest(LdapRequest ldapRequest) {
  478   
  479           LdapRequest ldr = pendingRequests;
  480           if (ldr == null) {
  481               pendingRequests = ldapRequest;
  482               ldapRequest.next = null;
  483           } else {
  484               ldapRequest.next = pendingRequests;
  485               pendingRequests = ldapRequest;
  486           }
  487       }
  488   
  489       synchronized LdapRequest findRequest(int msgId) {
  490   
  491           LdapRequest ldr = pendingRequests;
  492           while (ldr != null) {
  493               if (ldr.msgId == msgId) {
  494                   return ldr;
  495               }
  496               ldr = ldr.next;
  497           }
  498           return null;
  499   
  500       }
  501   
  502       synchronized void removeRequest(LdapRequest req) {
  503           LdapRequest ldr = pendingRequests;
  504           LdapRequest ldrprev = null;
  505   
  506           while (ldr != null) {
  507               if (ldr == req) {
  508                   ldr.cancel();
  509   
  510                   if (ldrprev != null) {
  511                       ldrprev.next = ldr.next;
  512                   } else {
  513                       pendingRequests = ldr.next;
  514                   }
  515                   ldr.next = null;
  516               }
  517               ldrprev = ldr;
  518               ldr = ldr.next;
  519           }
  520       }
  521   
  522       void abandonRequest(LdapRequest ldr, Control[] reqCtls) {
  523           // Remove from queue
  524           removeRequest(ldr);
  525   
  526           BerEncoder ber = new BerEncoder(256);
  527           int abandonMsgId = getMsgId();
  528   
  529           //
  530           // build the abandon request.
  531           //
  532           try {
  533               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  534                   ber.encodeInt(abandonMsgId);
  535                   ber.encodeInt(ldr.msgId, LdapClient.LDAP_REQ_ABANDON);
  536   
  537                   if (v3) {
  538                       LdapClient.encodeControls(ber, reqCtls);
  539                   }
  540               ber.endSeq();
  541   
  542               if (traceFile != null) {
  543                   Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(), 0,
  544                       ber.getDataLen());
  545               }
  546   
  547               synchronized (this) {
  548                   outStream.write(ber.getBuf(), 0, ber.getDataLen());
  549                   outStream.flush();
  550               }
  551   
  552           } catch (IOException ex) {
  553               //System.err.println("ldap.abandon: " + ex);
  554           }
  555   
  556           // Dont expect any response for the abandon request.
  557       }
  558   
  559       synchronized void abandonOutstandingReqs(Control[] reqCtls) {
  560           LdapRequest ldr = pendingRequests;
  561   
  562           while (ldr != null) {
  563               abandonRequest(ldr, reqCtls);
  564               pendingRequests = ldr = ldr.next;
  565           }
  566       }
  567   
  568       ////////////////////////////////////////////////////////////////////////////
  569       //
  570       // Methods to unbind from server and clear up resources when object is
  571       // destroyed.
  572       //
  573       ////////////////////////////////////////////////////////////////////////////
  574   
  575       private void ldapUnbind(Control[] reqCtls) {
  576   
  577           BerEncoder ber = new BerEncoder(256);
  578           int unbindMsgId = getMsgId();
  579   
  580           //
  581           // build the unbind request.
  582           //
  583   
  584           try {
  585   
  586               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  587                   ber.encodeInt(unbindMsgId);
  588                   // IMPLICIT TAGS
  589                   ber.encodeByte(LdapClient.LDAP_REQ_UNBIND);
  590                   ber.encodeByte(0);
  591   
  592                   if (v3) {
  593                       LdapClient.encodeControls(ber, reqCtls);
  594                   }
  595               ber.endSeq();
  596   
  597               if (traceFile != null) {
  598                   Ber.dumpBER(traceFile, traceTagOut, ber.getBuf(),
  599                       0, ber.getDataLen());
  600               }
  601   
  602               synchronized (this) {
  603                   outStream.write(ber.getBuf(), 0, ber.getDataLen());
  604                   outStream.flush();
  605               }
  606   
  607           } catch (IOException ex) {
  608               //System.err.println("ldap.unbind: " + ex);
  609           }
  610   
  611           // Dont expect any response for the unbind request.
  612       }
  613   
  614       /**
  615        * @param reqCtls Possibly null request controls that accompanies the
  616        *    abandon and unbind LDAP request.
  617        * @param notifyParent true means to call parent LdapClient back, notifying
  618        *    it that the connection has been closed; false means not to notify
  619        *    parent. If LdapClient invokes cleanup(), notifyParent should be set to
  620        *    false because LdapClient already knows that it is closing
  621        *    the connection. If Connection invokes cleanup(), notifyParent should be
  622        *    set to true because LdapClient needs to know about the closure.
  623        */
  624       void cleanup(Control[] reqCtls, boolean notifyParent) {
  625           boolean nparent = false;
  626   
  627           synchronized (this) {
  628               useable = false;
  629   
  630               if (sock != null) {
  631                   if (debug) {
  632                       System.err.println("Connection: closing socket: " + host + "," + port);
  633                   }
  634                   try {
  635                       if (!notifyParent) {
  636                           abandonOutstandingReqs(reqCtls);
  637                       }
  638                       if (bound) {
  639                           ldapUnbind(reqCtls);
  640                       }
  641                   } finally {
  642                       try {
  643                           outStream.flush();
  644                           sock.close();
  645                           unpauseReader();
  646                       } catch (IOException ie) {
  647                           if (debug)
  648                               System.err.println("Connection: problem closing socket: " + ie);
  649                       }
  650                       if (!notifyParent) {
  651                           LdapRequest ldr = pendingRequests;
  652                           while (ldr != null) {
  653                               ldr.cancel();
  654                               ldr = ldr.next;
  655                           }
  656                       }
  657                       sock = null;
  658                   }
  659                   nparent = notifyParent;
  660               }
  661           }
  662           if (nparent) {
  663               parent.processConnectionClosure();
  664           }
  665       }
  666   
  667   
  668       // Assume everything is "quiet"
  669       // "synchronize" might lead to deadlock so don't synchronize method
  670       // Use streamLock instead for synchronizing update to stream
  671   
  672       synchronized public void replaceStreams(InputStream newIn, OutputStream newOut) {
  673           if (debug) {
  674               System.err.println("Replacing " + inStream + " with: " + newIn);
  675               System.err.println("Replacing " + outStream + " with: " + newOut);
  676           }
  677   
  678           inStream = newIn;
  679   
  680           // Cleanup old stream
  681           try {
  682               outStream.flush();
  683           } catch (IOException ie) {
  684               if (debug)
  685                   System.err.println("Connection: cannot flush outstream: " + ie);
  686           }
  687   
  688           // Replace stream
  689           outStream = newOut;
  690       }
  691   
  692       /**
  693        * Used by Connection thread to read inStream into a local variable.
  694        * This ensures that there is no contention between the main thread
  695        * and the Connection thread when the main thread updates inStream.
  696        */
  697       synchronized private InputStream getInputStream() {
  698           return inStream;
  699       }
  700   
  701   
  702       ////////////////////////////////////////////////////////////////////////////
  703       //
  704       // Code for pausing/unpausing the reader thread ('worker')
  705       //
  706       ////////////////////////////////////////////////////////////////////////////
  707   
  708       /*
  709        * The main idea is to mark requests that need the reader thread to
  710        * pause after getting the response. When the reader thread gets the response,
  711        * it waits on a lock instead of returning to the read(). The next time a
  712        * request is sent, the reader is automatically unblocked if necessary.
  713        * Note that the reader must be unblocked BEFORE the request is sent.
  714        * Otherwise, there is a race condition where the request is sent and
  715        * the reader thread might read the response and be unblocked
  716        * by writeRequest().
  717        *
  718        * This pause gives the main thread (StartTLS or SASL) an opportunity to
  719        * update the reader's state (e.g., its streams) if necessary.
  720        * The assumption is that the connection will remain quiet during this pause
  721        * (i.e., no intervening requests being sent).
  722        *<p>
  723        * For dealing with StartTLS close,
  724        * when the read() exits either due to EOF or an exception,
  725        * the reader thread checks whether there is a new stream to read from.
  726        * If so, then it reattempts the read. Otherwise, the EOF or exception
  727        * is processed and the reader thread terminates.
  728        * In a StartTLS close, the client first replaces the SSL IO streams with
  729        * plain ones and then closes the SSL socket.
  730        * If the reader thread attempts to read, or was reading, from
  731        * the SSL socket (that is, it got to the read BEFORE replaceStreams()),
  732        * the SSL socket close will cause the reader thread to
  733        * get an EOF/exception and reexamine the input stream.
  734        * If the reader thread sees a new stream, it reattempts the read.
  735        * If the underlying socket is still alive, then the new read will succeed.
  736        * If the underlying socket has been closed also, then the new read will
  737        * fail and the reader thread exits.
  738        * If the reader thread attempts to read, or was reading, from the plain
  739        * socket (that is, it got to the read AFTER replaceStreams()), the
  740        * SSL socket close will have no effect on the reader thread.
  741        *
  742        * The check for new stream is made only
  743        * in the first attempt at reading a BER buffer; the reader should
  744        * never be in midst of reading a buffer when a nonfatal close occurs.
  745        * If this occurs, then the connection is in an inconsistent state and
  746        * the safest thing to do is to shut it down.
  747        */
  748   
  749       private Object pauseLock = new Object();  // lock for reader to wait on while paused
  750       private boolean paused = false;           // paused state of reader
  751   
  752       /*
  753        * Unpauses reader thread if it was paused
  754        */
  755       private void unpauseReader() throws IOException {
  756           synchronized (pauseLock) {
  757               if (paused) {
  758                   if (debug) {
  759                       System.err.println("Unpausing reader; read from: " +
  760                                           inStream);
  761                   }
  762                   paused = false;
  763                   pauseLock.notify();
  764               }
  765           }
  766       }
  767   
  768        /*
  769        * Pauses reader so that it stops reading from the input stream.
  770        * Reader blocks on pauseLock instead of read().
  771        * MUST be called from within synchronized (pauseLock) clause.
  772        */
  773       private void pauseReader() throws IOException {
  774           if (debug) {
  775               System.err.println("Pausing reader;  was reading from: " +
  776                                   inStream);
  777           }
  778           paused = true;
  779           try {
  780               while (paused) {
  781                   pauseLock.wait(); // notified by unpauseReader
  782               }
  783           } catch (InterruptedException e) {
  784               throw new InterruptedIOException(
  785                       "Pause/unpause reader has problems.");
  786           }
  787       }
  788   
  789   
  790       ////////////////////////////////////////////////////////////////////////////
  791       //
  792       // The LDAP Binding thread. It does the mux/demux of multiple requests
  793       // on the same TCP connection.
  794       //
  795       ////////////////////////////////////////////////////////////////////////////
  796   
  797   
  798       public void run() {
  799           byte inbuf[];   // Buffer for reading incoming bytes
  800           int inMsgId;    // Message id of incoming response
  801           int bytesread;  // Number of bytes in inbuf
  802           int bytesleft;  // Number of bytes that need to read for completing resp
  803           int br;         // Temp; number of bytes read from stream
  804           int offset;     // Offset of where to store bytes in inbuf
  805           int seqlen;     // Length of ASN sequence
  806           int seqlenlen;  // Number of sequence length bytes
  807           boolean eos;    // End of stream
  808           BerDecoder retBer;    // Decoder for ASN.1 BER data from inbuf
  809           InputStream in = null;
  810   
  811           try {
  812               while (true) {
  813                   try {
  814                       inbuf = new byte[2048];
  815   
  816                       offset = 0;
  817                       seqlen = 0;
  818                       seqlenlen = 0;
  819   
  820                       in = getInputStream();
  821   
  822                       // check that it is the beginning of a sequence
  823                       bytesread = in.read(inbuf, offset, 1);
  824                       if (bytesread < 0) {
  825                           if (in != getInputStream()) {
  826                               continue;   // a new stream to try
  827                           } else {
  828                               break; // EOF
  829                           }
  830                       }
  831   
  832                       if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR))
  833                           continue;
  834   
  835                       // get length of sequence
  836                       bytesread = in.read(inbuf, offset, 1);
  837                       if (bytesread < 0)
  838                           break; // EOF
  839                       seqlen = inbuf[offset++];
  840   
  841                       // if high bit is on, length is encoded in the
  842                       // subsequent length bytes and the number of length bytes
  843                       // is equal to & 0x80 (i.e. length byte with high bit off).
  844                       if ((seqlen & 0x80) == 0x80) {
  845                           seqlenlen = seqlen & 0x7f;  // number of length bytes
  846   
  847                           bytesread = 0;
  848                           eos = false;
  849   
  850                           // Read all length bytes
  851                           while (bytesread < seqlenlen) {
  852                               br = in.read(inbuf, offset+bytesread,
  853                                   seqlenlen-bytesread);
  854                               if (br < 0) {
  855                                   eos = true;
  856                                   break; // EOF
  857                               }
  858                               bytesread += br;
  859                           }
  860   
  861                           // end-of-stream reached before length bytes are read
  862                           if (eos)
  863                               break;  // EOF
  864   
  865                           // Add contents of length bytes to determine length
  866                           seqlen = 0;
  867                           for( int i = 0; i < seqlenlen; i++) {
  868                               seqlen = (seqlen << 8) + (inbuf[offset+i] & 0xff);
  869                           }
  870                           offset += bytesread;
  871                       }
  872   
  873                       // read in seqlen bytes
  874                       bytesleft = seqlen;
  875                       if ((offset + bytesleft) > inbuf.length) {
  876                           byte nbuf[] = new byte[offset + bytesleft];
  877                           System.arraycopy(inbuf, 0, nbuf, 0, offset);
  878                           inbuf = nbuf;
  879                       }
  880                       while (bytesleft > 0) {
  881                           bytesread = in.read(inbuf, offset, bytesleft);
  882                           if (bytesread < 0)
  883                               break; // EOF
  884                           offset += bytesread;
  885                           bytesleft -= bytesread;
  886                       }
  887   /*
  888   if (dump > 0) {
  889   System.err.println("seqlen: " + seqlen);
  890   System.err.println("bufsize: " + offset);
  891   System.err.println("bytesleft: " + bytesleft);
  892   System.err.println("bytesread: " + bytesread);
  893   }
  894   */
  895   
  896   
  897                       try {
  898                           retBer = new BerDecoder(inbuf, 0, offset);
  899   
  900                           if (traceFile != null) {
  901                               Ber.dumpBER(traceFile, traceTagIn, inbuf, 0, offset);
  902                           }
  903   
  904                           retBer.parseSeq(null);
  905                           inMsgId = retBer.parseInt();
  906                           retBer.reset(); // reset offset
  907   
  908                           boolean needPause = false;
  909   
  910                           if (inMsgId == 0) {
  911                               // Unsolicited Notification
  912                               parent.processUnsolicited(retBer);
  913                           } else {
  914                               LdapRequest ldr = findRequest(inMsgId);
  915   
  916                               if (ldr != null) {
  917   
  918                                   /**
  919                                    * Grab pauseLock before making reply available
  920                                    * to ensure that reader goes into paused state
  921                                    * before writer can attempt to unpause reader
  922                                    */
  923                                   synchronized (pauseLock) {
  924                                       needPause = ldr.addReplyBer(retBer);
  925                                       if (needPause) {
  926                                           /*
  927                                            * Go into paused state; release
  928                                            * pauseLock
  929                                            */
  930                                           pauseReader();
  931                                       }
  932   
  933                                       // else release pauseLock
  934                                   }
  935                               } else {
  936                                   // System.err.println("Cannot find" +
  937                                   //              "LdapRequest for " + inMsgId);
  938                               }
  939                           }
  940                       } catch (Ber.DecodeException e) {
  941                           //System.err.println("Cannot parse Ber");
  942                       }
  943                   } catch (IOException ie) {
  944                       if (debug) {
  945                           System.err.println("Connection: Inside Caught " + ie);
  946                           ie.printStackTrace();
  947                       }
  948   
  949                       if (in != getInputStream()) {
  950                           // A new stream to try
  951                           // Go to top of loop and continue
  952                       } else {
  953                           if (debug) {
  954                               System.err.println("Connection: rethrowing " + ie);
  955                           }
  956                           throw ie;  // rethrow exception
  957                       }
  958                   }
  959               }
  960   
  961               if (debug) {
  962                   System.err.println("Connection: end-of-stream detected: "
  963                       + in);
  964               }
  965           } catch (IOException ex) {
  966               if (debug) {
  967                   System.err.println("Connection: Caught " + ex);
  968               }
  969               closureReason = ex;
  970           } finally {
  971               cleanup(null, true); // cleanup
  972           }
  973           if (debug) {
  974               System.err.println("Connection: Thread Exiting");
  975           }
  976       }
  977   
  978   
  979       // This code must be uncommented to run the LdapAbandonTest.
  980       /*public void sendSearchReqs(String dn, int numReqs) {
  981           int i;
  982           String attrs[] = null;
  983           for(i = 1; i <= numReqs; i++) {
  984               BerEncoder ber = new BerEncoder(2048);
  985   
  986               try {
  987               ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  988                   ber.encodeInt(i);
  989                   ber.beginSeq(LdapClient.LDAP_REQ_SEARCH);
  990                       ber.encodeString(dn == null ? "" : dn);
  991                       ber.encodeInt(0, LdapClient.LBER_ENUMERATED);
  992                       ber.encodeInt(3, LdapClient.LBER_ENUMERATED);
  993                       ber.encodeInt(0);
  994                       ber.encodeInt(0);
  995                       ber.encodeBoolean(true);
  996                       LdapClient.encodeFilter(ber, "");
  997                       ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
  998                           ber.encodeStringArray(attrs);
  999                       ber.endSeq();
 1000                   ber.endSeq();
 1001               ber.endSeq();
 1002               writeRequest(ber, i);
 1003               //System.err.println("wrote request " + i);
 1004               } catch (Exception ex) {
 1005               //System.err.println("ldap.search: Caught " + ex + " building req");
 1006               }
 1007   
 1008           }
 1009       } */
 1010   }

Save This Page
Home » openjdk-7 » com.sun.jndi » ldap » [javadoc | source]