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 javax.naming;
   29   import javax.naming.directory;
   30   import javax.naming.spi;
   31   import javax.naming.event;
   32   import javax.naming.ldap;
   33   import javax.naming.ldap.LdapName;
   34   import javax.naming.ldap.Rdn;
   35   
   36   import java.util.Vector;
   37   import java.util.Hashtable;
   38   import java.util.List;
   39   import java.util.StringTokenizer;
   40   import java.util.Enumeration;
   41   
   42   import java.io.IOException;
   43   import java.io.OutputStream;
   44   
   45   import com.sun.jndi.toolkit.ctx;
   46   import com.sun.jndi.toolkit.dir.HierMemDirCtx;
   47   import com.sun.jndi.toolkit.dir.SearchFilter;
   48   import com.sun.jndi.ldap.ext.StartTlsResponseImpl;
   49   
   50   /**
   51    * The LDAP context implementation.
   52    *
   53    * Implementation is not thread-safe. Caller must sync as per JNDI spec.
   54    * Members that are used directly or indirectly by internal worker threads
   55    * (Connection, EventQueue, NamingEventNotifier) must be thread-safe.
   56    * Connection - calls LdapClient.processUnsolicited(), which in turn calls
   57    *   LdapCtx.convertControls() and LdapCtx.fireUnsolicited().
   58    *   convertControls() - no sync; reads envprops and 'this'
   59    *   fireUnsolicited() - sync on eventSupport for all references to 'unsolicited'
   60    *      (even those in other methods);  don't sync on LdapCtx in case caller
   61    *      is already sync'ing on it - this would prevent Unsol events from firing
   62    *      and the Connection thread to block (thus preventing any other data
   63    *      from being read from the connection)
   64    *      References to 'eventSupport' need not be sync'ed because these
   65    *      methods can only be called after eventSupport has been set first
   66    *      (via addNamingListener()).
   67    * EventQueue - no direct or indirect calls to LdapCtx
   68    * NamingEventNotifier - calls newInstance() to get instance for run() to use;
   69    *      no sync needed for methods invoked on new instance;
   70    *
   71    * LdapAttribute links to LdapCtx in order to process getAttributeDefinition()
   72    * and getAttributeSyntaxDefinition() calls. It invokes LdapCtx.getSchema(),
   73    * which uses schemaTrees (a Hashtable - already sync). Potential conflict
   74    * of duplicating construction of tree for same subschemasubentry
   75    * but no inconsistency problems.
   76    *
   77    * NamingEnumerations link to LdapCtx for the following:
   78    * 1. increment/decrement enum count so that ctx doesn't close the
   79    *    underlying connection
   80    * 2. LdapClient handle to get next batch of results
   81    * 3. Sets LdapCtx's response controls
   82    * 4. Process return code
   83    * 5. For narrowing response controls (using ctx's factories)
   84    * Since processing of NamingEnumeration by client is treated the same as method
   85    * invocation on LdapCtx, caller is responsible for locking.
   86    *
   87    * @author Vincent Ryan
   88    * @author Rosanna Lee
   89    */
   90   
   91   final public class LdapCtx extends ComponentDirContext
   92       implements EventDirContext, LdapContext {
   93   
   94       /*
   95        * Used to store arguments to the search method.
   96        */
   97       final static class SearchArgs {
   98           Name name;
   99           String filter;
  100           SearchControls cons;
  101           String[] reqAttrs; // those attributes originally requested
  102   
  103           SearchArgs(Name name, String filter, SearchControls cons, String[] ra) {
  104               this.name = name;
  105               this.filter = filter;
  106               this.cons = cons;
  107               this.reqAttrs = ra;
  108           }
  109       }
  110   
  111       private static final boolean debug = false;
  112   
  113       private static final boolean HARD_CLOSE = true;
  114       private static final boolean SOFT_CLOSE = false;
  115   
  116       // -----------------  Constants  -----------------
  117   
  118       public static final int DEFAULT_PORT = 389;
  119       public static final int DEFAULT_SSL_PORT = 636;
  120       public static final String DEFAULT_HOST = "localhost";
  121   
  122       private static final boolean DEFAULT_DELETE_RDN = true;
  123       private static final boolean DEFAULT_TYPES_ONLY = false;
  124       private static final int DEFAULT_DEREF_ALIASES = 3; // always deref
  125       private static final int DEFAULT_LDAP_VERSION = LdapClient.LDAP_VERSION3_VERSION2;
  126       private static final int DEFAULT_BATCH_SIZE = 1;
  127       private static final int DEFAULT_REFERRAL_MODE = LdapClient.LDAP_REF_IGNORE;
  128       private static final char DEFAULT_REF_SEPARATOR = '#';
  129   
  130           // Used by LdapPoolManager
  131       static final String DEFAULT_SSL_FACTORY =
  132           "javax.net.ssl.SSLSocketFactory";       // use Sun's SSL
  133       private static final int DEFAULT_REFERRAL_LIMIT = 10;
  134       private static final String STARTTLS_REQ_OID = "1.3.6.1.4.1.1466.20037";
  135   
  136       // schema operational and user attributes
  137       private static final String[] SCHEMA_ATTRIBUTES =
  138           { "objectClasses", "attributeTypes", "matchingRules", "ldapSyntaxes" };
  139   
  140       // --------------- Environment property names ----------
  141   
  142       // LDAP protocol version: "2", "3"
  143       private static final String VERSION = "java.naming.ldap.version";
  144   
  145       // Binary-valued attributes. Space separated string of attribute names.
  146       private static final String BINARY_ATTRIBUTES =
  147                                           "java.naming.ldap.attributes.binary";
  148   
  149       // Delete old RDN during modifyDN: "true", "false"
  150       private static final String DELETE_RDN = "java.naming.ldap.deleteRDN";
  151   
  152       // De-reference aliases: "never", "searching", "finding", "always"
  153       private static final String DEREF_ALIASES = "java.naming.ldap.derefAliases";
  154   
  155       // Return only attribute types (no values)
  156       private static final String TYPES_ONLY = "java.naming.ldap.typesOnly";
  157   
  158       // Separator character for encoding Reference's RefAddrs; default is '#'
  159       private static final String REF_SEPARATOR = "java.naming.ldap.ref.separator";
  160   
  161       // Socket factory
  162       private static final String SOCKET_FACTORY = "java.naming.ldap.factory.socket";
  163   
  164       // Bind Controls (used by LdapReferralException)
  165       static final String BIND_CONTROLS = "java.naming.ldap.control.connect";
  166   
  167       private static final String REFERRAL_LIMIT =
  168           "java.naming.ldap.referral.limit";
  169   
  170       // trace BER (java.io.OutputStream)
  171       private static final String TRACE_BER = "com.sun.jndi.ldap.trace.ber";
  172   
  173       // Get around Netscape Schema Bugs
  174       private static final String NETSCAPE_SCHEMA_BUG =
  175           "com.sun.jndi.ldap.netscape.schemaBugs";
  176       // deprecated
  177       private static final String OLD_NETSCAPE_SCHEMA_BUG =
  178           "com.sun.naming.netscape.schemaBugs";   // for backward compatability
  179   
  180       // Timeout for socket connect
  181       private static final String CONNECT_TIMEOUT =
  182           "com.sun.jndi.ldap.connect.timeout";
  183   
  184        // Timeout for reading responses
  185       private static final String READ_TIMEOUT =
  186           "com.sun.jndi.ldap.read.timeout";
  187   
  188       // Environment property for connection pooling
  189       private static final String ENABLE_POOL = "com.sun.jndi.ldap.connect.pool";
  190   
  191       // Environment property for the domain name (derived from this context's DN)
  192       private static final String DOMAIN_NAME = "com.sun.jndi.ldap.domainname";
  193   
  194       // ----------------- Fields that don't change -----------------------
  195       private static final NameParser parser = new LdapNameParser();
  196   
  197       // controls that Provider needs
  198       private static final ControlFactory myResponseControlFactory =
  199           new DefaultResponseControlFactory();
  200       private static final Control manageReferralControl =
  201           new ManageReferralControl(false);
  202   
  203       private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();
  204       static {
  205           EMPTY_SCHEMA.setReadOnly(
  206               new SchemaViolationException("Cannot update schema object"));
  207       }
  208   
  209       // ------------ Package private instance variables ----------------
  210       // Cannot be private; used by enums
  211   
  212           // ------- Inherited by derived context instances
  213   
  214       int port_number;                    // port number of server
  215       String hostname = null;             // host name of server (no brackets
  216                                           //   for IPv6 literals)
  217       LdapClient clnt = null;             // connection handle
  218       Hashtable envprops = null;          // environment properties of context
  219       int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled
  220       boolean hasLdapsScheme = false;     // true if the context was created
  221                                           //  using an LDAPS URL.
  222   
  223           // ------- Not inherited by derived context instances
  224   
  225       String currentDN;                   // DN of this context
  226       Name currentParsedDN;               // DN of this context
  227       Vector respCtls = null;             // Response controls read
  228       Control[] reqCtls = null;           // Controls to be sent with each request
  229   
  230   
  231       // ------------- Private instance variables ------------------------
  232   
  233           // ------- Inherited by derived context instances
  234   
  235       private OutputStream trace = null;  // output stream for BER debug output
  236       private boolean netscapeSchemaBug = false;       // workaround
  237       private Control[] bindCtls = null;  // Controls to be sent with LDAP "bind"
  238       private int referralHopLimit = DEFAULT_REFERRAL_LIMIT;  // max referral
  239       private Hashtable schemaTrees = null; // schema root of this context
  240       private int batchSize = DEFAULT_BATCH_SIZE;      // batch size for search results
  241       private boolean deleteRDN = DEFAULT_DELETE_RDN;  // delete the old RDN when modifying DN
  242       private boolean typesOnly = DEFAULT_TYPES_ONLY;  // return attribute types (no values)
  243       private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching
  244       private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR;  // encoding RefAddr
  245   
  246       private Hashtable binaryAttrs = null;    // attr values returned as byte[]
  247       private int connectTimeout = -1;         // no timeout value
  248       private int readTimeout = -1;            // no timeout value
  249       private boolean useSsl = false;          // true if SSL protocol is active
  250       private boolean useDefaultPortNumber = false; // no port number was supplied
  251   
  252           // ------- Not inherited by derived context instances
  253   
  254       // True if this context was created by another LdapCtx.
  255       private boolean parentIsLdapCtx = false; // see composeName()
  256   
  257       private int hopCount = 1;                // current referral hop count
  258       private String url = null;               // URL of context; see getURL()
  259       private EventSupport eventSupport;       // Event support helper for this ctx
  260       private boolean unsolicited = false;     // if there unsolicited listeners
  261       private boolean sharable = true;         // can share connection with other ctx
  262   
  263       // -------------- Constructors  -----------------------------------
  264   
  265       public LdapCtx(String dn, String host, int port_number, Hashtable props,
  266               boolean useSsl) throws NamingException {
  267   
  268           this.useSsl = this.hasLdapsScheme = useSsl;
  269   
  270           if (props != null) {
  271               envprops = (Hashtable) props.clone();
  272   
  273               // SSL env prop overrides the useSsl argument
  274               if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {
  275                   this.useSsl = true;
  276               }
  277   
  278               // %%% These are only examined when the context is created
  279               // %%% because they are only for debugging or workaround purposes.
  280               trace = (OutputStream)envprops.get(TRACE_BER);
  281   
  282               if (props.get(NETSCAPE_SCHEMA_BUG) != null ||
  283                   props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {
  284                   netscapeSchemaBug = true;
  285               }
  286           }
  287   
  288           currentDN = (dn != null) ? dn : "";
  289           currentParsedDN = parser.parse(currentDN);
  290   
  291           hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST;
  292           if (hostname.charAt(0) == '[') {
  293               hostname = hostname.substring(1, hostname.length() - 1);
  294           }
  295   
  296           if (port_number > 0) {
  297               this.port_number = port_number;
  298           } else {
  299               this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT;
  300               this.useDefaultPortNumber = true;
  301           }
  302   
  303           schemaTrees = new Hashtable(11, 0.75f);
  304           initEnv();
  305           connect(false);
  306       }
  307   
  308       LdapCtx(LdapCtx existing, String newDN) throws NamingException {
  309           useSsl = existing.useSsl;
  310           hasLdapsScheme = existing.hasLdapsScheme;
  311           useDefaultPortNumber = existing.useDefaultPortNumber;
  312   
  313           hostname = existing.hostname;
  314           port_number = existing.port_number;
  315           currentDN = newDN;
  316           if (existing.currentDN == currentDN) {
  317               currentParsedDN = existing.currentParsedDN;
  318           } else {
  319               currentParsedDN = parser.parse(currentDN);
  320           }
  321   
  322           envprops = existing.envprops;
  323           schemaTrees = existing.schemaTrees;
  324   
  325           clnt = existing.clnt;
  326           clnt.incRefCount();
  327   
  328           parentIsLdapCtx = ((newDN == null || newDN.equals(existing.currentDN))
  329                              ? existing.parentIsLdapCtx
  330                              : true);
  331   
  332           // inherit these debugging/workaround flags
  333           trace = existing.trace;
  334           netscapeSchemaBug = existing.netscapeSchemaBug;
  335   
  336           initEnv();
  337       }
  338   
  339       public LdapContext newInstance(Control[] reqCtls) throws NamingException {
  340   
  341           LdapContext clone = new LdapCtx(this, currentDN);
  342   
  343           // Connection controls are inherited from environment
  344   
  345           // Set clone's request controls
  346           // setRequestControls() will clone reqCtls
  347           clone.setRequestControls(reqCtls);
  348           return clone;
  349       }
  350   
  351       // --------------- Namespace Updates ---------------------
  352       // -- bind/rebind/unbind
  353       // -- rename
  354       // -- createSubcontext/destroySubcontext
  355   
  356       protected void c_bind(Name name, Object obj, Continuation cont)
  357               throws NamingException {
  358           c_bind(name, obj, null, cont);
  359       }
  360   
  361       /*
  362        * attrs == null
  363        *      if obj is DirContext, attrs = obj.getAttributes()
  364        * if attrs == null && obj == null
  365        *      disallow (cannot determine objectclass to use)
  366        * if obj == null
  367        *      just create entry using attrs
  368        * else
  369        *      objAttrs = create attributes for representing obj
  370        *      attrs += objAttrs
  371        *      create entry using attrs
  372        */
  373       protected void c_bind(Name name, Object obj, Attributes attrs,
  374                             Continuation cont)
  375               throws NamingException {
  376   
  377           cont.setError(this, name);
  378   
  379           Attributes inputAttrs = attrs; // Attributes supplied by caller
  380           try {
  381               ensureOpen();
  382   
  383               if (obj == null) {
  384                   if (attrs == null) {
  385                       throw new IllegalArgumentException(
  386                           "cannot bind null object with no attributes");
  387                   }
  388               } else {
  389                   attrs = Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,
  390                       false, name, this, envprops); // not cloned
  391               }
  392   
  393               String newDN = fullyQualifiedName(name);
  394               attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
  395               LdapEntry entry = new LdapEntry(newDN, attrs);
  396   
  397               LdapResult answer = clnt.add(entry, reqCtls);
  398               respCtls = answer.resControls; // retrieve response controls
  399   
  400               if (answer.status != LdapClient.LDAP_SUCCESS) {
  401                   processReturnCode(answer, name);
  402               }
  403   
  404           } catch (LdapReferralException e) {
  405               if (handleReferrals == LdapClient.LDAP_REF_THROW)
  406                   throw cont.fillInException(e);
  407   
  408               // process the referrals sequentially
  409               while (true) {
  410   
  411                   LdapReferralContext refCtx =
  412                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
  413   
  414                   // repeat the original operation at the new context
  415                   try {
  416   
  417                       refCtx.bind(name, obj, inputAttrs);
  418                       return;
  419   
  420                   } catch (LdapReferralException re) {
  421                       e = re;
  422                       continue;
  423   
  424                   } finally {
  425                       // Make sure we close referral context
  426                       refCtx.close();
  427                   }
  428               }
  429   
  430           } catch (IOException e) {
  431               NamingException e2 = new CommunicationException(e.getMessage());
  432               e2.setRootCause(e);
  433               throw cont.fillInException(e2);
  434   
  435           } catch (NamingException e) {
  436               throw cont.fillInException(e);
  437           }
  438       }
  439   
  440       protected void c_rebind(Name name, Object obj, Continuation cont)
  441               throws NamingException {
  442           c_rebind(name, obj, null, cont);
  443       }
  444   
  445   
  446       /*
  447        * attrs == null
  448        *    if obj is DirContext, attrs = obj.getAttributes().
  449        * if attrs == null
  450        *    leave any existing attributes alone
  451        *    (set attrs = {objectclass=top} if object doesn't exist)
  452        * else
  453        *    replace all existing attributes with attrs
  454        * if obj == null
  455        *      just create entry using attrs
  456        * else
  457        *      objAttrs = create attributes for representing obj
  458        *      attrs += objAttrs
  459        *      create entry using attrs
  460        */
  461       protected void c_rebind(Name name, Object obj, Attributes attrs,
  462           Continuation cont) throws NamingException {
  463   
  464           cont.setError(this, name);
  465   
  466           Attributes inputAttrs = attrs;
  467   
  468           try {
  469               Attributes origAttrs = null;
  470   
  471               // Check if name is bound
  472               try {
  473                   origAttrs = c_getAttributes(name, null, cont);
  474               } catch (NameNotFoundException e) {}
  475   
  476               // Name not bound, just add it
  477               if (origAttrs == null) {
  478                   c_bind(name, obj, attrs, cont);
  479                   return;
  480               }
  481   
  482               // there's an object there already, need to figure out
  483               // what to do about its attributes
  484   
  485               if (attrs == null && obj instanceof DirContext) {
  486                   attrs = ((DirContext)obj).getAttributes("");
  487               }
  488               Attributes keepAttrs = (Attributes)origAttrs.clone();
  489   
  490               if (attrs == null) {
  491                   // we're not changing any attrs, leave old attributes alone
  492   
  493                   // Remove Java-related object classes from objectclass attribute
  494                   Attribute origObjectClass =
  495                       origAttrs.get(Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS]);
  496   
  497                   if (origObjectClass != null) {
  498                       // clone so that keepAttrs is not affected
  499                       origObjectClass = (Attribute)origObjectClass.clone();
  500                       for (int i = 0; i < Obj.JAVA_OBJECT_CLASSES.length; i++) {
  501                           origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES_LOWER[i]);
  502                           origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES[i]);
  503                       }
  504                       // update;
  505                       origAttrs.put(origObjectClass);
  506                   }
  507   
  508                   // remove all Java-related attributes except objectclass
  509                   for (int i = 1; i < Obj.JAVA_ATTRIBUTES.length; i++) {
  510                       origAttrs.remove(Obj.JAVA_ATTRIBUTES[i]);
  511                   }
  512   
  513                   attrs = origAttrs;
  514               }
  515               if (obj != null) {
  516                   attrs =
  517                       Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,
  518                           inputAttrs != attrs, name, this, envprops);
  519               }
  520   
  521               String newDN = fullyQualifiedName(name);
  522               // remove entry
  523               LdapResult answer = clnt.delete(newDN, reqCtls);
  524               respCtls = answer.resControls; // retrieve response controls
  525   
  526               if (answer.status != LdapClient.LDAP_SUCCESS) {
  527                   processReturnCode(answer, name);
  528                   return;
  529               }
  530   
  531               Exception addEx = null;
  532               try {
  533                   attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
  534   
  535                   // add it back using updated attrs
  536                   LdapEntry entry = new LdapEntry(newDN, attrs);
  537                   answer = clnt.add(entry, reqCtls);
  538                   if (answer.resControls != null) {
  539                       respCtls = appendVector(respCtls, answer.resControls);
  540                   }
  541               } catch (NamingException ae) {
  542                   addEx = ae;
  543               } catch (IOException ae) {
  544                   addEx = ae;
  545               }
  546   
  547               if ((addEx != null && !(addEx instanceof LdapReferralException)) ||
  548                   answer.status != LdapClient.LDAP_SUCCESS) {
  549                   // Attempt to restore old entry
  550                   LdapResult answer2 =
  551                       clnt.add(new LdapEntry(newDN, keepAttrs), reqCtls);
  552                   if (answer2.resControls != null) {
  553                       respCtls = appendVector(respCtls, answer2.resControls);
  554                   }
  555   
  556                   if (addEx == null) {
  557                       processReturnCode(answer, name);
  558                   }
  559               }
  560   
  561               // Rethrow exception
  562               if (addEx instanceof NamingException) {
  563                   throw (NamingException)addEx;
  564               } else if (addEx instanceof IOException) {
  565                   throw (IOException)addEx;
  566               }
  567   
  568           } catch (LdapReferralException e) {
  569               if (handleReferrals == LdapClient.LDAP_REF_THROW)
  570                   throw cont.fillInException(e);
  571   
  572               // process the referrals sequentially
  573               while (true) {
  574   
  575                   LdapReferralContext refCtx =
  576                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
  577   
  578                   // repeat the original operation at the new context
  579                   try {
  580   
  581                       refCtx.rebind(name, obj, inputAttrs);
  582                       return;
  583   
  584                   } catch (LdapReferralException re) {
  585                       e = re;
  586                       continue;
  587   
  588                   } finally {
  589                       // Make sure we close referral context
  590                       refCtx.close();
  591                   }
  592               }
  593   
  594           } catch (IOException e) {
  595               NamingException e2 = new CommunicationException(e.getMessage());
  596               e2.setRootCause(e);
  597               throw cont.fillInException(e2);
  598   
  599           } catch (NamingException e) {
  600               throw cont.fillInException(e);
  601           }
  602       }
  603   
  604       protected void c_unbind(Name name, Continuation cont)
  605               throws NamingException {
  606           cont.setError(this, name);
  607   
  608           try {
  609               ensureOpen();
  610   
  611               String fname = fullyQualifiedName(name);
  612               LdapResult answer = clnt.delete(fname, reqCtls);
  613               respCtls = answer.resControls; // retrieve response controls
  614   
  615               adjustDeleteStatus(fname, answer);
  616   
  617               if (answer.status != LdapClient.LDAP_SUCCESS) {
  618                   processReturnCode(answer, name);
  619               }
  620   
  621           } catch (LdapReferralException e) {
  622               if (handleReferrals == LdapClient.LDAP_REF_THROW)
  623                   throw cont.fillInException(e);
  624   
  625               // process the referrals sequentially
  626               while (true) {
  627   
  628                   LdapReferralContext refCtx =
  629                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
  630   
  631                   // repeat the original operation at the new context
  632                   try {
  633   
  634                       refCtx.unbind(name);
  635                       return;
  636   
  637                   } catch (LdapReferralException re) {
  638                       e = re;
  639                       continue;
  640   
  641                   } finally {
  642                       // Make sure we close referral context
  643                       refCtx.close();
  644                   }
  645               }
  646   
  647           } catch (IOException e) {
  648               NamingException e2 = new CommunicationException(e.getMessage());
  649               e2.setRootCause(e);
  650               throw cont.fillInException(e2);
  651   
  652           } catch (NamingException e) {
  653               throw cont.fillInException(e);
  654           }
  655       }
  656   
  657       protected void c_rename(Name oldName, Name newName, Continuation cont)
  658               throws NamingException
  659       {
  660           Name oldParsed, newParsed;
  661           Name oldParent, newParent;
  662           String newRDN = null;
  663           String newSuperior = null;
  664   
  665           // assert (oldName instanceOf CompositeName);
  666   
  667           cont.setError(this, oldName);
  668   
  669           try {
  670               ensureOpen();
  671   
  672               // permit oldName to be empty (for processing referral contexts)
  673               if (oldName.isEmpty()) {
  674                   oldParent = parser.parse("");
  675               } else {
  676                   oldParsed = parser.parse(oldName.get(0)); // extract DN & parse
  677                   oldParent = oldParsed.getPrefix(oldParsed.size() - 1);
  678               }
  679   
  680               if (newName instanceof CompositeName) {
  681                   newParsed = parser.parse(newName.get(0)); // extract DN & parse
  682               } else {
  683                   newParsed = newName; // CompoundName/LdapName is already parsed
  684               }
  685               newParent = newParsed.getPrefix(newParsed.size() - 1);
  686   
  687               if(!oldParent.equals(newParent)) {
  688                   if (!clnt.isLdapv3) {
  689                       throw new InvalidNameException(
  690                                     "LDAPv2 doesn't support changing " +
  691                                     "the parent as a result of a rename");
  692                   } else {
  693                       newSuperior = fullyQualifiedName(newParent.toString());
  694                   }
  695               }
  696   
  697               newRDN = newParsed.get(newParsed.size() - 1);
  698   
  699               LdapResult answer = clnt.moddn(fullyQualifiedName(oldName),
  700                                       newRDN,
  701                                       deleteRDN,
  702                                       newSuperior,
  703                                       reqCtls);
  704               respCtls = answer.resControls; // retrieve response controls
  705   
  706               if (answer.status != LdapClient.LDAP_SUCCESS) {
  707                   processReturnCode(answer, oldName);
  708               }
  709   
  710           } catch (LdapReferralException e) {
  711   
  712               // Record the new RDN (for use after the referral is followed).
  713               e.setNewRdn(newRDN);
  714   
  715               // Cannot continue when a referral has been received and a
  716               // newSuperior name was supplied (because the newSuperior is
  717               // relative to a naming context BEFORE the referral is followed).
  718               if (newSuperior != null) {
  719                   PartialResultException pre = new PartialResultException(
  720                       "Cannot continue referral processing when newSuperior is " +
  721                       "nonempty: " + newSuperior);
  722                   pre.setRootCause(cont.fillInException(e));
  723                   throw cont.fillInException(pre);
  724               }
  725   
  726               if (handleReferrals == LdapClient.LDAP_REF_THROW)
  727                   throw cont.fillInException(e);
  728   
  729               // process the referrals sequentially
  730               while (true) {
  731   
  732                   LdapReferralContext refCtx =
  733                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
  734   
  735                   // repeat the original operation at the new context
  736                   try {
  737   
  738                       refCtx.rename(oldName, newName);
  739                       return;
  740   
  741                   } catch (LdapReferralException re) {
  742                       e = re;
  743                       continue;
  744   
  745                   } finally {
  746                       // Make sure we close referral context
  747                       refCtx.close();
  748                   }
  749               }
  750   
  751           } catch (IOException e) {
  752               NamingException e2 = new CommunicationException(e.getMessage());
  753               e2.setRootCause(e);
  754               throw cont.fillInException(e2);
  755   
  756           } catch (NamingException e) {
  757               throw cont.fillInException(e);
  758           }
  759       }
  760   
  761       protected Context c_createSubcontext(Name name, Continuation cont)
  762               throws NamingException {
  763           return c_createSubcontext(name, null, cont);
  764       }
  765   
  766       protected DirContext c_createSubcontext(Name name, Attributes attrs,
  767                                               Continuation cont)
  768               throws NamingException {
  769           cont.setError(this, name);
  770   
  771           Attributes inputAttrs = attrs;
  772           try {
  773               ensureOpen();
  774               if (attrs == null) {
  775                     // add structural objectclass; name needs to have "cn"
  776                     Attribute oc = new BasicAttribute(
  777                         Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS],
  778                         Obj.JAVA_OBJECT_CLASSES[Obj.STRUCTURAL]);
  779                     oc.add("top");
  780                     attrs = new BasicAttributes(true); // case ignore
  781                     attrs.put(oc);
  782               }
  783               String newDN = fullyQualifiedName(name);
  784               attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
  785   
  786               LdapEntry entry = new LdapEntry(newDN, attrs);
  787   
  788               LdapResult answer = clnt.add(entry, reqCtls);
  789               respCtls = answer.resControls; // retrieve response controls
  790   
  791               if (answer.status != LdapClient.LDAP_SUCCESS) {
  792                   processReturnCode(answer, name);
  793                   return null;
  794               }
  795   
  796               // creation successful, get back live object
  797               return new LdapCtx(this, newDN);
  798   
  799           } catch (LdapReferralException e) {
  800               if (handleReferrals == LdapClient.LDAP_REF_THROW)
  801                   throw cont.fillInException(e);
  802   
  803               // process the referrals sequentially
  804               while (true) {
  805   
  806                   LdapReferralContext refCtx =
  807                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
  808   
  809                   // repeat the original operation at the new context
  810                   try {
  811   
  812                       return refCtx.createSubcontext(name, inputAttrs);
  813   
  814                   } catch (LdapReferralException re) {
  815                       e = re;
  816                       continue;
  817   
  818                   } finally {
  819                       // Make sure we close referral context
  820                       refCtx.close();
  821                   }
  822               }
  823   
  824           } catch (IOException e) {
  825               NamingException e2 = new CommunicationException(e.getMessage());
  826               e2.setRootCause(e);
  827               throw cont.fillInException(e2);
  828   
  829           } catch (NamingException e) {
  830               throw cont.fillInException(e);
  831           }
  832       }
  833   
  834       protected void c_destroySubcontext(Name name, Continuation cont)
  835           throws NamingException {
  836           cont.setError(this, name);
  837   
  838           try {
  839               ensureOpen();
  840   
  841               String fname = fullyQualifiedName(name);
  842               LdapResult answer = clnt.delete(fname, reqCtls);
  843               respCtls = answer.resControls; // retrieve response controls
  844   
  845               adjustDeleteStatus(fname, answer);
  846   
  847               if (answer.status != LdapClient.LDAP_SUCCESS) {
  848                   processReturnCode(answer, name);
  849               }
  850   
  851           } catch (LdapReferralException e) {
  852               if (handleReferrals == LdapClient.LDAP_REF_THROW)
  853                   throw cont.fillInException(e);
  854   
  855               // process the referrals sequentially
  856               while (true) {
  857   
  858                   LdapReferralContext refCtx =
  859                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
  860   
  861                   // repeat the original operation at the new context
  862                   try {
  863   
  864                       refCtx.destroySubcontext(name);
  865                       return;
  866                   } catch (LdapReferralException re) {
  867                       e = re;
  868                       continue;
  869                   } finally {
  870                       // Make sure we close referral context
  871                       refCtx.close();
  872                   }
  873               }
  874           } catch (IOException e) {
  875               NamingException e2 = new CommunicationException(e.getMessage());
  876               e2.setRootCause(e);
  877               throw cont.fillInException(e2);
  878           } catch (NamingException e) {
  879               throw cont.fillInException(e);
  880           }
  881       }
  882   
  883       /**
  884        * Adds attributes from RDN to attrs if not already present.
  885        * Note that if attrs already contains an attribute by the same name,
  886        * or if the distinguished name is empty, then leave attrs unchanged.
  887        *
  888        * @param dn The non-null DN of the entry to add
  889        * @param attrs The non-null attributes of entry to add
  890        * @param directUpdate Whether attrs can be updated directly
  891        * @returns Non-null attributes with attributes from the RDN added
  892        */
  893       private static Attributes addRdnAttributes(String dn, Attributes attrs,
  894           boolean directUpdate) throws NamingException {
  895   
  896               // Handle the empty name
  897               if (dn.equals("")) {
  898                   return attrs;
  899               }
  900   
  901               // Parse string name into list of RDNs
  902               //List<Rdn> rdnList = (new LdapName(dn)).rdns();
  903               List rdnList = (new LdapName(dn)).getRdns();
  904   
  905               // Get leaf RDN
  906               //Rdn rdn = rdnList.get(rdnList.size() - 1);
  907               Rdn rdn = (Rdn) rdnList.get(rdnList.size() - 1);
  908               Attributes nameAttrs = rdn.toAttributes();
  909   
  910               // Add attributes of RDN to attrs if not already there
  911               NamingEnumeration enum_ = nameAttrs.getAll();
  912               Attribute nameAttr;
  913               while (enum_.hasMore()) {
  914                   nameAttr = (Attribute) enum_.next();
  915   
  916                   // If attrs already has the attribute, don't change or add to it
  917                   if (attrs.get(nameAttr.getID()) ==  null) {
  918   
  919                       /**
  920                        * When attrs.isCaseIgnored() is false, attrs.get() will
  921                        * return null when the case mis-matches for otherwise
  922                        * equal attrIDs.
  923                        * As the attrIDs' case is irrelevant for LDAP, ignore
  924                        * the case of attrIDs even when attrs.isCaseIgnored() is
  925                        * false. This is done by explicitly comparing the elements in
  926                        * the enumeration of IDs with their case ignored.
  927                        */
  928                       if (!attrs.isCaseIgnored() &&
  929                               containsIgnoreCase(attrs.getIDs(), nameAttr.getID())) {
  930                           continue;
  931                       }
  932   
  933                       if (!directUpdate) {
  934                           attrs = (Attributes)attrs.clone();
  935                           directUpdate = true;
  936                       }
  937                       attrs.put(nameAttr);
  938                   }
  939               }
  940   
  941               return attrs;
  942       }
  943   
  944   
  945       private static boolean containsIgnoreCase(NamingEnumeration enumStr,
  946                                   String str) throws NamingException {
  947           String strEntry;
  948   
  949           while (enumStr.hasMore()) {
  950                strEntry = (String) enumStr.next();
  951                if (strEntry.equalsIgnoreCase(str)) {
  952                   return true;
  953                }
  954           }
  955           return false;
  956       }
  957   
  958   
  959       private void adjustDeleteStatus(String fname, LdapResult answer) {
  960           if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT &&
  961               answer.matchedDN != null) {
  962               try {
  963                   // %%% RL: are there any implications for referrals?
  964   
  965                   Name orig = parser.parse(fname);
  966                   Name matched = parser.parse(answer.matchedDN);
  967                   if ((orig.size() - matched.size()) == 1)
  968                       answer.status = LdapClient.LDAP_SUCCESS;
  969               } catch (NamingException e) {}
  970           }
  971       }
  972   
  973       /*
  974        * Append the the second Vector onto the first Vector
  975        * (v2 must be non-null)
  976        */
  977       private static Vector appendVector(Vector v1, Vector v2) {
  978           if (v1 == null) {
  979               v1 = v2;
  980           } else {
  981               for (int i = 0; i < v2.size(); i++) {
  982                   v1.addElement(v2.elementAt(i));
  983               }
  984           }
  985           return v1;
  986       }
  987   
  988       // ------------- Lookups and Browsing -------------------------
  989       // lookup/lookupLink
  990       // list/listBindings
  991   
  992       protected Object c_lookupLink(Name name, Continuation cont)
  993               throws NamingException {
  994           return c_lookup(name, cont);
  995       }
  996   
  997       protected Object c_lookup(Name name, Continuation cont)
  998               throws NamingException {
  999           cont.setError(this, name);
 1000           Object obj = null;
 1001           Attributes attrs;
 1002   
 1003           try {
 1004               SearchControls cons = new SearchControls();
 1005               cons.setSearchScope(SearchControls.OBJECT_SCOPE);
 1006               cons.setReturningAttributes(null); // ask for all attributes
 1007               cons.setReturningObjFlag(true); // need values to construct obj
 1008   
 1009               LdapResult answer = doSearchOnce(name, "(objectClass=*)", cons, true);
 1010               respCtls = answer.resControls; // retrieve response controls
 1011   
 1012               // should get back 1 SearchResponse and 1 SearchResult
 1013   
 1014               if (answer.status != LdapClient.LDAP_SUCCESS) {
 1015                   processReturnCode(answer, name);
 1016               }
 1017   
 1018               if (answer.entries == null || answer.entries.size() != 1) {
 1019                   // found it but got no attributes
 1020                   attrs = new BasicAttributes(LdapClient.caseIgnore);
 1021               } else {
 1022                   LdapEntry entry = (LdapEntry)answer.entries.elementAt(0);
 1023                   attrs = entry.attributes;
 1024   
 1025                   Vector entryCtls = entry.respCtls; // retrieve entry controls
 1026                   if (entryCtls != null) {
 1027                       appendVector(respCtls, entryCtls); // concatenate controls
 1028                   }
 1029               }
 1030   
 1031               if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
 1032                   // serialized object or object reference
 1033                   obj = Obj.decodeObject(attrs);
 1034               }
 1035               if (obj == null) {
 1036                   obj = new LdapCtx(this, fullyQualifiedName(name));
 1037               }
 1038           } catch (LdapReferralException e) {
 1039               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1040                   throw cont.fillInException(e);
 1041   
 1042               // process the referrals sequentially
 1043               while (true) {
 1044   
 1045                   LdapReferralContext refCtx =
 1046                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1047                   // repeat the original operation at the new context
 1048                   try {
 1049   
 1050                       return refCtx.lookup(name);
 1051   
 1052                   } catch (LdapReferralException re) {
 1053                       e = re;
 1054                       continue;
 1055   
 1056                   } finally {
 1057                       // Make sure we close referral context
 1058                       refCtx.close();
 1059                   }
 1060               }
 1061   
 1062           } catch (NamingException e) {
 1063               throw cont.fillInException(e);
 1064           }
 1065   
 1066           try {
 1067               return DirectoryManager.getObjectInstance(obj, name,
 1068                   this, envprops, attrs);
 1069   
 1070           } catch (NamingException e) {
 1071               throw cont.fillInException(e);
 1072   
 1073           } catch (Exception e) {
 1074               NamingException e2 = new NamingException(
 1075                       "problem generating object using object factory");
 1076               e2.setRootCause(e);
 1077               throw cont.fillInException(e2);
 1078           }
 1079       }
 1080   
 1081       protected NamingEnumeration c_list(Name name, Continuation cont)
 1082               throws NamingException {
 1083           SearchControls cons = new SearchControls();
 1084           String[] classAttrs = new String[2];
 1085   
 1086           classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS];
 1087           classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME];
 1088           cons.setReturningAttributes(classAttrs);
 1089   
 1090           // set this flag to override the typesOnly flag
 1091           cons.setReturningObjFlag(true);
 1092   
 1093           cont.setError(this, name);
 1094   
 1095           LdapResult answer = null;
 1096   
 1097           try {
 1098               answer = doSearch(name, "(objectClass=*)", cons, true, true);
 1099   
 1100               // list result may contain continuation references
 1101               if ((answer.status != LdapClient.LDAP_SUCCESS) ||
 1102                   (answer.referrals != null)) {
 1103                   processReturnCode(answer, name);
 1104               }
 1105   
 1106               return new LdapNamingEnumeration(this, answer, name, cont);
 1107   
 1108           } catch (LdapReferralException e) {
 1109               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1110                   throw cont.fillInException(e);
 1111   
 1112               // process the referrals sequentially
 1113               while (true) {
 1114   
 1115                   LdapReferralContext refCtx =
 1116                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1117   
 1118                   // repeat the original operation at the new context
 1119                   try {
 1120   
 1121                       return refCtx.list(name);
 1122   
 1123                   } catch (LdapReferralException re) {
 1124                       e = re;
 1125                       continue;
 1126   
 1127                   } finally {
 1128                       // Make sure we close referral context
 1129                       refCtx.close();
 1130                   }
 1131               }
 1132   
 1133           } catch (LimitExceededException e) {
 1134               LdapNamingEnumeration res =
 1135                   new LdapNamingEnumeration(this, answer, name, cont);
 1136   
 1137               res.setNamingException(
 1138                       (LimitExceededException)cont.fillInException(e));
 1139               return res;
 1140   
 1141           } catch (PartialResultException e) {
 1142               LdapNamingEnumeration res =
 1143                   new LdapNamingEnumeration(this, answer, name, cont);
 1144   
 1145               res.setNamingException(
 1146                       (PartialResultException)cont.fillInException(e));
 1147               return res;
 1148   
 1149           } catch (NamingException e) {
 1150               throw cont.fillInException(e);
 1151           }
 1152       }
 1153   
 1154       protected NamingEnumeration c_listBindings(Name name, Continuation cont)
 1155               throws NamingException {
 1156   
 1157           SearchControls cons = new SearchControls();
 1158           cons.setReturningAttributes(null); // ask for all attributes
 1159           cons.setReturningObjFlag(true); // need values to construct obj
 1160   
 1161           cont.setError(this, name);
 1162   
 1163           LdapResult answer = null;
 1164   
 1165           try {
 1166               answer = doSearch(name, "(objectClass=*)", cons, true, true);
 1167   
 1168               // listBindings result may contain continuation references
 1169               if ((answer.status != LdapClient.LDAP_SUCCESS) ||
 1170                   (answer.referrals != null)) {
 1171                   processReturnCode(answer, name);
 1172               }
 1173   
 1174               return new LdapBindingEnumeration(this, answer, name, cont);
 1175   
 1176           } catch (LdapReferralException e) {
 1177               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1178                   throw cont.fillInException(e);
 1179   
 1180               // process the referrals sequentially
 1181               while (true) {
 1182   
 1183                   LdapReferralContext refCtx =
 1184                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1185   
 1186                   // repeat the original operation at the new context
 1187                   try {
 1188   
 1189                       return refCtx.listBindings(name);
 1190   
 1191                   } catch (LdapReferralException re) {
 1192                       e = re;
 1193                       continue;
 1194   
 1195                   } finally {
 1196                       // Make sure we close referral context
 1197                       refCtx.close();
 1198                   }
 1199               }
 1200           } catch (LimitExceededException e) {
 1201               LdapBindingEnumeration res =
 1202                   new LdapBindingEnumeration(this, answer, name, cont);
 1203   
 1204               res.setNamingException(
 1205                       (LimitExceededException)cont.fillInException(e));
 1206               return res;
 1207   
 1208           } catch (PartialResultException e) {
 1209               LdapBindingEnumeration res =
 1210                   new LdapBindingEnumeration(this, answer, name, cont);
 1211   
 1212               res.setNamingException(
 1213                       (PartialResultException)cont.fillInException(e));
 1214               return res;
 1215   
 1216           } catch (NamingException e) {
 1217               throw cont.fillInException(e);
 1218           }
 1219       }
 1220   
 1221       // --------------- Name-related Methods -----------------------
 1222       // -- getNameParser/getNameInNamespace/composeName
 1223   
 1224       protected NameParser c_getNameParser(Name name, Continuation cont)
 1225               throws NamingException
 1226       {
 1227           // ignore name, always return same parser
 1228           cont.setSuccess();
 1229           return parser;
 1230       }
 1231   
 1232       public String getNameInNamespace() {
 1233           return currentDN;
 1234       }
 1235   
 1236       public Name composeName(Name name, Name prefix)
 1237           throws NamingException
 1238       {
 1239           Name result;
 1240   
 1241           // Handle compound names.  A pair of LdapNames is an easy case.
 1242           if ((name instanceof LdapName) && (prefix instanceof LdapName)) {
 1243               result = (Name)(prefix.clone());
 1244               result.addAll(name);
 1245               return new CompositeName().add(result.toString());
 1246           }
 1247           if (!(name instanceof CompositeName)) {
 1248               name = new CompositeName().add(name.toString());
 1249           }
 1250           if (!(prefix instanceof CompositeName)) {
 1251               prefix = new CompositeName().add(prefix.toString());
 1252           }
 1253   
 1254           int prefixLast = prefix.size() - 1;
 1255   
 1256           if (name.isEmpty() || prefix.isEmpty() ||
 1257                   name.get(0).equals("") || prefix.get(prefixLast).equals("")) {
 1258               return super.composeName(name, prefix);
 1259           }
 1260   
 1261           result = (Name)(prefix.clone());
 1262           result.addAll(name);
 1263   
 1264           if (parentIsLdapCtx) {
 1265               String ldapComp = concatNames(result.get(prefixLast + 1),
 1266                                             result.get(prefixLast));
 1267               result.remove(prefixLast + 1);
 1268               result.remove(prefixLast);
 1269               result.add(prefixLast, ldapComp);
 1270           }
 1271           return result;
 1272       }
 1273   
 1274       private String fullyQualifiedName(Name rel) {
 1275           return rel.isEmpty()
 1276                   ? currentDN
 1277                   : fullyQualifiedName(rel.get(0));
 1278       }
 1279   
 1280       private String fullyQualifiedName(String rel) {
 1281           return (concatNames(rel, currentDN));
 1282       }
 1283   
 1284       // used by LdapSearchEnumeration
 1285       private static String concatNames(String lesser, String greater) {
 1286           if (lesser == null || lesser.equals("")) {
 1287               return greater;
 1288           } else if (greater == null || greater.equals("")) {
 1289               return lesser;
 1290           } else {
 1291               return (lesser + "," + greater);
 1292           }
 1293       }
 1294   
 1295      // --------------- Reading and Updating Attributes
 1296      // getAttributes/modifyAttributes
 1297   
 1298       protected Attributes c_getAttributes(Name name, String[] attrIds,
 1299                                         Continuation cont)
 1300               throws NamingException {
 1301           cont.setError(this, name);
 1302   
 1303           SearchControls cons = new SearchControls();
 1304           cons.setSearchScope(SearchControls.OBJECT_SCOPE);
 1305           cons.setReturningAttributes(attrIds);
 1306   
 1307           try {
 1308               LdapResult answer =
 1309                   doSearchOnce(name, "(objectClass=*)", cons, true);
 1310               respCtls = answer.resControls; // retrieve response controls
 1311   
 1312               if (answer.status != LdapClient.LDAP_SUCCESS) {
 1313                   processReturnCode(answer, name);
 1314               }
 1315   
 1316               if (answer.entries == null || answer.entries.size() != 1) {
 1317                   return new BasicAttributes(LdapClient.caseIgnore);
 1318               }
 1319   
 1320               // get attributes from result
 1321               LdapEntry entry = (LdapEntry) answer.entries.elementAt(0);
 1322   
 1323               Vector entryCtls = entry.respCtls; // retrieve entry controls
 1324               if (entryCtls != null) {
 1325                   appendVector(respCtls, entryCtls); // concatenate controls
 1326               }
 1327   
 1328               // do this so attributes can find their schema
 1329               setParents(entry.attributes, (Name) name.clone());
 1330   
 1331               return (entry.attributes);
 1332   
 1333           } catch (LdapReferralException e) {
 1334               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1335                   throw cont.fillInException(e);
 1336   
 1337               // process the referrals sequentially
 1338               while (true) {
 1339   
 1340                   LdapReferralContext refCtx =
 1341                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1342   
 1343                   // repeat the original operation at the new context
 1344                   try {
 1345   
 1346                       return refCtx.getAttributes(name, attrIds);
 1347   
 1348                   } catch (LdapReferralException re) {
 1349                       e = re;
 1350                       continue;
 1351   
 1352                   } finally {
 1353                       // Make sure we close referral context
 1354                       refCtx.close();
 1355                   }
 1356               }
 1357   
 1358           } catch (NamingException e) {
 1359               throw cont.fillInException(e);
 1360           }
 1361       }
 1362   
 1363       protected void c_modifyAttributes(Name name, int mod_op, Attributes attrs,
 1364                                         Continuation cont)
 1365               throws NamingException {
 1366   
 1367           cont.setError(this, name);
 1368   
 1369           try {
 1370               ensureOpen();
 1371   
 1372               if (attrs == null || attrs.size() == 0) {
 1373                   return; // nothing to do
 1374               }
 1375               String newDN = fullyQualifiedName(name);
 1376               int jmod_op = convertToLdapModCode(mod_op);
 1377   
 1378               // construct mod list
 1379               int[] jmods = new int[attrs.size()];
 1380               Attribute[] jattrs = new Attribute[attrs.size()];
 1381   
 1382               NamingEnumeration ae = attrs.getAll();
 1383               for(int i = 0; i < jmods.length && ae.hasMore(); i++) {
 1384                   jmods[i] = jmod_op;
 1385                   jattrs[i] = (Attribute)ae.next();
 1386               }
 1387   
 1388               LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
 1389               respCtls = answer.resControls; // retrieve response controls
 1390   
 1391               if (answer.status != LdapClient.LDAP_SUCCESS) {
 1392                   processReturnCode(answer, name);
 1393                   return;
 1394               }
 1395   
 1396           } catch (LdapReferralException e) {
 1397               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1398                   throw cont.fillInException(e);
 1399   
 1400               // process the referrals sequentially
 1401               while (true) {
 1402   
 1403                   LdapReferralContext refCtx =
 1404                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1405   
 1406                   // repeat the original operation at the new context
 1407                   try {
 1408   
 1409                       refCtx.modifyAttributes(name, mod_op, attrs);
 1410                       return;
 1411   
 1412                   } catch (LdapReferralException re) {
 1413                       e = re;
 1414                       continue;
 1415   
 1416                   } finally {
 1417                       // Make sure we close referral context
 1418                       refCtx.close();
 1419                   }
 1420               }
 1421   
 1422           } catch (IOException e) {
 1423               NamingException e2 = new CommunicationException(e.getMessage());
 1424               e2.setRootCause(e);
 1425               throw cont.fillInException(e2);
 1426   
 1427           } catch (NamingException e) {
 1428               throw cont.fillInException(e);
 1429           }
 1430       }
 1431   
 1432       protected void c_modifyAttributes(Name name, ModificationItem[] mods,
 1433                                         Continuation cont)
 1434               throws NamingException {
 1435           cont.setError(this, name);
 1436   
 1437           try {
 1438               ensureOpen();
 1439   
 1440               if (mods == null || mods.length == 0) {
 1441                   return; // nothing to do
 1442               }
 1443               String newDN = fullyQualifiedName(name);
 1444   
 1445               // construct mod list
 1446               int[] jmods = new int[mods.length];
 1447               Attribute[] jattrs = new Attribute[mods.length];
 1448               ModificationItem mod;
 1449               for (int i = 0; i < jmods.length; i++) {
 1450                   mod = mods[i];
 1451                   jmods[i] = convertToLdapModCode(mod.getModificationOp());
 1452                   jattrs[i] = mod.getAttribute();
 1453               }
 1454   
 1455               LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
 1456               respCtls = answer.resControls; // retrieve response controls
 1457   
 1458               if (answer.status != LdapClient.LDAP_SUCCESS) {
 1459                   processReturnCode(answer, name);
 1460               }
 1461   
 1462           } catch (LdapReferralException e) {
 1463               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1464                   throw cont.fillInException(e);
 1465   
 1466               // process the referrals sequentially
 1467               while (true) {
 1468   
 1469                   LdapReferralContext refCtx =
 1470                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1471   
 1472                   // repeat the original operation at the new context
 1473                   try {
 1474   
 1475                       refCtx.modifyAttributes(name, mods);
 1476                       return;
 1477   
 1478                   } catch (LdapReferralException re) {
 1479                       e = re;
 1480                       continue;
 1481   
 1482                   } finally {
 1483                       // Make sure we close referral context
 1484                       refCtx.close();
 1485                   }
 1486               }
 1487   
 1488           } catch (IOException e) {
 1489               NamingException e2 = new CommunicationException(e.getMessage());
 1490               e2.setRootCause(e);
 1491               throw cont.fillInException(e2);
 1492   
 1493           } catch (NamingException e) {
 1494               throw cont.fillInException(e);
 1495           }
 1496       }
 1497   
 1498       private static int convertToLdapModCode(int mod_op) {
 1499           switch (mod_op) {
 1500           case DirContext.ADD_ATTRIBUTE:
 1501               return(LdapClient.ADD);
 1502   
 1503           case DirContext.REPLACE_ATTRIBUTE:
 1504               return (LdapClient.REPLACE);
 1505   
 1506           case DirContext.REMOVE_ATTRIBUTE:
 1507               return (LdapClient.DELETE);
 1508   
 1509           default:
 1510               throw new IllegalArgumentException("Invalid modification code");
 1511           }
 1512       }
 1513   
 1514      // ------------------- Schema -----------------------
 1515   
 1516       protected DirContext c_getSchema(Name name, Continuation cont)
 1517               throws NamingException {
 1518           cont.setError(this, name);
 1519           try {
 1520               return getSchemaTree(name);
 1521   
 1522           } catch (NamingException e) {
 1523               throw cont.fillInException(e);
 1524           }
 1525       }
 1526   
 1527       protected DirContext c_getSchemaClassDefinition(Name name,
 1528                                                       Continuation cont)
 1529               throws NamingException {
 1530           cont.setError(this, name);
 1531   
 1532           try {
 1533               // retrieve the objectClass attribute from LDAP
 1534               Attribute objectClassAttr = c_getAttributes(name,
 1535                   new String[]{"objectclass"}, cont).get("objectclass");
 1536               if (objectClassAttr == null || objectClassAttr.size() == 0) {
 1537                   return EMPTY_SCHEMA;
 1538               }
 1539   
 1540               // retrieve the root of the ObjectClass schema tree
 1541               Context ocSchema = (Context) c_getSchema(name, cont).lookup(
 1542                   LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME);
 1543   
 1544               // create a context to hold the schema objects representing the object
 1545               // classes
 1546               HierMemDirCtx objectClassCtx = new HierMemDirCtx();
 1547               DirContext objectClassDef;
 1548               String objectClassName;
 1549               for (Enumeration objectClasses = objectClassAttr.getAll();
 1550                   objectClasses.hasMoreElements(); ) {
 1551                   objectClassName = (String)objectClasses.nextElement();
 1552                   // %%% Should we fail if not found, or just continue?
 1553                   objectClassDef = (DirContext)ocSchema.lookup(objectClassName);
 1554                   objectClassCtx.bind(objectClassName, objectClassDef);
 1555               }
 1556   
 1557               // Make context read-only
 1558               objectClassCtx.setReadOnly(
 1559                   new SchemaViolationException("Cannot update schema object"));
 1560               return (DirContext)objectClassCtx;
 1561   
 1562           } catch (NamingException e) {
 1563               throw cont.fillInException(e);
 1564           }
 1565       }
 1566   
 1567       /*
 1568        * getSchemaTree first looks to see if we have already built a
 1569        * schema tree for the given entry. If not, it builds a new one and
 1570        * stores it in our private hash table
 1571        */
 1572       private DirContext getSchemaTree(Name name) throws NamingException {
 1573           String subschemasubentry = getSchemaEntry(name, true);
 1574   
 1575           DirContext schemaTree = (DirContext)schemaTrees.get(subschemasubentry);
 1576   
 1577           if(schemaTree==null) {
 1578               if(debug){System.err.println("LdapCtx: building new schema tree " + this);}
 1579               schemaTree = buildSchemaTree(subschemasubentry);
 1580               schemaTrees.put(subschemasubentry, schemaTree);
 1581           }
 1582   
 1583           return schemaTree;
 1584       }
 1585   
 1586       /*
 1587        * buildSchemaTree builds the schema tree corresponding to the
 1588        * given subschemasubentree
 1589        */
 1590       private DirContext buildSchemaTree(String subschemasubentry)
 1591           throws NamingException {
 1592   
 1593           // get the schema entry itself
 1594           // DO ask for return object here because we need it to
 1595           // create context. Since asking for all attrs, we won't
 1596           // be transmitting any specific attrIDs (like Java-specific ones).
 1597           SearchControls constraints = new
 1598               SearchControls(SearchControls.OBJECT_SCOPE,
 1599                   0, 0, /* count and time limits */
 1600                   SCHEMA_ATTRIBUTES /* return schema attrs */,
 1601                   true /* return obj */,
 1602                   false /*deref link */ );
 1603   
 1604           Name sse = (new CompositeName()).add(subschemasubentry);
 1605           NamingEnumeration results =
 1606               searchAux(sse, "(objectClass=subschema)", constraints,
 1607               false, true, new Continuation());
 1608   
 1609           if(!results.hasMore()) {
 1610               throw new OperationNotSupportedException(
 1611                   "Cannot get read subschemasubentry: " + subschemasubentry);
 1612           }
 1613           SearchResult result = (SearchResult)results.next();
 1614           results.close();
 1615   
 1616           Object obj = result.getObject();
 1617           if(!(obj instanceof LdapCtx)) {
 1618               throw new NamingException(
 1619                   "Cannot get schema object as DirContext: " + subschemasubentry);
 1620           }
 1621   
 1622           return LdapSchemaCtx.createSchemaTree(envprops, subschemasubentry,
 1623               (LdapCtx)obj /* schema entry */,
 1624               result.getAttributes() /* schema attributes */,
 1625               netscapeSchemaBug);
 1626      }
 1627   
 1628       /*
 1629        * getSchemaEntree returns the DN of the subschemasubentree for the
 1630        * given entree. It first looks to see if the given entry has
 1631        * a subschema different from that of the root DIT (by looking for
 1632        * a "subschemasubentry" attribute). If it doesn't find one, it returns
 1633        * the one for the root of the DIT (by looking for the root's
 1634        * "subschemasubentry" attribute).
 1635        *
 1636        * This function is called regardless of the server's version, since
 1637        * an administrator may have setup the server to support client schema
 1638        * queries. If this function trys a serarch on a v2 server that
 1639        * doesn't support schema, one of these two things will happen:
 1640        * 1) It will get an exception when querying the root DSE
 1641        * 2) It will not find a subschemasubentry on the root DSE
 1642        * If either of these things occur and the server is not v3, we
 1643        * throw OperationNotSupported.
 1644        *
 1645        * the relative flag tells whether the given name is relative to this
 1646        * context.
 1647        */
 1648       private String getSchemaEntry(Name name, boolean relative)
 1649           throws NamingException {
 1650   
 1651           // Asks for operational attribute "subschemasubentry"
 1652           SearchControls constraints = new SearchControls(SearchControls.OBJECT_SCOPE,
 1653               0, 0, /* count and time limits */
 1654               new String[]{"subschemasubentry"} /* attr to return */,
 1655               false /* returning obj */,
 1656               false /* deref link */);
 1657   
 1658           NamingEnumeration results;
 1659           try {
 1660               results = searchAux(name, "objectclass=*", constraints, relative,
 1661                   true, new Continuation());
 1662   
 1663           } catch (NamingException ne) {
 1664               if (!clnt.isLdapv3 && currentDN.length() == 0 && name.isEmpty()) {
 1665                   // we got an error looking for a root entry on an ldapv2
 1666                   // server. The server must not support schema.
 1667                   throw new OperationNotSupportedException(
 1668                       "Cannot get schema information from server");
 1669               } else {
 1670                   throw ne;
 1671               }
 1672           }
 1673   
 1674           if (!results.hasMoreElements()) {
 1675               throw new ConfigurationException(
 1676                   "Requesting schema of nonexistent entry: " + name);
 1677           }
 1678   
 1679           SearchResult result = (SearchResult) results.next();
 1680           results.close();
 1681   
 1682           Attribute schemaEntryAttr =
 1683               result.getAttributes().get("subschemasubentry");
 1684           //System.err.println("schema entry attrs: " + schemaEntryAttr);
 1685   
 1686           if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) {
 1687               if (currentDN.length() == 0 && name.isEmpty()) {
 1688                   // the server doesn't have a subschemasubentry in its root DSE.
 1689                   // therefore, it doesn't support schema.
 1690                   throw new OperationNotSupportedException(
 1691                       "Cannot read subschemasubentry of root DSE");
 1692               } else {
 1693                   return getSchemaEntry(new CompositeName(), false);
 1694               }
 1695           }
 1696   
 1697           return (String)(schemaEntryAttr.get()); // return schema entry name
 1698       }
 1699   
 1700       // package-private; used by search enum.
 1701       // Set attributes to point to this context in case some one
 1702       // asked for their schema
 1703       void setParents(Attributes attrs, Name name) throws NamingException {
 1704           NamingEnumeration ae = attrs.getAll();
 1705           while(ae.hasMore()) {
 1706               ((LdapAttribute) ae.next()).setParent(this, name);
 1707           }
 1708       }
 1709   
 1710       /*
 1711        * Returns the URL associated with this context; used by LdapAttribute
 1712        * after deserialization to get pointer to this context.
 1713        */
 1714       String getURL() {
 1715           if (url == null) {
 1716               url = LdapURL.toUrlString(hostname, port_number, currentDN,
 1717                   hasLdapsScheme);
 1718           }
 1719   
 1720           return url;
 1721       }
 1722   
 1723      // --------------------- Searches -----------------------------
 1724       protected NamingEnumeration c_search(Name name,
 1725                                            Attributes matchingAttributes,
 1726                                            Continuation cont)
 1727               throws NamingException {
 1728           return c_search(name, matchingAttributes, null, cont);
 1729       }
 1730   
 1731       protected NamingEnumeration c_search(Name name,
 1732                                            Attributes matchingAttributes,
 1733                                            String[] attributesToReturn,
 1734                                            Continuation cont)
 1735               throws NamingException {
 1736           SearchControls cons = new SearchControls();
 1737           cons.setReturningAttributes(attributesToReturn);
 1738           String filter;
 1739           try {
 1740               filter = SearchFilter.format(matchingAttributes);
 1741           } catch (NamingException e) {
 1742               cont.setError(this, name);
 1743               throw cont.fillInException(e);
 1744           }
 1745           return c_search(name, filter, cons, cont);
 1746       }
 1747   
 1748       protected NamingEnumeration c_search(Name name,
 1749                                            String filter,
 1750                                            SearchControls cons,
 1751                                            Continuation cont)
 1752               throws NamingException {
 1753           return searchAux(name, filter, cloneSearchControls(cons), true, true,
 1754                            cont);
 1755       }
 1756   
 1757       protected NamingEnumeration c_search(Name name,
 1758                                            String filterExpr,
 1759                                            Object[] filterArgs,
 1760                                            SearchControls cons,
 1761                                            Continuation cont)
 1762               throws NamingException {
 1763           String strfilter;
 1764           try {
 1765               strfilter = SearchFilter.format(filterExpr, filterArgs);
 1766           } catch (NamingException e) {
 1767               cont.setError(this, name);
 1768               throw cont.fillInException(e);
 1769           }
 1770           return c_search(name, strfilter, cons, cont);
 1771       }
 1772   
 1773           // Used by NamingNotifier
 1774       NamingEnumeration searchAux(Name name,
 1775           String filter,
 1776           SearchControls cons,
 1777           boolean relative,
 1778           boolean waitForReply, Continuation cont) throws NamingException {
 1779   
 1780           LdapResult answer = null;
 1781           String[] tokens = new String[2];    // stores ldap compare op. values
 1782           String[] reqAttrs;                  // remember what was asked
 1783   
 1784           if (cons == null) {
 1785               cons = new SearchControls();
 1786           }
 1787           reqAttrs = cons.getReturningAttributes();
 1788   
 1789           // if objects are requested then request the Java attributes too
 1790           // so that the objects can be constructed
 1791           if (cons.getReturningObjFlag()) {
 1792               if (reqAttrs != null) {
 1793   
 1794                   // check for presence of "*" (user attributes wildcard)
 1795                   boolean hasWildcard = false;
 1796                   for (int i = reqAttrs.length - 1; i >= 0; i--) {
 1797                       if (reqAttrs[i].equals("*")) {
 1798                           hasWildcard = true;
 1799                           break;
 1800                       }
 1801                   }
 1802                   if (! hasWildcard) {
 1803                       String[] totalAttrs =
 1804                           new String[reqAttrs.length +Obj.JAVA_ATTRIBUTES.length];
 1805                       System.arraycopy(reqAttrs, 0, totalAttrs, 0,
 1806                           reqAttrs.length);
 1807                       System.arraycopy(Obj.JAVA_ATTRIBUTES, 0, totalAttrs,
 1808                           reqAttrs.length, Obj.JAVA_ATTRIBUTES.length);
 1809   
 1810                       cons.setReturningAttributes(totalAttrs);
 1811                   }
 1812               }
 1813           }
 1814   
 1815           LdapCtx.SearchArgs args =
 1816               new LdapCtx.SearchArgs(name, filter, cons, reqAttrs);
 1817   
 1818           cont.setError(this, name);
 1819           try {
 1820               // see if this can be done as a compare, otherwise do a search
 1821               if (searchToCompare(filter, cons, tokens)){
 1822                   //System.err.println("compare triggered");
 1823                   answer = compare(name, tokens[0], tokens[1]);
 1824                   if (! (answer.compareToSearchResult(fullyQualifiedName(name)))){
 1825                       processReturnCode(answer, name);
 1826                   }
 1827               } else {
 1828                   answer = doSearch(name, filter, cons, relative, waitForReply);
 1829                   // search result may contain referrals
 1830                   processReturnCode(answer, name);
 1831               }
 1832               return new LdapSearchEnumeration(this, answer,
 1833                   fullyQualifiedName(name), args, cont);
 1834   
 1835           } catch (LdapReferralException e) {
 1836               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 1837                   throw cont.fillInException(e);
 1838   
 1839               // process the referrals sequentially
 1840               while (true) {
 1841   
 1842                   LdapReferralContext refCtx =
 1843                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 1844   
 1845                   // repeat the original operation at the new context
 1846                   try {
 1847   
 1848                       return refCtx.search(name, filter, cons);
 1849   
 1850                   } catch (LdapReferralException re) {
 1851                       e = re;
 1852                       continue;
 1853   
 1854                   } finally {
 1855                       // Make sure we close referral context
 1856                       refCtx.close();
 1857                   }
 1858               }
 1859   
 1860           } catch (LimitExceededException e) {
 1861               LdapSearchEnumeration res =
 1862                   new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
 1863                                             args, cont);
 1864               res.setNamingException(e);
 1865               return res;
 1866   
 1867           } catch (PartialResultException e) {
 1868               LdapSearchEnumeration res =
 1869                   new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
 1870                                             args, cont);
 1871   
 1872               res.setNamingException(e);
 1873               return res;
 1874   
 1875           } catch (IOException e) {
 1876               NamingException e2 = new CommunicationException(e.getMessage());
 1877               e2.setRootCause(e);
 1878               throw cont.fillInException(e2);
 1879   
 1880           } catch (NamingException e) {
 1881               throw cont.fillInException(e);
 1882           }
 1883       }
 1884   
 1885   
 1886       LdapResult getSearchReply(LdapClient eClnt, LdapResult res)
 1887               throws NamingException {
 1888           // ensureOpen() won't work here because
 1889           // session was associated with previous connection
 1890   
 1891           // %%% RL: we can actually allow the enumeration to continue
 1892           // using the old handle but other weird things might happen
 1893           // when we hit a referral
 1894           if (clnt != eClnt) {
 1895               throw new CommunicationException(
 1896                   "Context's connection changed; unable to continue enumeration");
 1897           }
 1898   
 1899           try {
 1900               return eClnt.getSearchReply(batchSize, res, binaryAttrs);
 1901           } catch (IOException e) {
 1902               NamingException e2 = new CommunicationException(e.getMessage());
 1903               e2.setRootCause(e);
 1904               throw e2;
 1905           }
 1906       }
 1907   
 1908       // Perform a search. Expect 1 SearchResultEntry and the SearchResultDone.
 1909       private LdapResult doSearchOnce(Name name, String filter,
 1910           SearchControls cons, boolean relative) throws NamingException {
 1911   
 1912           int savedBatchSize = batchSize;
 1913           batchSize = 2; // 2 protocol elements
 1914   
 1915           LdapResult answer = doSearch(name, filter, cons, relative, true);
 1916   
 1917           batchSize = savedBatchSize;
 1918           return answer;
 1919       }
 1920   
 1921       private LdapResult doSearch(Name name, String filter, SearchControls cons,
 1922           boolean relative, boolean waitFirstReply) throws NamingException {
 1923               ensureOpen();
 1924               try {
 1925                   int scope;
 1926   
 1927                   switch (cons.getSearchScope()) {
 1928                   case SearchControls.OBJECT_SCOPE:
 1929                       scope = LdapClient.SCOPE_BASE_OBJECT;
 1930                       break;
 1931                   default:
 1932                   case SearchControls.ONELEVEL_SCOPE:
 1933                       scope = LdapClient.SCOPE_ONE_LEVEL;
 1934                       break;
 1935                   case SearchControls.SUBTREE_SCOPE:
 1936                       scope = LdapClient.SCOPE_SUBTREE;
 1937                       break;
 1938                   }
 1939   
 1940                   // If cons.getReturningObjFlag() then caller should already
 1941                   // have make sure to request the appropriate attrs
 1942   
 1943                   String[] retattrs = cons.getReturningAttributes();
 1944                   if (retattrs != null && retattrs.length == 0) {
 1945                       // Ldap treats null and empty array the same
 1946                       // need to replace with single element array
 1947                       retattrs = new String[1];
 1948                       retattrs[0] = "1.1";
 1949                   }
 1950   
 1951                   String nm = (relative
 1952                                ? fullyQualifiedName(name)
 1953                                : (name.isEmpty()
 1954                                   ? ""
 1955                                   : name.get(0)));
 1956   
 1957                   // JNDI unit is milliseconds, LDAP unit is seconds.
 1958                   // Zero means no limit.
 1959                   int msecLimit = cons.getTimeLimit();
 1960                   int secLimit = 0;
 1961   
 1962                   if (msecLimit > 0) {
 1963                       secLimit = (msecLimit / 1000) + 1;
 1964                   }
 1965   
 1966                   LdapResult answer =
 1967                       clnt.search(nm,
 1968                           scope,
 1969                           derefAliases,
 1970                           (int)cons.getCountLimit(),
 1971                           secLimit,
 1972                           cons.getReturningObjFlag() ? false : typesOnly,
 1973                           retattrs,
 1974                           filter,
 1975                           batchSize,
 1976                           reqCtls,
 1977                           binaryAttrs,
 1978                           waitFirstReply);
 1979                   respCtls = answer.resControls; // retrieve response controls
 1980                   return answer;
 1981   
 1982               } catch (IOException e) {
 1983                   NamingException e2 = new CommunicationException(e.getMessage());
 1984                   e2.setRootCause(e);
 1985                   throw e2;
 1986               }
 1987       }
 1988   
 1989   
 1990       /*
 1991        * Certain simple JNDI searches are automatically converted to
 1992        * LDAP compare operations by the LDAP service provider. A search
 1993        * is converted to a compare iff:
 1994        *
 1995        *    - the scope is set to OBJECT_SCOPE
 1996        *    - the filter string contains a simple assertion: "<type>=<value>"
 1997        *    - the returning attributes list is present but empty
 1998        */
 1999   
 2000       // returns true if a search can be caried out as a compare, and sets
 2001       // tokens[0] and tokens[1] to the type and value respectively.
 2002       // e.g. filter "cn=Jon Ruiz" becomes, type "cn" and value "Jon Ruiz"
 2003       // This function uses the documents JNDI Compare example as a model
 2004       // for when to turn a search into a compare.
 2005   
 2006       private static boolean searchToCompare(
 2007                                       String filter,
 2008                                       SearchControls cons,
 2009                                       String tokens[]) {
 2010   
 2011           // if scope is not object-scope, it's really a search
 2012           if (cons.getSearchScope() != SearchControls.OBJECT_SCOPE) {
 2013               return false;
 2014           }
 2015   
 2016           // if attributes are to be returned, it's really a search
 2017           String[] attrs = cons.getReturningAttributes();
 2018           if (attrs == null || attrs.length != 0) {
 2019               return false;
 2020           }
 2021   
 2022           // if the filter not a simple assertion, it's really a search
 2023           if (! filterToAssertion(filter, tokens)) {
 2024               return false;
 2025           }
 2026   
 2027           // it can be converted to a compare
 2028           return true;
 2029       }
 2030   
 2031       // If the supplied filter is a simple assertion i.e. "<type>=<value>"
 2032       // (enclosing parentheses are permitted) then
 2033       // filterToAssertion will return true and pass the type and value as
 2034       // the first and second elements of tokens respectively.
 2035       // precondition: tokens[] must be initialized and be at least of size 2.
 2036   
 2037       private static boolean filterToAssertion(String filter, String tokens[]) {
 2038   
 2039           // find the left and right half of the assertion
 2040           StringTokenizer assertionTokenizer = new StringTokenizer(filter, "=");
 2041   
 2042           if (assertionTokenizer.countTokens() != 2) {
 2043               return false;
 2044           }
 2045   
 2046           tokens[0] = assertionTokenizer.nextToken();
 2047           tokens[1] = assertionTokenizer.nextToken();
 2048   
 2049           // make sure the value does not contain a wildcard
 2050           if (tokens[1].indexOf('*') != -1) {
 2051               return false;
 2052           }
 2053   
 2054           // test for enclosing parenthesis
 2055           boolean hasParens = false;
 2056           int len = tokens[1].length();
 2057   
 2058           if ((tokens[0].charAt(0) == '(') &&
 2059               (tokens[1].charAt(len - 1) == ')')) {
 2060               hasParens = true;
 2061   
 2062           } else if ((tokens[0].charAt(0) == '(') ||
 2063               (tokens[1].charAt(len - 1) == ')')) {
 2064               return false; // unbalanced
 2065           }
 2066   
 2067           // make sure the left and right half are not expresions themselves
 2068           StringTokenizer illegalCharsTokenizer =
 2069               new StringTokenizer(tokens[0], "()&|!=~><*", true);
 2070   
 2071           if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
 2072               return false;
 2073           }
 2074   
 2075           illegalCharsTokenizer =
 2076               new StringTokenizer(tokens[1], "()&|!=~><*", true);
 2077   
 2078           if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
 2079               return false;
 2080           }
 2081   
 2082           // strip off enclosing parenthesis, if present
 2083           if (hasParens) {
 2084               tokens[0] = tokens[0].substring(1);
 2085               tokens[1] = tokens[1].substring(0, len - 1);
 2086           }
 2087   
 2088           return true;
 2089       }
 2090   
 2091       private LdapResult compare(Name name, String type, String value)
 2092           throws IOException, NamingException {
 2093   
 2094           ensureOpen();
 2095           String nm = fullyQualifiedName(name);
 2096   
 2097           LdapResult answer = clnt.compare(nm, type, value, reqCtls);
 2098           respCtls = answer.resControls; // retrieve response controls
 2099   
 2100           return answer;
 2101       }
 2102   
 2103       private static SearchControls cloneSearchControls(SearchControls cons) {
 2104           if (cons == null) {
 2105               return null;
 2106           }
 2107           String[] retAttrs = cons.getReturningAttributes();
 2108           if (retAttrs != null) {
 2109               String[] attrs = new String[retAttrs.length];
 2110               System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length);
 2111               retAttrs = attrs;
 2112           }
 2113           return new SearchControls(cons.getSearchScope(),
 2114                                     cons.getCountLimit(),
 2115                                     cons.getTimeLimit(),
 2116                                     retAttrs,
 2117                                     cons.getReturningObjFlag(),
 2118                                     cons.getDerefLinkFlag());
 2119       }
 2120   
 2121      // -------------- Environment Properties ------------------
 2122   
 2123       /**
 2124        * Override with noncloning version.
 2125        */
 2126       protected Hashtable p_getEnvironment() {
 2127           return envprops;
 2128       }
 2129   
 2130       public Hashtable getEnvironment() throws NamingException {
 2131           return (envprops == null
 2132                   ? new Hashtable(5, 0.75f)
 2133                   : (Hashtable)envprops.clone());
 2134       }
 2135   
 2136       public Object removeFromEnvironment(String propName)
 2137           throws NamingException {
 2138   
 2139           // not there; just return
 2140           if (envprops == null || envprops.get(propName) == null) {
 2141               return null;
 2142           }
 2143   
 2144           if (propName.equals(REF_SEPARATOR)) {
 2145               addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
 2146           } else if (propName.equals(TYPES_ONLY)) {
 2147               typesOnly = DEFAULT_TYPES_ONLY;
 2148           } else if (propName.equals(DELETE_RDN)) {
 2149               deleteRDN = DEFAULT_DELETE_RDN;
 2150           } else if (propName.equals(DEREF_ALIASES)) {
 2151               derefAliases = DEFAULT_DEREF_ALIASES;
 2152           } else if (propName.equals(Context.BATCHSIZE)) {
 2153               batchSize = DEFAULT_BATCH_SIZE;
 2154           } else if (propName.equals(REFERRAL_LIMIT)) {
 2155               referralHopLimit = DEFAULT_REFERRAL_LIMIT;
 2156           } else if (propName.equals(Context.REFERRAL)) {
 2157               setReferralMode(null, true);
 2158           } else if (propName.equals(BINARY_ATTRIBUTES)) {
 2159               setBinaryAttributes(null);
 2160           } else if (propName.equals(CONNECT_TIMEOUT)) {
 2161               connectTimeout = -1;
 2162           } else if (propName.equals(READ_TIMEOUT)) {
 2163               readTimeout = -1;
 2164   
 2165   // The following properties affect the connection
 2166   
 2167           } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
 2168               closeConnection(SOFT_CLOSE);
 2169               // De-activate SSL and reset the context's url and port number
 2170               if (useSsl && !hasLdapsScheme) {
 2171                   useSsl = false;
 2172                   url = null;
 2173                   if (useDefaultPortNumber) {
 2174                       port_number = DEFAULT_PORT;
 2175                   }
 2176               }
 2177           } else if (propName.equals(VERSION) ||
 2178               propName.equals(SOCKET_FACTORY)) {
 2179               closeConnection(SOFT_CLOSE);
 2180           } else if(propName.equals(Context.SECURITY_AUTHENTICATION) ||
 2181               propName.equals(Context.SECURITY_PRINCIPAL) ||
 2182               propName.equals(Context.SECURITY_CREDENTIALS)) {
 2183               sharable = false;
 2184           }
 2185   
 2186           // Update environment; reconnection will use new props
 2187           envprops = (Hashtable)envprops.clone();
 2188           return envprops.remove(propName);
 2189       }
 2190   
 2191       public Object addToEnvironment(String propName, Object propVal)
 2192           throws NamingException {
 2193   
 2194               // If adding null, call remove
 2195               if (propVal == null) {
 2196                   return removeFromEnvironment(propName);
 2197               }
 2198   
 2199               if (propName.equals(REF_SEPARATOR)) {
 2200                   setRefSeparator((String)propVal);
 2201               } else if (propName.equals(TYPES_ONLY)) {
 2202                   setTypesOnly((String)propVal);
 2203               } else if (propName.equals(DELETE_RDN)) {
 2204                   setDeleteRDN((String)propVal);
 2205               } else if (propName.equals(DEREF_ALIASES)) {
 2206                   setDerefAliases((String)propVal);
 2207               } else if (propName.equals(Context.BATCHSIZE)) {
 2208                   setBatchSize((String)propVal);
 2209               } else if (propName.equals(REFERRAL_LIMIT)) {
 2210                   setReferralLimit((String)propVal);
 2211               } else if (propName.equals(Context.REFERRAL)) {
 2212                   setReferralMode((String)propVal, true);
 2213               } else if (propName.equals(BINARY_ATTRIBUTES)) {
 2214                   setBinaryAttributes((String)propVal);
 2215               } else if (propName.equals(CONNECT_TIMEOUT)) {
 2216                   setConnectTimeout((String)propVal);
 2217               } else if (propName.equals(READ_TIMEOUT)) {
 2218                   setReadTimeout((String)propVal);
 2219   // The following properties affect the connection
 2220   
 2221               } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
 2222                   closeConnection(SOFT_CLOSE);
 2223                   // Activate SSL and reset the context's url and port number
 2224                   if ("ssl".equals(propVal)) {
 2225                       useSsl = true;
 2226                       url = null;
 2227                       if (useDefaultPortNumber) {
 2228                           port_number = DEFAULT_SSL_PORT;
 2229                       }
 2230                   }
 2231               } else if (propName.equals(VERSION) ||
 2232                   propName.equals(SOCKET_FACTORY)) {
 2233                   closeConnection(SOFT_CLOSE);
 2234               } else if (propName.equals(Context.SECURITY_AUTHENTICATION) ||
 2235                   propName.equals(Context.SECURITY_PRINCIPAL) ||
 2236                   propName.equals(Context.SECURITY_CREDENTIALS)) {
 2237                   sharable = false;
 2238               }
 2239   
 2240               // Update environment; reconnection will use new props
 2241               envprops = (envprops == null
 2242                   ? new Hashtable(5, 0.75f)
 2243                   : (Hashtable)envprops.clone());
 2244               return envprops.put(propName, propVal);
 2245       }
 2246   
 2247       /**
 2248        * Sets the URL that created the context in the java.naming.provider.url
 2249        * property.
 2250        */
 2251       void setProviderUrl(String providerUrl) { // called by LdapCtxFactory
 2252           if (envprops != null) {
 2253               envprops.put(Context.PROVIDER_URL, providerUrl);
 2254           }
 2255       }
 2256   
 2257       /**
 2258        * Sets the domain name for the context in the com.sun.jndi.ldap.domainname
 2259        * property.
 2260        * Used for hostname verification by Start TLS
 2261        */
 2262       void setDomainName(String domainName) { // called by LdapCtxFactory
 2263           if (envprops != null) {
 2264               envprops.put(DOMAIN_NAME, domainName);
 2265           }
 2266       }
 2267   
 2268       private void initEnv() throws NamingException {
 2269           if (envprops == null) {
 2270               // Make sure that referrals are to their default
 2271               setReferralMode(null, false);
 2272               return;
 2273           }
 2274   
 2275           // Set batch size
 2276           setBatchSize((String)envprops.get(Context.BATCHSIZE));
 2277   
 2278           // Set separator used for encoding RefAddr
 2279           setRefSeparator((String)envprops.get(REF_SEPARATOR));
 2280   
 2281           // Set whether RDN is removed when renaming object
 2282           setDeleteRDN((String)envprops.get(DELETE_RDN));
 2283   
 2284           // Set whether types are returned only
 2285           setTypesOnly((String)envprops.get(TYPES_ONLY));
 2286   
 2287           // Set how aliases are dereferenced
 2288           setDerefAliases((String)envprops.get(DEREF_ALIASES));
 2289   
 2290           // Set the limit on referral chains
 2291           setReferralLimit((String)envprops.get(REFERRAL_LIMIT));
 2292   
 2293           setBinaryAttributes((String)envprops.get(BINARY_ATTRIBUTES));
 2294   
 2295           bindCtls = cloneControls((Control[]) envprops.get(BIND_CONTROLS));
 2296   
 2297           // set referral handling
 2298           setReferralMode((String)envprops.get(Context.REFERRAL), false);
 2299   
 2300           // Set the connect timeout
 2301           setConnectTimeout((String)envprops.get(CONNECT_TIMEOUT));
 2302   
 2303           // Set the read timeout
 2304           setReadTimeout((String)envprops.get(READ_TIMEOUT));
 2305   
 2306           // When connection is created, it will use these and other
 2307           // properties from the environment
 2308       }
 2309   
 2310       private void setDeleteRDN(String deleteRDNProp) {
 2311           if ((deleteRDNProp != null) &&
 2312               (deleteRDNProp.equalsIgnoreCase("false"))) {
 2313               deleteRDN = false;
 2314           } else {
 2315               deleteRDN = DEFAULT_DELETE_RDN;
 2316           }
 2317       }
 2318   
 2319       private void setTypesOnly(String typesOnlyProp) {
 2320           if ((typesOnlyProp != null) &&
 2321               (typesOnlyProp.equalsIgnoreCase("true"))) {
 2322               typesOnly = true;
 2323           } else {
 2324               typesOnly = DEFAULT_TYPES_ONLY;
 2325           }
 2326       }
 2327   
 2328       /**
 2329        * Sets the batch size of this context;
 2330        */
 2331       private void setBatchSize(String batchSizeProp) {
 2332           // set batchsize
 2333           if (batchSizeProp != null) {
 2334               batchSize = Integer.parseInt(batchSizeProp);
 2335           } else {
 2336               batchSize = DEFAULT_BATCH_SIZE;
 2337           }
 2338       }
 2339   
 2340       /**
 2341        * Sets the referral mode of this context to 'follow', 'throw' or 'ignore'.
 2342        * If referral mode is 'ignore' then activate the manageReferral control.
 2343        */
 2344       private void setReferralMode(String ref, boolean update) {
 2345           // First determine the referral mode
 2346           if (ref != null) {
 2347               if (ref.equals("follow")) {
 2348                   handleReferrals = LdapClient.LDAP_REF_FOLLOW;
 2349               } else if (ref.equals("throw")) {
 2350                   handleReferrals = LdapClient.LDAP_REF_THROW;
 2351               } else if (ref.equals("ignore")) {
 2352                   handleReferrals = LdapClient.LDAP_REF_IGNORE;
 2353               } else {
 2354                   throw new IllegalArgumentException(
 2355                       "Illegal value for " + Context.REFERRAL + " property.");
 2356               }
 2357           } else {
 2358               handleReferrals = DEFAULT_REFERRAL_MODE;
 2359           }
 2360   
 2361           if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
 2362               // If ignoring referrals, add manageReferralControl
 2363               reqCtls = addControl(reqCtls, manageReferralControl);
 2364   
 2365           } else if (update) {
 2366   
 2367               // If we're update an existing context, remove the control
 2368               reqCtls = removeControl(reqCtls, manageReferralControl);
 2369   
 2370           } // else, leave alone; need not update
 2371       }
 2372   
 2373       /**
 2374        * Set whether aliases are derefereced during resolution and searches.
 2375        */
 2376       private void setDerefAliases(String deref) {
 2377           if (deref != null) {
 2378               if (deref.equals("never")) {
 2379                   derefAliases = 0; // never de-reference aliases
 2380               } else if (deref.equals("searching")) {
 2381                   derefAliases = 1; // de-reference aliases during searching
 2382               } else if (deref.equals("finding")) {
 2383                   derefAliases = 2; // de-reference during name resolution
 2384               } else if (deref.equals("always")) {
 2385                   derefAliases = 3; // always de-reference aliases
 2386               } else {
 2387                   throw new IllegalArgumentException("Illegal value for " +
 2388                       DEREF_ALIASES + " property.");
 2389               }
 2390           } else {
 2391               derefAliases = DEFAULT_DEREF_ALIASES;
 2392           }
 2393       }
 2394   
 2395       private void setRefSeparator(String sepStr) throws NamingException {
 2396           if (sepStr != null && sepStr.length() > 0) {
 2397               addrEncodingSeparator = sepStr.charAt(0);
 2398           } else {
 2399               addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
 2400           }
 2401       }
 2402   
 2403       /**
 2404        * Sets the limit on referral chains
 2405        */
 2406       private void setReferralLimit(String referralLimitProp) {
 2407           // set referral limit
 2408           if (referralLimitProp != null) {
 2409               referralHopLimit = Integer.parseInt(referralLimitProp);
 2410   
 2411               // a zero setting indicates no limit
 2412               if (referralHopLimit == 0)
 2413                   referralHopLimit = Integer.MAX_VALUE;
 2414           } else {
 2415               referralHopLimit = DEFAULT_REFERRAL_LIMIT;
 2416           }
 2417       }
 2418   
 2419       // For counting referral hops
 2420       void setHopCount(int hopCount) {
 2421           this.hopCount = hopCount;
 2422       }
 2423   
 2424       /**
 2425        * Sets the connect timeout value
 2426        */
 2427       private void setConnectTimeout(String connectTimeoutProp) {
 2428           if (connectTimeoutProp != null) {
 2429               connectTimeout = Integer.parseInt(connectTimeoutProp);
 2430           } else {
 2431               connectTimeout = -1;
 2432           }
 2433       }
 2434   
 2435       /**
 2436        * Sets the read timeout value
 2437        */
 2438       private void setReadTimeout(String readTimeoutProp) {
 2439           if (readTimeoutProp != null) {
 2440              readTimeout = Integer.parseInt(readTimeoutProp);
 2441           } else {
 2442               readTimeout = -1;
 2443           }
 2444       }
 2445   
 2446       /*
 2447        * Extract URLs from a string. The format of the string is:
 2448        *
 2449        *     <urlstring > ::= "Referral:" <ldapurls>
 2450        *     <ldapurls>   ::= <separator> <ldapurl> | <ldapurls>
 2451        *     <separator>  ::= ASCII linefeed character (0x0a)
 2452        *     <ldapurl>    ::= LDAP URL format (RFC 1959)
 2453        */
 2454       private static Vector extractURLs(String refString) {
 2455   
 2456           int separator = 0;
 2457           int urlCount = 0;
 2458   
 2459           // count the number of URLs
 2460           while ((separator = refString.indexOf('\n', separator)) >= 0) {
 2461               separator++;
 2462               urlCount++;
 2463           }
 2464   
 2465           Vector referrals = new Vector(urlCount);
 2466           int iURL;
 2467           int i = 0;
 2468   
 2469           separator = refString.indexOf('\n');
 2470           iURL = separator + 1;
 2471           while ((separator = refString.indexOf('\n', iURL)) >= 0) {
 2472               referrals.addElement(refString.substring(iURL, separator));
 2473               iURL = separator + 1;
 2474           }
 2475           referrals.addElement(refString.substring(iURL));
 2476   
 2477           return referrals;
 2478       }
 2479   
 2480       /*
 2481        * Argument is a space-separated list of attribute IDs
 2482        * Converts attribute IDs to lowercase before adding to built-in list.
 2483        */
 2484       private void setBinaryAttributes(String attrIds) {
 2485           if (attrIds == null) {
 2486               binaryAttrs = null;
 2487           } else {
 2488               binaryAttrs = new Hashtable(11, 0.75f);
 2489               StringTokenizer tokens =
 2490                   new StringTokenizer(attrIds.toLowerCase(), " ");
 2491   
 2492               while (tokens.hasMoreTokens()) {
 2493                   binaryAttrs.put(tokens.nextToken(), Boolean.TRUE);
 2494               }
 2495           }
 2496       }
 2497   
 2498      // ----------------- Connection  ---------------------
 2499   
 2500       protected void finalize() {
 2501           try {
 2502               close();
 2503           } catch (NamingException e) {
 2504               // ignore failures
 2505           }
 2506       }
 2507   
 2508       synchronized public void close() throws NamingException {
 2509           if (debug) {
 2510               System.err.println("LdapCtx: close() called " + this);
 2511               (new Throwable()).printStackTrace();
 2512           }
 2513   
 2514           // Event (normal and unsolicited)
 2515           if (eventSupport != null) {
 2516               eventSupport.cleanup(); // idempotent
 2517               removeUnsolicited();
 2518           }
 2519   
 2520           // Enumerations that are keeping the connection alive
 2521           if (enumCount > 0) {
 2522               if (debug)
 2523                   System.err.println("LdapCtx: close deferred");
 2524               closeRequested = true;
 2525               return;
 2526           }
 2527           closeConnection(SOFT_CLOSE);
 2528   
 2529   // %%%: RL: There is no need to set these to null, as they're just
 2530   // variables whose contents and references will automatically
 2531   // be cleaned up when they're no longer referenced.
 2532   // Also, setting these to null creates problems for the attribute
 2533   // schema-related methods, which need these to work.
 2534   /*
 2535           schemaTrees = null;
 2536           envprops = null;
 2537   */
 2538       }
 2539   
 2540       public void reconnect(Control[] connCtls) throws NamingException {
 2541           // Update environment
 2542           envprops = (envprops == null
 2543                   ? new Hashtable(5, 0.75f)
 2544                   : (Hashtable)envprops.clone());
 2545   
 2546           if (connCtls == null) {
 2547               envprops.remove(BIND_CONTROLS);
 2548               bindCtls = null;
 2549           } else {
 2550               envprops.put(BIND_CONTROLS, bindCtls = cloneControls(connCtls));
 2551           }
 2552   
 2553           sharable = false;  // can't share with existing contexts
 2554           ensureOpen();      // open or reauthenticated
 2555       }
 2556   
 2557       private void ensureOpen() throws NamingException {
 2558           ensureOpen(false);
 2559       }
 2560   
 2561       private void ensureOpen(boolean startTLS) throws NamingException {
 2562   
 2563           try {
 2564               if (clnt == null) {
 2565                   if (debug) {
 2566                       System.err.println("LdapCtx: Reconnecting " + this);
 2567                   }
 2568   
 2569                   // reset the cache before a new connection is established
 2570                   schemaTrees = new Hashtable(11, 0.75f);
 2571                   connect(startTLS);
 2572   
 2573               } else if (!sharable || startTLS) {
 2574   
 2575                   synchronized (clnt) {
 2576                       if (!clnt.isLdapv3
 2577                           || clnt.referenceCount > 1
 2578                           || clnt.usingSaslStreams()) {
 2579                           closeConnection(SOFT_CLOSE);
 2580                       }
 2581                   }
 2582                   // reset the cache before a new connection is established
 2583                   schemaTrees = new Hashtable(11, 0.75f);
 2584                   connect(startTLS);
 2585               }
 2586   
 2587           } finally {
 2588               sharable = true;   // connection is now either new or single-use
 2589                                  // OK for others to start sharing again
 2590           }
 2591       }
 2592   
 2593       private void connect(boolean startTLS) throws NamingException {
 2594           if (debug) { System.err.println("LdapCtx: Connecting " + this); }
 2595   
 2596           String user = null;             // authenticating user
 2597           Object passwd = null;           // password for authenticating user
 2598           String secProtocol = null;      // security protocol (e.g. "ssl")
 2599           String socketFactory = null;    // socket factory
 2600           String authMechanism = null;    // authentication mechanism
 2601           String ver = null;
 2602           int ldapVersion;                // LDAP protocol version
 2603           boolean usePool = false;        // enable connection pooling
 2604   
 2605           if (envprops != null) {
 2606               user = (String)envprops.get(Context.SECURITY_PRINCIPAL);
 2607               passwd = envprops.get(Context.SECURITY_CREDENTIALS);
 2608               ver = (String)envprops.get(VERSION);
 2609               secProtocol =
 2610                  useSsl ? "ssl" : (String)envprops.get(Context.SECURITY_PROTOCOL);
 2611               socketFactory = (String)envprops.get(SOCKET_FACTORY);
 2612               authMechanism =
 2613                   (String)envprops.get(Context.SECURITY_AUTHENTICATION);
 2614   
 2615               usePool = "true".equalsIgnoreCase((String)envprops.get(ENABLE_POOL));
 2616           }
 2617   
 2618           if (socketFactory == null) {
 2619               socketFactory =
 2620                   "ssl".equals(secProtocol) ? DEFAULT_SSL_FACTORY : null;
 2621           }
 2622   
 2623           if (authMechanism == null) {
 2624               authMechanism = (user == null) ? "none" : "simple";
 2625           }
 2626   
 2627           try {
 2628               boolean initial = (clnt == null);
 2629   
 2630               if (initial) {
 2631                   ldapVersion = (ver != null) ? Integer.parseInt(ver) :
 2632                       DEFAULT_LDAP_VERSION;
 2633   
 2634                   clnt = LdapClient.getInstance(
 2635                       usePool, // Whether to use connection pooling
 2636   
 2637                       // Required for LdapClient constructor
 2638                       hostname,
 2639                       port_number,
 2640                       socketFactory,
 2641                       connectTimeout,
 2642                       readTimeout,
 2643                       trace,
 2644   
 2645                       // Required for basic client identity
 2646                       ldapVersion,
 2647                       authMechanism,
 2648                       bindCtls,
 2649                       secProtocol,
 2650   
 2651                       // Required for simple client identity
 2652                       user,
 2653                       passwd,
 2654   
 2655                       // Required for SASL client identity
 2656                       envprops);
 2657   
 2658   
 2659                   /**
 2660                    * Pooled connections are preauthenticated;
 2661                    * newly created ones are not.
 2662                    */
 2663                   if (clnt.authenticateCalled()) {
 2664                       return;
 2665                   }
 2666   
 2667               } else if (sharable && startTLS) {
 2668                   return; // no authentication required
 2669   
 2670               } else {
 2671                   // reauthenticating over existing connection;
 2672                   // only v3 supports this
 2673                   ldapVersion = LdapClient.LDAP_VERSION3;
 2674               }
 2675   
 2676               LdapResult answer = clnt.authenticate(initial,
 2677                   user, passwd, ldapVersion, authMechanism, bindCtls, envprops);
 2678   
 2679               respCtls = answer.resControls; // retrieve (bind) response controls
 2680   
 2681               if (answer.status != LdapClient.LDAP_SUCCESS) {
 2682                   if (initial) {
 2683                       closeConnection(HARD_CLOSE);  // hard close
 2684                   }
 2685                   processReturnCode(answer);
 2686               }
 2687   
 2688           } catch (LdapReferralException e) {
 2689               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 2690                   throw e;
 2691   
 2692               String referral;
 2693               LdapURL url;
 2694               NamingException saved_ex = null;
 2695   
 2696               // Process the referrals sequentially (top level) and
 2697               // recursively (per referral)
 2698               while (true) {
 2699   
 2700                   if ((referral = e.getNextReferral()) == null) {
 2701                       // No more referrals to follow
 2702   
 2703                       if (saved_ex != null) {
 2704                           throw (NamingException)(saved_ex.fillInStackTrace());
 2705                       } else {
 2706                           // No saved exception, something must have gone wrong
 2707                           throw new NamingException(
 2708                           "Internal error processing referral during connection");
 2709                       }
 2710                   }
 2711   
 2712                   // Use host/port number from referral
 2713                   url = new LdapURL(referral);
 2714                   hostname = url.getHost();
 2715                   if ((hostname != null) && (hostname.charAt(0) == '[')) {
 2716                       hostname = hostname.substring(1, hostname.length() - 1);
 2717                   }
 2718                   port_number = url.getPort();
 2719   
 2720                   // Try to connect again using new host/port number
 2721                   try {
 2722                       connect(startTLS);
 2723                       break;
 2724   
 2725                   } catch (NamingException ne) {
 2726                       saved_ex = ne;
 2727                       continue; // follow another referral
 2728                   }
 2729               }
 2730           }
 2731       }
 2732   
 2733       private void closeConnection(boolean hardclose) {
 2734           removeUnsolicited();            // idempotent
 2735   
 2736           if (clnt != null) {
 2737               if (debug) {
 2738                   System.err.println("LdapCtx: calling clnt.close() " + this);
 2739               }
 2740               clnt.close(reqCtls, hardclose);
 2741               clnt = null;
 2742           }
 2743       }
 2744   
 2745       // Used by Enum classes to track whether it still needs context
 2746       private int enumCount = 0;
 2747       private boolean closeRequested = false;
 2748   
 2749       synchronized void incEnumCount() {
 2750           ++enumCount;
 2751           if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount);
 2752       }
 2753   
 2754       synchronized void decEnumCount() {
 2755           --enumCount;
 2756           if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount);
 2757   
 2758           if (enumCount == 0 && closeRequested) {
 2759               try {
 2760                   close();
 2761               } catch (NamingException e) {
 2762                   // ignore failures
 2763               }
 2764           }
 2765       }
 2766   
 2767   
 2768      // ------------ Return code and Error messages  -----------------------
 2769   
 2770       protected void processReturnCode(LdapResult answer) throws NamingException {
 2771           processReturnCode(answer, null, this, null, envprops, null);
 2772       }
 2773   
 2774       void processReturnCode(LdapResult answer, Name remainName)
 2775       throws NamingException {
 2776           processReturnCode(answer,
 2777                             (new CompositeName()).add(currentDN),
 2778                             this,
 2779                             remainName,
 2780                             envprops,
 2781                             fullyQualifiedName(remainName));
 2782       }
 2783   
 2784       protected void processReturnCode(LdapResult res, Name resolvedName,
 2785           Object resolvedObj, Name remainName, Hashtable envprops, String fullDN)
 2786       throws NamingException {
 2787   
 2788           String msg = LdapClient.getErrorMessage(res.status, res.errorMessage);
 2789           NamingException e;
 2790           LdapReferralException r = null;
 2791   
 2792           switch (res.status) {
 2793   
 2794           case LdapClient.LDAP_SUCCESS:
 2795   
 2796               // handle Search continuation references
 2797               if (res.referrals != null) {
 2798   
 2799                   msg = "Unprocessed Continuation Reference(s)";
 2800   
 2801                   if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
 2802                       e = new PartialResultException(msg);
 2803                       break;
 2804                   }
 2805   
 2806                   // handle multiple sets of URLs
 2807                   int contRefCount = res.referrals.size();
 2808                   LdapReferralException head = null;
 2809                   LdapReferralException ptr = null;
 2810   
 2811                   msg = "Continuation Reference";
 2812   
 2813                   // make a chain of LdapReferralExceptions
 2814                   for (int i = 0; i < contRefCount; i++) {
 2815   
 2816                       r = new LdapReferralException(resolvedName, resolvedObj,
 2817                           remainName, msg, envprops, fullDN, handleReferrals,
 2818                           reqCtls);
 2819                       r.setReferralInfo((Vector)res.referrals.elementAt(i), true);
 2820   
 2821                       if (hopCount > 1) {
 2822                           r.setHopCount(hopCount);
 2823                       }
 2824   
 2825                       if (head == null) {
 2826                           head = ptr = r;
 2827                       } else {
 2828                           ptr.nextReferralEx = r; // append ex. to end of chain
 2829                           ptr = r;
 2830                       }
 2831                   }
 2832                   res.referrals = null;  // reset
 2833   
 2834                   if (res.refEx == null) {
 2835                       res.refEx = head;
 2836   
 2837                   } else {
 2838                       ptr = res.refEx;
 2839   
 2840                       while (ptr.nextReferralEx != null) {
 2841                           ptr = ptr.nextReferralEx;
 2842                       }
 2843                       ptr.nextReferralEx = head;
 2844                   }
 2845   
 2846                   // check the hop limit
 2847                   if (hopCount > referralHopLimit) {
 2848                       NamingException lee =
 2849                           new LimitExceededException("Referral limit exceeded");
 2850                       lee.setRootCause(r);
 2851                       throw lee;
 2852                   }
 2853               }
 2854               return;
 2855   
 2856           case LdapClient.LDAP_REFERRAL:
 2857   
 2858               if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
 2859                   e = new PartialResultException(msg);
 2860                   break;
 2861               }
 2862   
 2863               r = new LdapReferralException(resolvedName, resolvedObj, remainName,
 2864                   msg, envprops, fullDN, handleReferrals, reqCtls);
 2865               // only one set of URLs is present
 2866               r.setReferralInfo((Vector)res.referrals.elementAt(0), false);
 2867   
 2868               if (hopCount > 1) {
 2869                   r.setHopCount(hopCount);
 2870               }
 2871   
 2872               // check the hop limit
 2873               if (hopCount > referralHopLimit) {
 2874                   NamingException lee =
 2875                       new LimitExceededException("Referral limit exceeded");
 2876                   lee.setRootCause(r);
 2877                   e = lee;
 2878   
 2879               } else {
 2880                   e = r;
 2881               }
 2882               break;
 2883   
 2884           /*
 2885            * Handle SLAPD-style referrals.
 2886            *
 2887            * Referrals received during name resolution should be followed
 2888            * until one succeeds - the target entry is located. An exception
 2889            * is thrown now to handle these.
 2890            *
 2891            * Referrals received during a search operation point to unexplored
 2892            * parts of the directory and each should be followed. An exception
 2893            * is thrown later (during results enumeration) to handle these.
 2894            */
 2895   
 2896           case LdapClient.LDAP_PARTIAL_RESULTS:
 2897   
 2898               if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
 2899                   e = new PartialResultException(msg);
 2900                   break;
 2901               }
 2902   
 2903               // extract SLAPD-style referrals from errorMessage
 2904               if ((res.errorMessage != null) && (!res.errorMessage.equals(""))) {
 2905                   res.referrals = extractURLs(res.errorMessage);
 2906               } else {
 2907                   e = new PartialResultException(msg);
 2908                   break;
 2909               }
 2910   
 2911               // build exception
 2912               r = new LdapReferralException(resolvedName,
 2913                   resolvedObj,
 2914                   remainName,
 2915                   msg,
 2916                   envprops,
 2917                   fullDN,
 2918                   handleReferrals,
 2919                   reqCtls);
 2920   
 2921               if (hopCount > 1) {
 2922                   r.setHopCount(hopCount);
 2923               }
 2924               /*
 2925                * %%%
 2926                * SLAPD-style referrals received during name resolution
 2927                * cannot be distinguished from those received during a
 2928                * search operation. Since both must be handled differently
 2929                * the following rule is applied:
 2930                *
 2931                *     If 1 referral and 0 entries is received then
 2932                *     assume name resolution has not yet completed.
 2933                */
 2934               if (((res.entries == null) || (res.entries.size() == 0)) &&
 2935                   (res.referrals.size() == 1)) {
 2936   
 2937                   r.setReferralInfo((Vector)res.referrals, false);
 2938   
 2939                   // check the hop limit
 2940                   if (hopCount > referralHopLimit) {
 2941                       NamingException lee =
 2942                           new LimitExceededException("Referral limit exceeded");
 2943                       lee.setRootCause(r);
 2944                       e = lee;
 2945   
 2946                   } else {
 2947                       e = r;
 2948                   }
 2949   
 2950               } else {
 2951                   r.setReferralInfo(res.referrals, true);
 2952                   res.refEx = r;
 2953                   return;
 2954               }
 2955               break;
 2956   
 2957           case LdapClient.LDAP_INVALID_DN_SYNTAX:
 2958           case LdapClient.LDAP_NAMING_VIOLATION:
 2959   
 2960               if (remainName != null) {
 2961                   e = new
 2962                       InvalidNameException(remainName.toString() + ": " + msg);
 2963               } else {
 2964                   e = new InvalidNameException(msg);
 2965               }
 2966               break;
 2967   
 2968           default:
 2969               e = mapErrorCode(res.status, res.errorMessage);
 2970               break;
 2971           }
 2972           e.setResolvedName(resolvedName);
 2973           e.setResolvedObj(resolvedObj);
 2974           e.setRemainingName(remainName);
 2975           throw e;
 2976       }
 2977   
 2978       /**
 2979        * Maps an LDAP error code to an appropriate NamingException.
 2980        * %%% public; used by controls
 2981        *
 2982        * @param errorCode numeric LDAP error code
 2983        * @param errorMessage textual description of the LDAP error. May be null.
 2984        *
 2985        * @return A NamingException or null if the error code indicates success.
 2986        */
 2987       public static NamingException mapErrorCode(int errorCode,
 2988           String errorMessage) {
 2989   
 2990           if (errorCode == LdapClient.LDAP_SUCCESS)
 2991               return null;
 2992   
 2993           NamingException e = null;
 2994           String message = LdapClient.getErrorMessage(errorCode, errorMessage);
 2995   
 2996           switch (errorCode) {
 2997   
 2998           case LdapClient.LDAP_ALIAS_DEREFERENCING_PROBLEM:
 2999               e = new NamingException(message);
 3000               break;
 3001   
 3002           case LdapClient.LDAP_ALIAS_PROBLEM:
 3003               e = new NamingException(message);
 3004               break;
 3005   
 3006           case LdapClient.LDAP_ATTRIBUTE_OR_VALUE_EXISTS:
 3007               e = new AttributeInUseException(message);
 3008               break;
 3009   
 3010           case LdapClient.LDAP_AUTH_METHOD_NOT_SUPPORTED:
 3011           case LdapClient.LDAP_CONFIDENTIALITY_REQUIRED:
 3012           case LdapClient.LDAP_STRONG_AUTH_REQUIRED:
 3013           case LdapClient.LDAP_INAPPROPRIATE_AUTHENTICATION:
 3014               e = new AuthenticationNotSupportedException(message);
 3015               break;
 3016   
 3017           case LdapClient.LDAP_ENTRY_ALREADY_EXISTS:
 3018               e = new NameAlreadyBoundException(message);
 3019               break;
 3020   
 3021           case LdapClient.LDAP_INVALID_CREDENTIALS:
 3022           case LdapClient.LDAP_SASL_BIND_IN_PROGRESS:
 3023               e = new AuthenticationException(message);
 3024               break;
 3025   
 3026           case LdapClient.LDAP_INAPPROPRIATE_MATCHING:
 3027               e = new InvalidSearchFilterException(message);
 3028               break;
 3029   
 3030           case LdapClient.LDAP_INSUFFICIENT_ACCESS_RIGHTS:
 3031               e = new NoPermissionException(message);
 3032               break;
 3033   
 3034           case LdapClient.LDAP_INVALID_ATTRIBUTE_SYNTAX:
 3035           case LdapClient.LDAP_CONSTRAINT_VIOLATION:
 3036               e =  new InvalidAttributeValueException(message);
 3037               break;
 3038   
 3039           case LdapClient.LDAP_LOOP_DETECT:
 3040               e = new NamingException(message);
 3041               break;
 3042   
 3043           case LdapClient.LDAP_NO_SUCH_ATTRIBUTE:
 3044               e = new NoSuchAttributeException(message);
 3045               break;
 3046   
 3047           case LdapClient.LDAP_NO_SUCH_OBJECT:
 3048               e = new NameNotFoundException(message);
 3049               break;
 3050   
 3051           case LdapClient.LDAP_OBJECT_CLASS_MODS_PROHIBITED:
 3052           case LdapClient.LDAP_OBJECT_CLASS_VIOLATION:
 3053           case LdapClient.LDAP_NOT_ALLOWED_ON_RDN:
 3054               e = new SchemaViolationException(message);
 3055               break;
 3056   
 3057           case LdapClient.LDAP_NOT_ALLOWED_ON_NON_LEAF:
 3058               e = new ContextNotEmptyException(message);
 3059               break;
 3060   
 3061           case LdapClient.LDAP_OPERATIONS_ERROR:
 3062               // %%% need new exception ?
 3063               e = new NamingException(message);
 3064               break;
 3065   
 3066           case LdapClient.LDAP_OTHER:
 3067               e = new NamingException(message);
 3068               break;
 3069   
 3070           case LdapClient.LDAP_PROTOCOL_ERROR:
 3071               e = new CommunicationException(message);
 3072               break;
 3073   
 3074           case LdapClient.LDAP_SIZE_LIMIT_EXCEEDED:
 3075               e = new SizeLimitExceededException(message);
 3076               break;
 3077   
 3078           case LdapClient.LDAP_TIME_LIMIT_EXCEEDED:
 3079               e = new TimeLimitExceededException(message);
 3080               break;
 3081   
 3082           case LdapClient.LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
 3083               e = new OperationNotSupportedException(message);
 3084               break;
 3085   
 3086           case LdapClient.LDAP_UNAVAILABLE:
 3087           case LdapClient.LDAP_BUSY:
 3088               e = new ServiceUnavailableException(message);
 3089               break;
 3090   
 3091           case LdapClient.LDAP_UNDEFINED_ATTRIBUTE_TYPE:
 3092               e = new InvalidAttributeIdentifierException(message);
 3093               break;
 3094   
 3095           case LdapClient.LDAP_UNWILLING_TO_PERFORM:
 3096               e = new OperationNotSupportedException(message);
 3097               break;
 3098   
 3099           case LdapClient.LDAP_COMPARE_FALSE:
 3100           case LdapClient.LDAP_COMPARE_TRUE:
 3101           case LdapClient.LDAP_IS_LEAF:
 3102               // these are really not exceptions and this code probably
 3103               // never gets executed
 3104               e = new NamingException(message);
 3105               break;
 3106   
 3107           case LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED:
 3108               e = new LimitExceededException(message);
 3109               break;
 3110   
 3111           case LdapClient.LDAP_REFERRAL:
 3112               e = new NamingException(message);
 3113               break;
 3114   
 3115           case LdapClient.LDAP_PARTIAL_RESULTS:
 3116               e = new NamingException(message);
 3117               break;
 3118   
 3119           case LdapClient.LDAP_INVALID_DN_SYNTAX:
 3120           case LdapClient.LDAP_NAMING_VIOLATION:
 3121               e = new InvalidNameException(message);
 3122               break;
 3123   
 3124           default:
 3125               e = new NamingException(message);
 3126               break;
 3127           }
 3128   
 3129           return e;
 3130       }
 3131   
 3132       // ----------------- Extensions and Controls -------------------
 3133   
 3134       public ExtendedResponse extendedOperation(ExtendedRequest request)
 3135           throws NamingException {
 3136   
 3137           boolean startTLS = (request.getID().equals(STARTTLS_REQ_OID));
 3138           ensureOpen(startTLS);
 3139   
 3140           try {
 3141   
 3142               LdapResult answer =
 3143                   clnt.extendedOp(request.getID(), request.getEncodedValue(),
 3144                                   reqCtls, startTLS);
 3145               respCtls = answer.resControls; // retrieve response controls
 3146   
 3147               if (answer.status != LdapClient.LDAP_SUCCESS) {
 3148                   processReturnCode(answer, new CompositeName());
 3149               }
 3150               // %%% verify request.getID() == answer.extensionId
 3151   
 3152               int len = (answer.extensionValue == null) ?
 3153                           0 :
 3154                           answer.extensionValue.length;
 3155   
 3156               ExtendedResponse er =
 3157                   request.createExtendedResponse(answer.extensionId,
 3158                       answer.extensionValue, 0, len);
 3159   
 3160               if (er instanceof StartTlsResponseImpl) {
 3161                   // Pass the connection handle to StartTlsResponseImpl
 3162                   String domainName = (String)
 3163                       (envprops != null ? envprops.get(DOMAIN_NAME) : null);
 3164                   ((StartTlsResponseImpl)er).setConnection(clnt.conn, domainName);
 3165               }
 3166               return er;
 3167   
 3168           } catch (LdapReferralException e) {
 3169   
 3170               if (handleReferrals == LdapClient.LDAP_REF_THROW)
 3171                   throw e;
 3172   
 3173               // process the referrals sequentially
 3174               while (true) {
 3175   
 3176                   LdapReferralContext refCtx =
 3177                       (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
 3178   
 3179                   // repeat the original operation at the new context
 3180                   try {
 3181   
 3182                       return refCtx.extendedOperation(request);
 3183   
 3184                   } catch (LdapReferralException re) {
 3185                       e = re;
 3186                       continue;
 3187   
 3188                   } finally {
 3189                       // Make sure we close referral context
 3190                       refCtx.close();
 3191                   }
 3192               }
 3193   
 3194           } catch (IOException e) {
 3195               NamingException e2 = new CommunicationException(e.getMessage());
 3196               e2.setRootCause(e);
 3197               throw e2;
 3198           }
 3199       }
 3200   
 3201       public void setRequestControls(Control[] reqCtls) throws NamingException {
 3202           if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
 3203               this.reqCtls = addControl(reqCtls, manageReferralControl);
 3204           } else {
 3205               this.reqCtls = cloneControls(reqCtls);
 3206           }
 3207       }
 3208   
 3209       public Control[] getRequestControls() throws NamingException {
 3210           return cloneControls(reqCtls);
 3211       }
 3212   
 3213       public Control[] getConnectControls() throws NamingException {
 3214           return cloneControls(bindCtls);
 3215       }
 3216   
 3217       public Control[] getResponseControls() throws NamingException {
 3218           return (respCtls != null)? convertControls(respCtls) : null;
 3219       }
 3220   
 3221       /**
 3222        * Narrow controls using own default factory and ControlFactory.
 3223        * @param ctls A non-null Vector
 3224        */
 3225       Control[] convertControls(Vector ctls) throws NamingException {
 3226           int count = ctls.size();
 3227   
 3228           if (count == 0) {
 3229               return null;
 3230           }
 3231   
 3232           Control[] controls = new Control[count];
 3233   
 3234           for (int i = 0; i < count; i++) {
 3235               // Try own factory first
 3236               controls[i] = myResponseControlFactory.getControlInstance(
 3237                   (Control)ctls.elementAt(i));
 3238   
 3239               // Try assigned factories if own produced null
 3240               if (controls[i] == null) {
 3241                   controls[i] = ControlFactory.getControlInstance(
 3242                   (Control)ctls.elementAt(i), this, envprops);
 3243               }
 3244           }
 3245           return controls;
 3246       }
 3247   
 3248       private static Control[] addControl(Control[] prevCtls, Control addition) {
 3249           if (prevCtls == null) {
 3250               return new Control[]{addition};
 3251           }
 3252   
 3253           // Find it
 3254           int found = findControl(prevCtls, addition);
 3255           if (found != -1) {
 3256               return prevCtls;  // no need to do it again
 3257           }
 3258   
 3259           Control[] newCtls = new Control[prevCtls.length+1];
 3260           System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length);
 3261           newCtls[prevCtls.length] = addition;
 3262           return newCtls;
 3263       }
 3264   
 3265       private static int findControl(Control[] ctls, Control target) {
 3266           for (int i = 0; i < ctls.length; i++) {
 3267               if (ctls[i] == target) {
 3268                   return i;
 3269               }
 3270           }
 3271           return -1;
 3272       }
 3273   
 3274       private static Control[] removeControl(Control[] prevCtls, Control target) {
 3275           if (prevCtls == null) {
 3276               return null;
 3277           }
 3278   
 3279           // Find it
 3280           int found = findControl(prevCtls, target);
 3281           if (found == -1) {
 3282               return prevCtls;  // not there
 3283           }
 3284   
 3285           // Remove it
 3286           Control[] newCtls = new Control[prevCtls.length-1];
 3287           System.arraycopy(prevCtls, 0, newCtls, 0, found);
 3288           System.arraycopy(prevCtls, found+1, newCtls, found,
 3289               prevCtls.length-found-1);
 3290           return newCtls;
 3291       }
 3292   
 3293       private static Control[] cloneControls(Control[] ctls) {
 3294           if (ctls == null) {
 3295               return null;
 3296           }
 3297           Control[] copiedCtls = new Control[ctls.length];
 3298           System.arraycopy(ctls, 0, copiedCtls, 0, ctls.length);
 3299           return copiedCtls;
 3300       }
 3301   
 3302       // -------------------- Events ------------------------
 3303       /*
 3304        * Access to eventSupport need not be synchronized even though the
 3305        * Connection thread can access it asynchronously. It is
 3306        * impossible for a race condition to occur because
 3307        * eventSupport.addNamingListener() must have been called before
 3308        * the Connection thread can call back to this ctx.
 3309        */
 3310       public void addNamingListener(Name nm, int scope, NamingListener l)
 3311           throws NamingException {
 3312               addNamingListener(getTargetName(nm), scope, l);
 3313       }
 3314   
 3315       public void addNamingListener(String nm, int scope, NamingListener l)
 3316           throws NamingException {
 3317               if (eventSupport == null)
 3318                   eventSupport = new EventSupport(this);
 3319               eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
 3320                   scope, l);
 3321   
 3322               // If first time asking for unsol
 3323               if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
 3324                   addUnsolicited();
 3325               }
 3326       }
 3327   
 3328       public void removeNamingListener(NamingListener l) throws NamingException {
 3329           if (eventSupport == null)
 3330               return; // no activity before, so just return
 3331   
 3332           eventSupport.removeNamingListener(l);
 3333   
 3334           // If removing an Unsol listener and it is the last one, let clnt know
 3335           if (l instanceof UnsolicitedNotificationListener &&
 3336               !eventSupport.hasUnsolicited()) {
 3337               removeUnsolicited();
 3338           }
 3339       }
 3340   
 3341       public void addNamingListener(String nm, String filter, SearchControls ctls,
 3342           NamingListener l) throws NamingException {
 3343               if (eventSupport == null)
 3344                   eventSupport = new EventSupport(this);
 3345               eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
 3346                   filter, cloneSearchControls(ctls), l);
 3347   
 3348               // If first time asking for unsol
 3349               if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
 3350                   addUnsolicited();
 3351               }
 3352       }
 3353   
 3354       public void addNamingListener(Name nm, String filter, SearchControls ctls,
 3355           NamingListener l) throws NamingException {
 3356               addNamingListener(getTargetName(nm), filter, ctls, l);
 3357       }
 3358   
 3359       public void addNamingListener(Name nm, String filter, Object[] filterArgs,
 3360           SearchControls ctls, NamingListener l) throws NamingException {
 3361               addNamingListener(getTargetName(nm), filter, filterArgs, ctls, l);
 3362       }
 3363   
 3364       public void addNamingListener(String nm, String filterExpr, Object[] filterArgs,
 3365           SearchControls ctls, NamingListener l) throws NamingException {
 3366           String strfilter = SearchFilter.format(filterExpr, filterArgs);
 3367           addNamingListener(getTargetName(new CompositeName(nm)), strfilter, ctls, l);
 3368       }
 3369   
 3370       public boolean targetMustExist() {
 3371           return true;
 3372       }
 3373   
 3374       /**
 3375        * Retrieves the target name for which the listener is registering.
 3376        * If nm is a CompositeName, use its first and only component. It
 3377        * cannot have more than one components because a target be outside of
 3378        * this namespace. If nm is not a CompositeName, then treat it as a
 3379        * compound name.
 3380        * @param nm The non-null target name.
 3381        */
 3382       private static String getTargetName(Name nm) throws NamingException {
 3383           if (nm instanceof CompositeName) {
 3384               if (nm.size() > 1) {
 3385                   throw new InvalidNameException(
 3386                       "Target cannot span multiple namespaces: " + nm);
 3387               } else if (nm.size() == 0) {
 3388                   return "";
 3389               } else {
 3390                   return nm.get(0);
 3391               }
 3392           } else {
 3393               // treat as compound name
 3394               return nm.toString();
 3395           }
 3396       }
 3397   
 3398       // ------------------ Unsolicited Notification ---------------
 3399       // package private methods for handling unsolicited notification
 3400   
 3401       /**
 3402        * Registers this context with the underlying LdapClient.
 3403        * When the underlying LdapClient receives an unsolicited notification,
 3404        * it will invoke LdapCtx.fireUnsolicited() so that this context
 3405        * can (using EventSupport) notified any registered listeners.
 3406        * This method is called by EventSupport when an unsolicited listener
 3407        * first registers with this context (should be called just once).
 3408        * @see #removeUnsolicited
 3409        * @see #fireUnsolicited
 3410        */
 3411       private void addUnsolicited() throws NamingException {
 3412           if (debug) {
 3413               System.out.println("LdapCtx.addUnsolicited: " + this);
 3414           }
 3415   
 3416           // addNamingListener must have created EventSupport already
 3417           ensureOpen();
 3418           synchronized (eventSupport) {
 3419               clnt.addUnsolicited(this);
 3420               unsolicited = true;
 3421           }
 3422       }
 3423   
 3424       /**
 3425        * Removes this context from registering interest in unsolicited
 3426        * notifications from the underlying LdapClient. This method is called
 3427        * under any one of the following conditions:
 3428        * <ul>
 3429        * <li>All unsolicited listeners have been removed. (see removingNamingListener)
 3430        * <li>This context is closed.
 3431        * <li>This context's underlying LdapClient changes.
 3432        *</ul>
 3433        * After this method has been called, this context will not pass
 3434        * on any events related to unsolicited notifications to EventSupport and
 3435        * and its listeners.
 3436        */
 3437   
 3438       private void removeUnsolicited() {
 3439           if (debug) {
 3440               System.out.println("LdapCtx.removeUnsolicited: " + unsolicited);
 3441           }
 3442           if (eventSupport == null) {
 3443               return;
 3444           }
 3445   
 3446           // addNamingListener must have created EventSupport already
 3447           synchronized(eventSupport) {
 3448               if (unsolicited && clnt != null) {
 3449                   clnt.removeUnsolicited(this);
 3450               }
 3451               unsolicited = false;
 3452           }
 3453       }
 3454   
 3455       /**
 3456        * Uses EventSupport to fire an event related to an unsolicited notification.
 3457        * Called by LdapClient when LdapClient receives an unsolicited notification.
 3458        */
 3459       void fireUnsolicited(Object obj) {
 3460           if (debug) {
 3461               System.out.println("LdapCtx.fireUnsolicited: " + obj);
 3462           }
 3463           // addNamingListener must have created EventSupport already
 3464           synchronized(eventSupport) {
 3465               if (unsolicited) {
 3466                   eventSupport.fireUnsolicited(obj);
 3467   
 3468                   if (obj instanceof NamingException) {
 3469                       unsolicited = false;
 3470                       // No need to notify clnt because clnt is the
 3471                       // only one that can fire a NamingException to
 3472                       // unsol listeners and it will handle its own cleanup
 3473                   }
 3474               }
 3475           }
 3476       }
 3477   }

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