0N/A/*
3667N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.jndi.ldap;
0N/A
0N/Aimport javax.naming.*;
0N/Aimport javax.naming.directory.*;
0N/Aimport javax.naming.spi.*;
0N/Aimport javax.naming.event.*;
0N/Aimport javax.naming.ldap.*;
0N/Aimport javax.naming.ldap.LdapName;
0N/Aimport javax.naming.ldap.Rdn;
0N/A
0N/Aimport java.util.Vector;
0N/Aimport java.util.Hashtable;
0N/Aimport java.util.List;
0N/Aimport java.util.StringTokenizer;
0N/Aimport java.util.Enumeration;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.OutputStream;
0N/A
0N/Aimport com.sun.jndi.toolkit.ctx.*;
0N/Aimport com.sun.jndi.toolkit.dir.HierMemDirCtx;
0N/Aimport com.sun.jndi.toolkit.dir.SearchFilter;
0N/Aimport com.sun.jndi.ldap.ext.StartTlsResponseImpl;
0N/A
0N/A/**
0N/A * The LDAP context implementation.
0N/A *
0N/A * Implementation is not thread-safe. Caller must sync as per JNDI spec.
0N/A * Members that are used directly or indirectly by internal worker threads
0N/A * (Connection, EventQueue, NamingEventNotifier) must be thread-safe.
0N/A * Connection - calls LdapClient.processUnsolicited(), which in turn calls
0N/A * LdapCtx.convertControls() and LdapCtx.fireUnsolicited().
0N/A * convertControls() - no sync; reads envprops and 'this'
0N/A * fireUnsolicited() - sync on eventSupport for all references to 'unsolicited'
0N/A * (even those in other methods); don't sync on LdapCtx in case caller
0N/A * is already sync'ing on it - this would prevent Unsol events from firing
0N/A * and the Connection thread to block (thus preventing any other data
0N/A * from being read from the connection)
0N/A * References to 'eventSupport' need not be sync'ed because these
0N/A * methods can only be called after eventSupport has been set first
0N/A * (via addNamingListener()).
0N/A * EventQueue - no direct or indirect calls to LdapCtx
0N/A * NamingEventNotifier - calls newInstance() to get instance for run() to use;
0N/A * no sync needed for methods invoked on new instance;
0N/A *
0N/A * LdapAttribute links to LdapCtx in order to process getAttributeDefinition()
0N/A * and getAttributeSyntaxDefinition() calls. It invokes LdapCtx.getSchema(),
0N/A * which uses schemaTrees (a Hashtable - already sync). Potential conflict
0N/A * of duplicating construction of tree for same subschemasubentry
0N/A * but no inconsistency problems.
0N/A *
0N/A * NamingEnumerations link to LdapCtx for the following:
0N/A * 1. increment/decrement enum count so that ctx doesn't close the
0N/A * underlying connection
0N/A * 2. LdapClient handle to get next batch of results
0N/A * 3. Sets LdapCtx's response controls
0N/A * 4. Process return code
0N/A * 5. For narrowing response controls (using ctx's factories)
0N/A * Since processing of NamingEnumeration by client is treated the same as method
0N/A * invocation on LdapCtx, caller is responsible for locking.
0N/A *
0N/A * @author Vincent Ryan
0N/A * @author Rosanna Lee
0N/A */
0N/A
0N/Afinal public class LdapCtx extends ComponentDirContext
0N/A implements EventDirContext, LdapContext {
0N/A
0N/A /*
0N/A * Used to store arguments to the search method.
0N/A */
0N/A final static class SearchArgs {
0N/A Name name;
0N/A String filter;
0N/A SearchControls cons;
0N/A String[] reqAttrs; // those attributes originally requested
0N/A
0N/A SearchArgs(Name name, String filter, SearchControls cons, String[] ra) {
0N/A this.name = name;
0N/A this.filter = filter;
0N/A this.cons = cons;
0N/A this.reqAttrs = ra;
0N/A }
0N/A }
0N/A
0N/A private static final boolean debug = false;
0N/A
0N/A private static final boolean HARD_CLOSE = true;
0N/A private static final boolean SOFT_CLOSE = false;
0N/A
0N/A // ----------------- Constants -----------------
0N/A
0N/A public static final int DEFAULT_PORT = 389;
0N/A public static final int DEFAULT_SSL_PORT = 636;
0N/A public static final String DEFAULT_HOST = "localhost";
0N/A
0N/A private static final boolean DEFAULT_DELETE_RDN = true;
0N/A private static final boolean DEFAULT_TYPES_ONLY = false;
0N/A private static final int DEFAULT_DEREF_ALIASES = 3; // always deref
0N/A private static final int DEFAULT_LDAP_VERSION = LdapClient.LDAP_VERSION3_VERSION2;
0N/A private static final int DEFAULT_BATCH_SIZE = 1;
0N/A private static final int DEFAULT_REFERRAL_MODE = LdapClient.LDAP_REF_IGNORE;
0N/A private static final char DEFAULT_REF_SEPARATOR = '#';
0N/A
0N/A // Used by LdapPoolManager
0N/A static final String DEFAULT_SSL_FACTORY =
0N/A "javax.net.ssl.SSLSocketFactory"; // use Sun's SSL
0N/A private static final int DEFAULT_REFERRAL_LIMIT = 10;
0N/A private static final String STARTTLS_REQ_OID = "1.3.6.1.4.1.1466.20037";
0N/A
0N/A // schema operational and user attributes
0N/A private static final String[] SCHEMA_ATTRIBUTES =
0N/A { "objectClasses", "attributeTypes", "matchingRules", "ldapSyntaxes" };
0N/A
0N/A // --------------- Environment property names ----------
0N/A
0N/A // LDAP protocol version: "2", "3"
0N/A private static final String VERSION = "java.naming.ldap.version";
0N/A
0N/A // Binary-valued attributes. Space separated string of attribute names.
0N/A private static final String BINARY_ATTRIBUTES =
0N/A "java.naming.ldap.attributes.binary";
0N/A
0N/A // Delete old RDN during modifyDN: "true", "false"
0N/A private static final String DELETE_RDN = "java.naming.ldap.deleteRDN";
0N/A
0N/A // De-reference aliases: "never", "searching", "finding", "always"
0N/A private static final String DEREF_ALIASES = "java.naming.ldap.derefAliases";
0N/A
0N/A // Return only attribute types (no values)
0N/A private static final String TYPES_ONLY = "java.naming.ldap.typesOnly";
0N/A
0N/A // Separator character for encoding Reference's RefAddrs; default is '#'
0N/A private static final String REF_SEPARATOR = "java.naming.ldap.ref.separator";
0N/A
0N/A // Socket factory
0N/A private static final String SOCKET_FACTORY = "java.naming.ldap.factory.socket";
0N/A
0N/A // Bind Controls (used by LdapReferralException)
0N/A static final String BIND_CONTROLS = "java.naming.ldap.control.connect";
0N/A
0N/A private static final String REFERRAL_LIMIT =
0N/A "java.naming.ldap.referral.limit";
0N/A
0N/A // trace BER (java.io.OutputStream)
0N/A private static final String TRACE_BER = "com.sun.jndi.ldap.trace.ber";
0N/A
0N/A // Get around Netscape Schema Bugs
0N/A private static final String NETSCAPE_SCHEMA_BUG =
0N/A "com.sun.jndi.ldap.netscape.schemaBugs";
0N/A // deprecated
0N/A private static final String OLD_NETSCAPE_SCHEMA_BUG =
0N/A "com.sun.naming.netscape.schemaBugs"; // for backward compatability
0N/A
0N/A // Timeout for socket connect
0N/A private static final String CONNECT_TIMEOUT =
0N/A "com.sun.jndi.ldap.connect.timeout";
0N/A
0N/A // Timeout for reading responses
0N/A private static final String READ_TIMEOUT =
0N/A "com.sun.jndi.ldap.read.timeout";
0N/A
0N/A // Environment property for connection pooling
0N/A private static final String ENABLE_POOL = "com.sun.jndi.ldap.connect.pool";
0N/A
0N/A // Environment property for the domain name (derived from this context's DN)
0N/A private static final String DOMAIN_NAME = "com.sun.jndi.ldap.domainname";
0N/A
3667N/A // Block until the first search reply is received
3667N/A private static final String WAIT_FOR_REPLY =
3667N/A "com.sun.jndi.ldap.search.waitForReply";
3667N/A
3667N/A // Size of the queue of unprocessed search replies
3667N/A private static final String REPLY_QUEUE_SIZE =
3667N/A "com.sun.jndi.ldap.search.replyQueueSize";
3667N/A
0N/A // ----------------- Fields that don't change -----------------------
0N/A private static final NameParser parser = new LdapNameParser();
0N/A
0N/A // controls that Provider needs
0N/A private static final ControlFactory myResponseControlFactory =
0N/A new DefaultResponseControlFactory();
0N/A private static final Control manageReferralControl =
0N/A new ManageReferralControl(false);
0N/A
0N/A private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();
0N/A static {
0N/A EMPTY_SCHEMA.setReadOnly(
0N/A new SchemaViolationException("Cannot update schema object"));
0N/A }
0N/A
0N/A // ------------ Package private instance variables ----------------
0N/A // Cannot be private; used by enums
0N/A
0N/A // ------- Inherited by derived context instances
0N/A
0N/A int port_number; // port number of server
0N/A String hostname = null; // host name of server (no brackets
0N/A // for IPv6 literals)
0N/A LdapClient clnt = null; // connection handle
0N/A Hashtable envprops = null; // environment properties of context
0N/A int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled
0N/A boolean hasLdapsScheme = false; // true if the context was created
0N/A // using an LDAPS URL.
0N/A
0N/A // ------- Not inherited by derived context instances
0N/A
0N/A String currentDN; // DN of this context
0N/A Name currentParsedDN; // DN of this context
0N/A Vector respCtls = null; // Response controls read
0N/A Control[] reqCtls = null; // Controls to be sent with each request
0N/A
0N/A
0N/A // ------------- Private instance variables ------------------------
0N/A
0N/A // ------- Inherited by derived context instances
0N/A
0N/A private OutputStream trace = null; // output stream for BER debug output
0N/A private boolean netscapeSchemaBug = false; // workaround
0N/A private Control[] bindCtls = null; // Controls to be sent with LDAP "bind"
0N/A private int referralHopLimit = DEFAULT_REFERRAL_LIMIT; // max referral
0N/A private Hashtable schemaTrees = null; // schema root of this context
0N/A private int batchSize = DEFAULT_BATCH_SIZE; // batch size for search results
0N/A private boolean deleteRDN = DEFAULT_DELETE_RDN; // delete the old RDN when modifying DN
0N/A private boolean typesOnly = DEFAULT_TYPES_ONLY; // return attribute types (no values)
0N/A private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching
0N/A private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR; // encoding RefAddr
0N/A
0N/A private Hashtable binaryAttrs = null; // attr values returned as byte[]
0N/A private int connectTimeout = -1; // no timeout value
0N/A private int readTimeout = -1; // no timeout value
3667N/A private boolean waitForReply = true; // wait for search response
3667N/A private int replyQueueSize = -1; // unlimited queue size
0N/A private boolean useSsl = false; // true if SSL protocol is active
0N/A private boolean useDefaultPortNumber = false; // no port number was supplied
0N/A
0N/A // ------- Not inherited by derived context instances
0N/A
0N/A // True if this context was created by another LdapCtx.
0N/A private boolean parentIsLdapCtx = false; // see composeName()
0N/A
0N/A private int hopCount = 1; // current referral hop count
0N/A private String url = null; // URL of context; see getURL()
0N/A private EventSupport eventSupport; // Event support helper for this ctx
0N/A private boolean unsolicited = false; // if there unsolicited listeners
0N/A private boolean sharable = true; // can share connection with other ctx
0N/A
0N/A // -------------- Constructors -----------------------------------
0N/A
0N/A public LdapCtx(String dn, String host, int port_number, Hashtable props,
0N/A boolean useSsl) throws NamingException {
0N/A
0N/A this.useSsl = this.hasLdapsScheme = useSsl;
0N/A
0N/A if (props != null) {
0N/A envprops = (Hashtable) props.clone();
0N/A
0N/A // SSL env prop overrides the useSsl argument
0N/A if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {
0N/A this.useSsl = true;
0N/A }
0N/A
0N/A // %%% These are only examined when the context is created
0N/A // %%% because they are only for debugging or workaround purposes.
0N/A trace = (OutputStream)envprops.get(TRACE_BER);
0N/A
0N/A if (props.get(NETSCAPE_SCHEMA_BUG) != null ||
0N/A props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {
0N/A netscapeSchemaBug = true;
0N/A }
0N/A }
0N/A
0N/A currentDN = (dn != null) ? dn : "";
0N/A currentParsedDN = parser.parse(currentDN);
0N/A
0N/A hostname = (host != null && host.length() > 0) ? host : DEFAULT_HOST;
0N/A if (hostname.charAt(0) == '[') {
0N/A hostname = hostname.substring(1, hostname.length() - 1);
0N/A }
0N/A
0N/A if (port_number > 0) {
0N/A this.port_number = port_number;
0N/A } else {
0N/A this.port_number = this.useSsl ? DEFAULT_SSL_PORT : DEFAULT_PORT;
0N/A this.useDefaultPortNumber = true;
0N/A }
0N/A
0N/A schemaTrees = new Hashtable(11, 0.75f);
0N/A initEnv();
1120N/A try {
1120N/A connect(false);
1120N/A } catch (NamingException e) {
1120N/A try {
1120N/A close();
1120N/A } catch (Exception e2) {
1120N/A // Nothing
1120N/A }
1120N/A throw e;
1120N/A }
0N/A }
0N/A
0N/A LdapCtx(LdapCtx existing, String newDN) throws NamingException {
0N/A useSsl = existing.useSsl;
0N/A hasLdapsScheme = existing.hasLdapsScheme;
0N/A useDefaultPortNumber = existing.useDefaultPortNumber;
0N/A
0N/A hostname = existing.hostname;
0N/A port_number = existing.port_number;
0N/A currentDN = newDN;
0N/A if (existing.currentDN == currentDN) {
0N/A currentParsedDN = existing.currentParsedDN;
0N/A } else {
0N/A currentParsedDN = parser.parse(currentDN);
0N/A }
0N/A
0N/A envprops = existing.envprops;
0N/A schemaTrees = existing.schemaTrees;
0N/A
0N/A clnt = existing.clnt;
0N/A clnt.incRefCount();
0N/A
0N/A parentIsLdapCtx = ((newDN == null || newDN.equals(existing.currentDN))
0N/A ? existing.parentIsLdapCtx
0N/A : true);
0N/A
0N/A // inherit these debugging/workaround flags
0N/A trace = existing.trace;
0N/A netscapeSchemaBug = existing.netscapeSchemaBug;
0N/A
0N/A initEnv();
0N/A }
0N/A
0N/A public LdapContext newInstance(Control[] reqCtls) throws NamingException {
0N/A
0N/A LdapContext clone = new LdapCtx(this, currentDN);
0N/A
0N/A // Connection controls are inherited from environment
0N/A
0N/A // Set clone's request controls
0N/A // setRequestControls() will clone reqCtls
0N/A clone.setRequestControls(reqCtls);
0N/A return clone;
0N/A }
0N/A
0N/A // --------------- Namespace Updates ---------------------
0N/A // -- bind/rebind/unbind
0N/A // -- rename
0N/A // -- createSubcontext/destroySubcontext
0N/A
0N/A protected void c_bind(Name name, Object obj, Continuation cont)
0N/A throws NamingException {
0N/A c_bind(name, obj, null, cont);
0N/A }
0N/A
0N/A /*
0N/A * attrs == null
0N/A * if obj is DirContext, attrs = obj.getAttributes()
0N/A * if attrs == null && obj == null
0N/A * disallow (cannot determine objectclass to use)
0N/A * if obj == null
0N/A * just create entry using attrs
0N/A * else
0N/A * objAttrs = create attributes for representing obj
0N/A * attrs += objAttrs
0N/A * create entry using attrs
0N/A */
0N/A protected void c_bind(Name name, Object obj, Attributes attrs,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A
0N/A cont.setError(this, name);
0N/A
0N/A Attributes inputAttrs = attrs; // Attributes supplied by caller
0N/A try {
0N/A ensureOpen();
0N/A
0N/A if (obj == null) {
0N/A if (attrs == null) {
0N/A throw new IllegalArgumentException(
0N/A "cannot bind null object with no attributes");
0N/A }
0N/A } else {
0N/A attrs = Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,
0N/A false, name, this, envprops); // not cloned
0N/A }
0N/A
0N/A String newDN = fullyQualifiedName(name);
0N/A attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
0N/A LdapEntry entry = new LdapEntry(newDN, attrs);
0N/A
0N/A LdapResult answer = clnt.add(entry, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.bind(name, obj, inputAttrs);
0N/A return;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected void c_rebind(Name name, Object obj, Continuation cont)
0N/A throws NamingException {
0N/A c_rebind(name, obj, null, cont);
0N/A }
0N/A
0N/A
0N/A /*
0N/A * attrs == null
0N/A * if obj is DirContext, attrs = obj.getAttributes().
0N/A * if attrs == null
0N/A * leave any existing attributes alone
0N/A * (set attrs = {objectclass=top} if object doesn't exist)
0N/A * else
0N/A * replace all existing attributes with attrs
0N/A * if obj == null
0N/A * just create entry using attrs
0N/A * else
0N/A * objAttrs = create attributes for representing obj
0N/A * attrs += objAttrs
0N/A * create entry using attrs
0N/A */
0N/A protected void c_rebind(Name name, Object obj, Attributes attrs,
0N/A Continuation cont) throws NamingException {
0N/A
0N/A cont.setError(this, name);
0N/A
0N/A Attributes inputAttrs = attrs;
0N/A
0N/A try {
0N/A Attributes origAttrs = null;
0N/A
0N/A // Check if name is bound
0N/A try {
0N/A origAttrs = c_getAttributes(name, null, cont);
0N/A } catch (NameNotFoundException e) {}
0N/A
0N/A // Name not bound, just add it
0N/A if (origAttrs == null) {
0N/A c_bind(name, obj, attrs, cont);
0N/A return;
0N/A }
0N/A
0N/A // there's an object there already, need to figure out
0N/A // what to do about its attributes
0N/A
0N/A if (attrs == null && obj instanceof DirContext) {
0N/A attrs = ((DirContext)obj).getAttributes("");
0N/A }
0N/A Attributes keepAttrs = (Attributes)origAttrs.clone();
0N/A
0N/A if (attrs == null) {
0N/A // we're not changing any attrs, leave old attributes alone
0N/A
0N/A // Remove Java-related object classes from objectclass attribute
0N/A Attribute origObjectClass =
0N/A origAttrs.get(Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS]);
0N/A
0N/A if (origObjectClass != null) {
0N/A // clone so that keepAttrs is not affected
0N/A origObjectClass = (Attribute)origObjectClass.clone();
0N/A for (int i = 0; i < Obj.JAVA_OBJECT_CLASSES.length; i++) {
0N/A origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES_LOWER[i]);
0N/A origObjectClass.remove(Obj.JAVA_OBJECT_CLASSES[i]);
0N/A }
0N/A // update;
0N/A origAttrs.put(origObjectClass);
0N/A }
0N/A
0N/A // remove all Java-related attributes except objectclass
0N/A for (int i = 1; i < Obj.JAVA_ATTRIBUTES.length; i++) {
0N/A origAttrs.remove(Obj.JAVA_ATTRIBUTES[i]);
0N/A }
0N/A
0N/A attrs = origAttrs;
0N/A }
0N/A if (obj != null) {
0N/A attrs =
0N/A Obj.determineBindAttrs(addrEncodingSeparator, obj, attrs,
0N/A inputAttrs != attrs, name, this, envprops);
0N/A }
0N/A
0N/A String newDN = fullyQualifiedName(name);
0N/A // remove entry
0N/A LdapResult answer = clnt.delete(newDN, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A return;
0N/A }
0N/A
0N/A Exception addEx = null;
0N/A try {
0N/A attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
0N/A
0N/A // add it back using updated attrs
0N/A LdapEntry entry = new LdapEntry(newDN, attrs);
0N/A answer = clnt.add(entry, reqCtls);
0N/A if (answer.resControls != null) {
0N/A respCtls = appendVector(respCtls, answer.resControls);
0N/A }
0N/A } catch (NamingException ae) {
0N/A addEx = ae;
0N/A } catch (IOException ae) {
0N/A addEx = ae;
0N/A }
0N/A
0N/A if ((addEx != null && !(addEx instanceof LdapReferralException)) ||
0N/A answer.status != LdapClient.LDAP_SUCCESS) {
0N/A // Attempt to restore old entry
0N/A LdapResult answer2 =
0N/A clnt.add(new LdapEntry(newDN, keepAttrs), reqCtls);
0N/A if (answer2.resControls != null) {
0N/A respCtls = appendVector(respCtls, answer2.resControls);
0N/A }
0N/A
0N/A if (addEx == null) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A }
0N/A
0N/A // Rethrow exception
0N/A if (addEx instanceof NamingException) {
0N/A throw (NamingException)addEx;
0N/A } else if (addEx instanceof IOException) {
0N/A throw (IOException)addEx;
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.rebind(name, obj, inputAttrs);
0N/A return;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected void c_unbind(Name name, Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A
0N/A try {
0N/A ensureOpen();
0N/A
0N/A String fname = fullyQualifiedName(name);
0N/A LdapResult answer = clnt.delete(fname, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A adjustDeleteStatus(fname, answer);
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.unbind(name);
0N/A return;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected void c_rename(Name oldName, Name newName, Continuation cont)
0N/A throws NamingException
0N/A {
0N/A Name oldParsed, newParsed;
0N/A Name oldParent, newParent;
0N/A String newRDN = null;
0N/A String newSuperior = null;
0N/A
0N/A // assert (oldName instanceOf CompositeName);
0N/A
0N/A cont.setError(this, oldName);
0N/A
0N/A try {
0N/A ensureOpen();
0N/A
0N/A // permit oldName to be empty (for processing referral contexts)
0N/A if (oldName.isEmpty()) {
0N/A oldParent = parser.parse("");
0N/A } else {
0N/A oldParsed = parser.parse(oldName.get(0)); // extract DN & parse
0N/A oldParent = oldParsed.getPrefix(oldParsed.size() - 1);
0N/A }
0N/A
0N/A if (newName instanceof CompositeName) {
0N/A newParsed = parser.parse(newName.get(0)); // extract DN & parse
0N/A } else {
0N/A newParsed = newName; // CompoundName/LdapName is already parsed
0N/A }
0N/A newParent = newParsed.getPrefix(newParsed.size() - 1);
0N/A
0N/A if(!oldParent.equals(newParent)) {
0N/A if (!clnt.isLdapv3) {
0N/A throw new InvalidNameException(
0N/A "LDAPv2 doesn't support changing " +
0N/A "the parent as a result of a rename");
0N/A } else {
0N/A newSuperior = fullyQualifiedName(newParent.toString());
0N/A }
0N/A }
0N/A
0N/A newRDN = newParsed.get(newParsed.size() - 1);
0N/A
0N/A LdapResult answer = clnt.moddn(fullyQualifiedName(oldName),
0N/A newRDN,
0N/A deleteRDN,
0N/A newSuperior,
0N/A reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, oldName);
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A
0N/A // Record the new RDN (for use after the referral is followed).
0N/A e.setNewRdn(newRDN);
0N/A
0N/A // Cannot continue when a referral has been received and a
0N/A // newSuperior name was supplied (because the newSuperior is
0N/A // relative to a naming context BEFORE the referral is followed).
0N/A if (newSuperior != null) {
0N/A PartialResultException pre = new PartialResultException(
0N/A "Cannot continue referral processing when newSuperior is " +
0N/A "nonempty: " + newSuperior);
0N/A pre.setRootCause(cont.fillInException(e));
0N/A throw cont.fillInException(pre);
0N/A }
0N/A
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.rename(oldName, newName);
0N/A return;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected Context c_createSubcontext(Name name, Continuation cont)
0N/A throws NamingException {
0N/A return c_createSubcontext(name, null, cont);
0N/A }
0N/A
0N/A protected DirContext c_createSubcontext(Name name, Attributes attrs,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A
0N/A Attributes inputAttrs = attrs;
0N/A try {
0N/A ensureOpen();
0N/A if (attrs == null) {
0N/A // add structural objectclass; name needs to have "cn"
0N/A Attribute oc = new BasicAttribute(
0N/A Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS],
0N/A Obj.JAVA_OBJECT_CLASSES[Obj.STRUCTURAL]);
0N/A oc.add("top");
0N/A attrs = new BasicAttributes(true); // case ignore
0N/A attrs.put(oc);
0N/A }
0N/A String newDN = fullyQualifiedName(name);
0N/A attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
0N/A
0N/A LdapEntry entry = new LdapEntry(newDN, attrs);
0N/A
0N/A LdapResult answer = clnt.add(entry, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A return null;
0N/A }
0N/A
0N/A // creation successful, get back live object
0N/A return new LdapCtx(this, newDN);
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.createSubcontext(name, inputAttrs);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected void c_destroySubcontext(Name name, Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A
0N/A try {
0N/A ensureOpen();
0N/A
0N/A String fname = fullyQualifiedName(name);
0N/A LdapResult answer = clnt.delete(fname, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A adjustDeleteStatus(fname, answer);
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.destroySubcontext(name);
0N/A return;
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Adds attributes from RDN to attrs if not already present.
0N/A * Note that if attrs already contains an attribute by the same name,
0N/A * or if the distinguished name is empty, then leave attrs unchanged.
0N/A *
0N/A * @param dn The non-null DN of the entry to add
0N/A * @param attrs The non-null attributes of entry to add
0N/A * @param directUpdate Whether attrs can be updated directly
0N/A * @returns Non-null attributes with attributes from the RDN added
0N/A */
0N/A private static Attributes addRdnAttributes(String dn, Attributes attrs,
0N/A boolean directUpdate) throws NamingException {
0N/A
0N/A // Handle the empty name
0N/A if (dn.equals("")) {
0N/A return attrs;
0N/A }
0N/A
0N/A // Parse string name into list of RDNs
0N/A //List<Rdn> rdnList = (new LdapName(dn)).rdns();
0N/A List rdnList = (new LdapName(dn)).getRdns();
0N/A
0N/A // Get leaf RDN
0N/A //Rdn rdn = rdnList.get(rdnList.size() - 1);
0N/A Rdn rdn = (Rdn) rdnList.get(rdnList.size() - 1);
0N/A Attributes nameAttrs = rdn.toAttributes();
0N/A
0N/A // Add attributes of RDN to attrs if not already there
0N/A NamingEnumeration enum_ = nameAttrs.getAll();
0N/A Attribute nameAttr;
0N/A while (enum_.hasMore()) {
0N/A nameAttr = (Attribute) enum_.next();
0N/A
0N/A // If attrs already has the attribute, don't change or add to it
0N/A if (attrs.get(nameAttr.getID()) == null) {
0N/A
0N/A /**
0N/A * When attrs.isCaseIgnored() is false, attrs.get() will
0N/A * return null when the case mis-matches for otherwise
0N/A * equal attrIDs.
0N/A * As the attrIDs' case is irrelevant for LDAP, ignore
0N/A * the case of attrIDs even when attrs.isCaseIgnored() is
0N/A * false. This is done by explicitly comparing the elements in
0N/A * the enumeration of IDs with their case ignored.
0N/A */
0N/A if (!attrs.isCaseIgnored() &&
0N/A containsIgnoreCase(attrs.getIDs(), nameAttr.getID())) {
0N/A continue;
0N/A }
0N/A
0N/A if (!directUpdate) {
0N/A attrs = (Attributes)attrs.clone();
0N/A directUpdate = true;
0N/A }
0N/A attrs.put(nameAttr);
0N/A }
0N/A }
0N/A
0N/A return attrs;
0N/A }
0N/A
0N/A
0N/A private static boolean containsIgnoreCase(NamingEnumeration enumStr,
0N/A String str) throws NamingException {
0N/A String strEntry;
0N/A
0N/A while (enumStr.hasMore()) {
0N/A strEntry = (String) enumStr.next();
0N/A if (strEntry.equalsIgnoreCase(str)) {
0N/A return true;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A
0N/A private void adjustDeleteStatus(String fname, LdapResult answer) {
0N/A if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT &&
0N/A answer.matchedDN != null) {
0N/A try {
0N/A // %%% RL: are there any implications for referrals?
0N/A
0N/A Name orig = parser.parse(fname);
0N/A Name matched = parser.parse(answer.matchedDN);
0N/A if ((orig.size() - matched.size()) == 1)
0N/A answer.status = LdapClient.LDAP_SUCCESS;
0N/A } catch (NamingException e) {}
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Append the the second Vector onto the first Vector
0N/A * (v2 must be non-null)
0N/A */
0N/A private static Vector appendVector(Vector v1, Vector v2) {
0N/A if (v1 == null) {
0N/A v1 = v2;
0N/A } else {
0N/A for (int i = 0; i < v2.size(); i++) {
0N/A v1.addElement(v2.elementAt(i));
0N/A }
0N/A }
0N/A return v1;
0N/A }
0N/A
0N/A // ------------- Lookups and Browsing -------------------------
0N/A // lookup/lookupLink
0N/A // list/listBindings
0N/A
0N/A protected Object c_lookupLink(Name name, Continuation cont)
0N/A throws NamingException {
0N/A return c_lookup(name, cont);
0N/A }
0N/A
0N/A protected Object c_lookup(Name name, Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A Object obj = null;
0N/A Attributes attrs;
0N/A
0N/A try {
0N/A SearchControls cons = new SearchControls();
0N/A cons.setSearchScope(SearchControls.OBJECT_SCOPE);
0N/A cons.setReturningAttributes(null); // ask for all attributes
0N/A cons.setReturningObjFlag(true); // need values to construct obj
0N/A
0N/A LdapResult answer = doSearchOnce(name, "(objectClass=*)", cons, true);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A // should get back 1 SearchResponse and 1 SearchResult
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A if (answer.entries == null || answer.entries.size() != 1) {
0N/A // found it but got no attributes
0N/A attrs = new BasicAttributes(LdapClient.caseIgnore);
0N/A } else {
0N/A LdapEntry entry = (LdapEntry)answer.entries.elementAt(0);
0N/A attrs = entry.attributes;
0N/A
0N/A Vector entryCtls = entry.respCtls; // retrieve entry controls
0N/A if (entryCtls != null) {
0N/A appendVector(respCtls, entryCtls); // concatenate controls
0N/A }
0N/A }
0N/A
0N/A if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
0N/A // serialized object or object reference
0N/A obj = Obj.decodeObject(attrs);
0N/A }
0N/A if (obj == null) {
0N/A obj = new LdapCtx(this, fullyQualifiedName(name));
0N/A }
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.lookup(name);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A
0N/A try {
0N/A return DirectoryManager.getObjectInstance(obj, name,
0N/A this, envprops, attrs);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A
0N/A } catch (Exception e) {
0N/A NamingException e2 = new NamingException(
0N/A "problem generating object using object factory");
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A }
0N/A }
0N/A
0N/A protected NamingEnumeration c_list(Name name, Continuation cont)
0N/A throws NamingException {
0N/A SearchControls cons = new SearchControls();
0N/A String[] classAttrs = new String[2];
0N/A
0N/A classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS];
0N/A classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME];
0N/A cons.setReturningAttributes(classAttrs);
0N/A
0N/A // set this flag to override the typesOnly flag
0N/A cons.setReturningObjFlag(true);
0N/A
0N/A cont.setError(this, name);
0N/A
0N/A LdapResult answer = null;
0N/A
0N/A try {
0N/A answer = doSearch(name, "(objectClass=*)", cons, true, true);
0N/A
0N/A // list result may contain continuation references
0N/A if ((answer.status != LdapClient.LDAP_SUCCESS) ||
0N/A (answer.referrals != null)) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A return new LdapNamingEnumeration(this, answer, name, cont);
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.list(name);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (LimitExceededException e) {
0N/A LdapNamingEnumeration res =
0N/A new LdapNamingEnumeration(this, answer, name, cont);
0N/A
0N/A res.setNamingException(
0N/A (LimitExceededException)cont.fillInException(e));
0N/A return res;
0N/A
0N/A } catch (PartialResultException e) {
0N/A LdapNamingEnumeration res =
0N/A new LdapNamingEnumeration(this, answer, name, cont);
0N/A
0N/A res.setNamingException(
0N/A (PartialResultException)cont.fillInException(e));
0N/A return res;
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected NamingEnumeration c_listBindings(Name name, Continuation cont)
0N/A throws NamingException {
0N/A
0N/A SearchControls cons = new SearchControls();
0N/A cons.setReturningAttributes(null); // ask for all attributes
0N/A cons.setReturningObjFlag(true); // need values to construct obj
0N/A
0N/A cont.setError(this, name);
0N/A
0N/A LdapResult answer = null;
0N/A
0N/A try {
0N/A answer = doSearch(name, "(objectClass=*)", cons, true, true);
0N/A
0N/A // listBindings result may contain continuation references
0N/A if ((answer.status != LdapClient.LDAP_SUCCESS) ||
0N/A (answer.referrals != null)) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A return new LdapBindingEnumeration(this, answer, name, cont);
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.listBindings(name);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A } catch (LimitExceededException e) {
0N/A LdapBindingEnumeration res =
0N/A new LdapBindingEnumeration(this, answer, name, cont);
0N/A
0N/A res.setNamingException(
0N/A (LimitExceededException)cont.fillInException(e));
0N/A return res;
0N/A
0N/A } catch (PartialResultException e) {
0N/A LdapBindingEnumeration res =
0N/A new LdapBindingEnumeration(this, answer, name, cont);
0N/A
0N/A res.setNamingException(
0N/A (PartialResultException)cont.fillInException(e));
0N/A return res;
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A // --------------- Name-related Methods -----------------------
0N/A // -- getNameParser/getNameInNamespace/composeName
0N/A
0N/A protected NameParser c_getNameParser(Name name, Continuation cont)
0N/A throws NamingException
0N/A {
0N/A // ignore name, always return same parser
0N/A cont.setSuccess();
0N/A return parser;
0N/A }
0N/A
0N/A public String getNameInNamespace() {
0N/A return currentDN;
0N/A }
0N/A
0N/A public Name composeName(Name name, Name prefix)
0N/A throws NamingException
0N/A {
0N/A Name result;
0N/A
0N/A // Handle compound names. A pair of LdapNames is an easy case.
0N/A if ((name instanceof LdapName) && (prefix instanceof LdapName)) {
0N/A result = (Name)(prefix.clone());
0N/A result.addAll(name);
0N/A return new CompositeName().add(result.toString());
0N/A }
0N/A if (!(name instanceof CompositeName)) {
0N/A name = new CompositeName().add(name.toString());
0N/A }
0N/A if (!(prefix instanceof CompositeName)) {
0N/A prefix = new CompositeName().add(prefix.toString());
0N/A }
0N/A
0N/A int prefixLast = prefix.size() - 1;
0N/A
0N/A if (name.isEmpty() || prefix.isEmpty() ||
0N/A name.get(0).equals("") || prefix.get(prefixLast).equals("")) {
0N/A return super.composeName(name, prefix);
0N/A }
0N/A
0N/A result = (Name)(prefix.clone());
0N/A result.addAll(name);
0N/A
0N/A if (parentIsLdapCtx) {
0N/A String ldapComp = concatNames(result.get(prefixLast + 1),
0N/A result.get(prefixLast));
0N/A result.remove(prefixLast + 1);
0N/A result.remove(prefixLast);
0N/A result.add(prefixLast, ldapComp);
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A private String fullyQualifiedName(Name rel) {
0N/A return rel.isEmpty()
0N/A ? currentDN
0N/A : fullyQualifiedName(rel.get(0));
0N/A }
0N/A
0N/A private String fullyQualifiedName(String rel) {
0N/A return (concatNames(rel, currentDN));
0N/A }
0N/A
0N/A // used by LdapSearchEnumeration
0N/A private static String concatNames(String lesser, String greater) {
0N/A if (lesser == null || lesser.equals("")) {
0N/A return greater;
0N/A } else if (greater == null || greater.equals("")) {
0N/A return lesser;
0N/A } else {
0N/A return (lesser + "," + greater);
0N/A }
0N/A }
0N/A
0N/A // --------------- Reading and Updating Attributes
0N/A // getAttributes/modifyAttributes
0N/A
0N/A protected Attributes c_getAttributes(Name name, String[] attrIds,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A
0N/A SearchControls cons = new SearchControls();
0N/A cons.setSearchScope(SearchControls.OBJECT_SCOPE);
0N/A cons.setReturningAttributes(attrIds);
0N/A
0N/A try {
0N/A LdapResult answer =
0N/A doSearchOnce(name, "(objectClass=*)", cons, true);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A if (answer.entries == null || answer.entries.size() != 1) {
0N/A return new BasicAttributes(LdapClient.caseIgnore);
0N/A }
0N/A
0N/A // get attributes from result
0N/A LdapEntry entry = (LdapEntry) answer.entries.elementAt(0);
0N/A
0N/A Vector entryCtls = entry.respCtls; // retrieve entry controls
0N/A if (entryCtls != null) {
0N/A appendVector(respCtls, entryCtls); // concatenate controls
0N/A }
0N/A
0N/A // do this so attributes can find their schema
0N/A setParents(entry.attributes, (Name) name.clone());
0N/A
0N/A return (entry.attributes);
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.getAttributes(name, attrIds);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected void c_modifyAttributes(Name name, int mod_op, Attributes attrs,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A
0N/A cont.setError(this, name);
0N/A
0N/A try {
0N/A ensureOpen();
0N/A
0N/A if (attrs == null || attrs.size() == 0) {
0N/A return; // nothing to do
0N/A }
0N/A String newDN = fullyQualifiedName(name);
0N/A int jmod_op = convertToLdapModCode(mod_op);
0N/A
0N/A // construct mod list
0N/A int[] jmods = new int[attrs.size()];
0N/A Attribute[] jattrs = new Attribute[attrs.size()];
0N/A
0N/A NamingEnumeration ae = attrs.getAll();
0N/A for(int i = 0; i < jmods.length && ae.hasMore(); i++) {
0N/A jmods[i] = jmod_op;
0N/A jattrs[i] = (Attribute)ae.next();
0N/A }
0N/A
0N/A LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A return;
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.modifyAttributes(name, mod_op, attrs);
0N/A return;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected void c_modifyAttributes(Name name, ModificationItem[] mods,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A
0N/A try {
0N/A ensureOpen();
0N/A
0N/A if (mods == null || mods.length == 0) {
0N/A return; // nothing to do
0N/A }
0N/A String newDN = fullyQualifiedName(name);
0N/A
0N/A // construct mod list
0N/A int[] jmods = new int[mods.length];
0N/A Attribute[] jattrs = new Attribute[mods.length];
0N/A ModificationItem mod;
0N/A for (int i = 0; i < jmods.length; i++) {
0N/A mod = mods[i];
0N/A jmods[i] = convertToLdapModCode(mod.getModificationOp());
0N/A jattrs[i] = mod.getAttribute();
0N/A }
0N/A
0N/A LdapResult answer = clnt.modify(newDN, jmods, jattrs, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, name);
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A refCtx.modifyAttributes(name, mods);
0N/A return;
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A private static int convertToLdapModCode(int mod_op) {
0N/A switch (mod_op) {
0N/A case DirContext.ADD_ATTRIBUTE:
0N/A return(LdapClient.ADD);
0N/A
0N/A case DirContext.REPLACE_ATTRIBUTE:
0N/A return (LdapClient.REPLACE);
0N/A
0N/A case DirContext.REMOVE_ATTRIBUTE:
0N/A return (LdapClient.DELETE);
0N/A
0N/A default:
0N/A throw new IllegalArgumentException("Invalid modification code");
0N/A }
0N/A }
0N/A
0N/A // ------------------- Schema -----------------------
0N/A
0N/A protected DirContext c_getSchema(Name name, Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A try {
0N/A return getSchemaTree(name);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A protected DirContext c_getSchemaClassDefinition(Name name,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A cont.setError(this, name);
0N/A
0N/A try {
0N/A // retrieve the objectClass attribute from LDAP
0N/A Attribute objectClassAttr = c_getAttributes(name,
0N/A new String[]{"objectclass"}, cont).get("objectclass");
0N/A if (objectClassAttr == null || objectClassAttr.size() == 0) {
0N/A return EMPTY_SCHEMA;
0N/A }
0N/A
0N/A // retrieve the root of the ObjectClass schema tree
0N/A Context ocSchema = (Context) c_getSchema(name, cont).lookup(
0N/A LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME);
0N/A
0N/A // create a context to hold the schema objects representing the object
0N/A // classes
0N/A HierMemDirCtx objectClassCtx = new HierMemDirCtx();
0N/A DirContext objectClassDef;
0N/A String objectClassName;
0N/A for (Enumeration objectClasses = objectClassAttr.getAll();
0N/A objectClasses.hasMoreElements(); ) {
0N/A objectClassName = (String)objectClasses.nextElement();
0N/A // %%% Should we fail if not found, or just continue?
0N/A objectClassDef = (DirContext)ocSchema.lookup(objectClassName);
0N/A objectClassCtx.bind(objectClassName, objectClassDef);
0N/A }
0N/A
0N/A // Make context read-only
0N/A objectClassCtx.setReadOnly(
0N/A new SchemaViolationException("Cannot update schema object"));
0N/A return (DirContext)objectClassCtx;
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * getSchemaTree first looks to see if we have already built a
0N/A * schema tree for the given entry. If not, it builds a new one and
0N/A * stores it in our private hash table
0N/A */
0N/A private DirContext getSchemaTree(Name name) throws NamingException {
0N/A String subschemasubentry = getSchemaEntry(name, true);
0N/A
0N/A DirContext schemaTree = (DirContext)schemaTrees.get(subschemasubentry);
0N/A
0N/A if(schemaTree==null) {
0N/A if(debug){System.err.println("LdapCtx: building new schema tree " + this);}
0N/A schemaTree = buildSchemaTree(subschemasubentry);
0N/A schemaTrees.put(subschemasubentry, schemaTree);
0N/A }
0N/A
0N/A return schemaTree;
0N/A }
0N/A
0N/A /*
0N/A * buildSchemaTree builds the schema tree corresponding to the
0N/A * given subschemasubentree
0N/A */
0N/A private DirContext buildSchemaTree(String subschemasubentry)
0N/A throws NamingException {
0N/A
0N/A // get the schema entry itself
0N/A // DO ask for return object here because we need it to
0N/A // create context. Since asking for all attrs, we won't
0N/A // be transmitting any specific attrIDs (like Java-specific ones).
0N/A SearchControls constraints = new
0N/A SearchControls(SearchControls.OBJECT_SCOPE,
0N/A 0, 0, /* count and time limits */
0N/A SCHEMA_ATTRIBUTES /* return schema attrs */,
0N/A true /* return obj */,
0N/A false /*deref link */ );
0N/A
0N/A Name sse = (new CompositeName()).add(subschemasubentry);
0N/A NamingEnumeration results =
0N/A searchAux(sse, "(objectClass=subschema)", constraints,
0N/A false, true, new Continuation());
0N/A
0N/A if(!results.hasMore()) {
0N/A throw new OperationNotSupportedException(
0N/A "Cannot get read subschemasubentry: " + subschemasubentry);
0N/A }
0N/A SearchResult result = (SearchResult)results.next();
0N/A results.close();
0N/A
0N/A Object obj = result.getObject();
0N/A if(!(obj instanceof LdapCtx)) {
0N/A throw new NamingException(
0N/A "Cannot get schema object as DirContext: " + subschemasubentry);
0N/A }
0N/A
0N/A return LdapSchemaCtx.createSchemaTree(envprops, subschemasubentry,
0N/A (LdapCtx)obj /* schema entry */,
0N/A result.getAttributes() /* schema attributes */,
0N/A netscapeSchemaBug);
0N/A }
0N/A
0N/A /*
0N/A * getSchemaEntree returns the DN of the subschemasubentree for the
0N/A * given entree. It first looks to see if the given entry has
0N/A * a subschema different from that of the root DIT (by looking for
0N/A * a "subschemasubentry" attribute). If it doesn't find one, it returns
0N/A * the one for the root of the DIT (by looking for the root's
0N/A * "subschemasubentry" attribute).
0N/A *
0N/A * This function is called regardless of the server's version, since
0N/A * an administrator may have setup the server to support client schema
0N/A * queries. If this function trys a serarch on a v2 server that
0N/A * doesn't support schema, one of these two things will happen:
0N/A * 1) It will get an exception when querying the root DSE
0N/A * 2) It will not find a subschemasubentry on the root DSE
0N/A * If either of these things occur and the server is not v3, we
0N/A * throw OperationNotSupported.
0N/A *
0N/A * the relative flag tells whether the given name is relative to this
0N/A * context.
0N/A */
0N/A private String getSchemaEntry(Name name, boolean relative)
0N/A throws NamingException {
0N/A
0N/A // Asks for operational attribute "subschemasubentry"
0N/A SearchControls constraints = new SearchControls(SearchControls.OBJECT_SCOPE,
0N/A 0, 0, /* count and time limits */
0N/A new String[]{"subschemasubentry"} /* attr to return */,
0N/A false /* returning obj */,
0N/A false /* deref link */);
0N/A
0N/A NamingEnumeration results;
0N/A try {
0N/A results = searchAux(name, "objectclass=*", constraints, relative,
0N/A true, new Continuation());
0N/A
0N/A } catch (NamingException ne) {
0N/A if (!clnt.isLdapv3 && currentDN.length() == 0 && name.isEmpty()) {
0N/A // we got an error looking for a root entry on an ldapv2
0N/A // server. The server must not support schema.
0N/A throw new OperationNotSupportedException(
0N/A "Cannot get schema information from server");
0N/A } else {
0N/A throw ne;
0N/A }
0N/A }
0N/A
0N/A if (!results.hasMoreElements()) {
0N/A throw new ConfigurationException(
0N/A "Requesting schema of nonexistent entry: " + name);
0N/A }
0N/A
0N/A SearchResult result = (SearchResult) results.next();
0N/A results.close();
0N/A
0N/A Attribute schemaEntryAttr =
0N/A result.getAttributes().get("subschemasubentry");
0N/A //System.err.println("schema entry attrs: " + schemaEntryAttr);
0N/A
0N/A if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) {
0N/A if (currentDN.length() == 0 && name.isEmpty()) {
0N/A // the server doesn't have a subschemasubentry in its root DSE.
0N/A // therefore, it doesn't support schema.
0N/A throw new OperationNotSupportedException(
0N/A "Cannot read subschemasubentry of root DSE");
0N/A } else {
0N/A return getSchemaEntry(new CompositeName(), false);
0N/A }
0N/A }
0N/A
0N/A return (String)(schemaEntryAttr.get()); // return schema entry name
0N/A }
0N/A
0N/A // package-private; used by search enum.
0N/A // Set attributes to point to this context in case some one
0N/A // asked for their schema
0N/A void setParents(Attributes attrs, Name name) throws NamingException {
0N/A NamingEnumeration ae = attrs.getAll();
0N/A while(ae.hasMore()) {
0N/A ((LdapAttribute) ae.next()).setParent(this, name);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Returns the URL associated with this context; used by LdapAttribute
0N/A * after deserialization to get pointer to this context.
0N/A */
0N/A String getURL() {
0N/A if (url == null) {
0N/A url = LdapURL.toUrlString(hostname, port_number, currentDN,
0N/A hasLdapsScheme);
0N/A }
0N/A
0N/A return url;
0N/A }
0N/A
0N/A // --------------------- Searches -----------------------------
0N/A protected NamingEnumeration c_search(Name name,
0N/A Attributes matchingAttributes,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A return c_search(name, matchingAttributes, null, cont);
0N/A }
0N/A
0N/A protected NamingEnumeration c_search(Name name,
0N/A Attributes matchingAttributes,
0N/A String[] attributesToReturn,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A SearchControls cons = new SearchControls();
0N/A cons.setReturningAttributes(attributesToReturn);
0N/A String filter;
0N/A try {
0N/A filter = SearchFilter.format(matchingAttributes);
0N/A } catch (NamingException e) {
0N/A cont.setError(this, name);
0N/A throw cont.fillInException(e);
0N/A }
0N/A return c_search(name, filter, cons, cont);
0N/A }
0N/A
0N/A protected NamingEnumeration c_search(Name name,
0N/A String filter,
0N/A SearchControls cons,
0N/A Continuation cont)
0N/A throws NamingException {
3667N/A return searchAux(name, filter, cloneSearchControls(cons), true,
3667N/A waitForReply, cont);
0N/A }
0N/A
0N/A protected NamingEnumeration c_search(Name name,
0N/A String filterExpr,
0N/A Object[] filterArgs,
0N/A SearchControls cons,
0N/A Continuation cont)
0N/A throws NamingException {
0N/A String strfilter;
0N/A try {
0N/A strfilter = SearchFilter.format(filterExpr, filterArgs);
0N/A } catch (NamingException e) {
0N/A cont.setError(this, name);
0N/A throw cont.fillInException(e);
0N/A }
0N/A return c_search(name, strfilter, cons, cont);
0N/A }
0N/A
0N/A // Used by NamingNotifier
0N/A NamingEnumeration searchAux(Name name,
0N/A String filter,
0N/A SearchControls cons,
0N/A boolean relative,
0N/A boolean waitForReply, Continuation cont) throws NamingException {
0N/A
0N/A LdapResult answer = null;
0N/A String[] tokens = new String[2]; // stores ldap compare op. values
0N/A String[] reqAttrs; // remember what was asked
0N/A
0N/A if (cons == null) {
0N/A cons = new SearchControls();
0N/A }
0N/A reqAttrs = cons.getReturningAttributes();
0N/A
0N/A // if objects are requested then request the Java attributes too
0N/A // so that the objects can be constructed
0N/A if (cons.getReturningObjFlag()) {
0N/A if (reqAttrs != null) {
0N/A
0N/A // check for presence of "*" (user attributes wildcard)
0N/A boolean hasWildcard = false;
0N/A for (int i = reqAttrs.length - 1; i >= 0; i--) {
0N/A if (reqAttrs[i].equals("*")) {
0N/A hasWildcard = true;
0N/A break;
0N/A }
0N/A }
0N/A if (! hasWildcard) {
0N/A String[] totalAttrs =
0N/A new String[reqAttrs.length +Obj.JAVA_ATTRIBUTES.length];
0N/A System.arraycopy(reqAttrs, 0, totalAttrs, 0,
0N/A reqAttrs.length);
0N/A System.arraycopy(Obj.JAVA_ATTRIBUTES, 0, totalAttrs,
0N/A reqAttrs.length, Obj.JAVA_ATTRIBUTES.length);
0N/A
0N/A cons.setReturningAttributes(totalAttrs);
0N/A }
0N/A }
0N/A }
0N/A
0N/A LdapCtx.SearchArgs args =
0N/A new LdapCtx.SearchArgs(name, filter, cons, reqAttrs);
0N/A
0N/A cont.setError(this, name);
0N/A try {
0N/A // see if this can be done as a compare, otherwise do a search
0N/A if (searchToCompare(filter, cons, tokens)){
0N/A //System.err.println("compare triggered");
0N/A answer = compare(name, tokens[0], tokens[1]);
0N/A if (! (answer.compareToSearchResult(fullyQualifiedName(name)))){
0N/A processReturnCode(answer, name);
0N/A }
0N/A } else {
0N/A answer = doSearch(name, filter, cons, relative, waitForReply);
0N/A // search result may contain referrals
0N/A processReturnCode(answer, name);
0N/A }
0N/A return new LdapSearchEnumeration(this, answer,
0N/A fullyQualifiedName(name), args, cont);
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw cont.fillInException(e);
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.search(name, filter, cons);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (LimitExceededException e) {
0N/A LdapSearchEnumeration res =
0N/A new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
0N/A args, cont);
0N/A res.setNamingException(e);
0N/A return res;
0N/A
0N/A } catch (PartialResultException e) {
0N/A LdapSearchEnumeration res =
0N/A new LdapSearchEnumeration(this, answer, fullyQualifiedName(name),
0N/A args, cont);
0N/A
0N/A res.setNamingException(e);
0N/A return res;
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw cont.fillInException(e2);
0N/A
0N/A } catch (NamingException e) {
0N/A throw cont.fillInException(e);
0N/A }
0N/A }
0N/A
0N/A
0N/A LdapResult getSearchReply(LdapClient eClnt, LdapResult res)
0N/A throws NamingException {
0N/A // ensureOpen() won't work here because
0N/A // session was associated with previous connection
0N/A
0N/A // %%% RL: we can actually allow the enumeration to continue
0N/A // using the old handle but other weird things might happen
0N/A // when we hit a referral
0N/A if (clnt != eClnt) {
0N/A throw new CommunicationException(
0N/A "Context's connection changed; unable to continue enumeration");
0N/A }
0N/A
0N/A try {
0N/A return eClnt.getSearchReply(batchSize, res, binaryAttrs);
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw e2;
0N/A }
0N/A }
0N/A
0N/A // Perform a search. Expect 1 SearchResultEntry and the SearchResultDone.
0N/A private LdapResult doSearchOnce(Name name, String filter,
0N/A SearchControls cons, boolean relative) throws NamingException {
0N/A
0N/A int savedBatchSize = batchSize;
0N/A batchSize = 2; // 2 protocol elements
0N/A
0N/A LdapResult answer = doSearch(name, filter, cons, relative, true);
0N/A
0N/A batchSize = savedBatchSize;
0N/A return answer;
0N/A }
0N/A
0N/A private LdapResult doSearch(Name name, String filter, SearchControls cons,
3667N/A boolean relative, boolean waitForReply) throws NamingException {
0N/A ensureOpen();
0N/A try {
0N/A int scope;
0N/A
0N/A switch (cons.getSearchScope()) {
0N/A case SearchControls.OBJECT_SCOPE:
0N/A scope = LdapClient.SCOPE_BASE_OBJECT;
0N/A break;
0N/A default:
0N/A case SearchControls.ONELEVEL_SCOPE:
0N/A scope = LdapClient.SCOPE_ONE_LEVEL;
0N/A break;
0N/A case SearchControls.SUBTREE_SCOPE:
0N/A scope = LdapClient.SCOPE_SUBTREE;
0N/A break;
0N/A }
0N/A
0N/A // If cons.getReturningObjFlag() then caller should already
0N/A // have make sure to request the appropriate attrs
0N/A
0N/A String[] retattrs = cons.getReturningAttributes();
0N/A if (retattrs != null && retattrs.length == 0) {
0N/A // Ldap treats null and empty array the same
0N/A // need to replace with single element array
0N/A retattrs = new String[1];
0N/A retattrs[0] = "1.1";
0N/A }
0N/A
0N/A String nm = (relative
0N/A ? fullyQualifiedName(name)
0N/A : (name.isEmpty()
0N/A ? ""
0N/A : name.get(0)));
0N/A
0N/A // JNDI unit is milliseconds, LDAP unit is seconds.
0N/A // Zero means no limit.
0N/A int msecLimit = cons.getTimeLimit();
0N/A int secLimit = 0;
0N/A
0N/A if (msecLimit > 0) {
0N/A secLimit = (msecLimit / 1000) + 1;
0N/A }
0N/A
0N/A LdapResult answer =
0N/A clnt.search(nm,
0N/A scope,
0N/A derefAliases,
0N/A (int)cons.getCountLimit(),
0N/A secLimit,
0N/A cons.getReturningObjFlag() ? false : typesOnly,
0N/A retattrs,
0N/A filter,
0N/A batchSize,
0N/A reqCtls,
0N/A binaryAttrs,
3667N/A waitForReply,
3667N/A replyQueueSize);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A return answer;
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw e2;
0N/A }
0N/A }
0N/A
0N/A
0N/A /*
0N/A * Certain simple JNDI searches are automatically converted to
0N/A * LDAP compare operations by the LDAP service provider. A search
0N/A * is converted to a compare iff:
0N/A *
0N/A * - the scope is set to OBJECT_SCOPE
0N/A * - the filter string contains a simple assertion: "<type>=<value>"
0N/A * - the returning attributes list is present but empty
0N/A */
0N/A
0N/A // returns true if a search can be caried out as a compare, and sets
0N/A // tokens[0] and tokens[1] to the type and value respectively.
0N/A // e.g. filter "cn=Jon Ruiz" becomes, type "cn" and value "Jon Ruiz"
0N/A // This function uses the documents JNDI Compare example as a model
0N/A // for when to turn a search into a compare.
0N/A
0N/A private static boolean searchToCompare(
0N/A String filter,
0N/A SearchControls cons,
0N/A String tokens[]) {
0N/A
0N/A // if scope is not object-scope, it's really a search
0N/A if (cons.getSearchScope() != SearchControls.OBJECT_SCOPE) {
0N/A return false;
0N/A }
0N/A
0N/A // if attributes are to be returned, it's really a search
0N/A String[] attrs = cons.getReturningAttributes();
0N/A if (attrs == null || attrs.length != 0) {
0N/A return false;
0N/A }
0N/A
0N/A // if the filter not a simple assertion, it's really a search
0N/A if (! filterToAssertion(filter, tokens)) {
0N/A return false;
0N/A }
0N/A
0N/A // it can be converted to a compare
0N/A return true;
0N/A }
0N/A
0N/A // If the supplied filter is a simple assertion i.e. "<type>=<value>"
0N/A // (enclosing parentheses are permitted) then
0N/A // filterToAssertion will return true and pass the type and value as
0N/A // the first and second elements of tokens respectively.
0N/A // precondition: tokens[] must be initialized and be at least of size 2.
0N/A
0N/A private static boolean filterToAssertion(String filter, String tokens[]) {
0N/A
0N/A // find the left and right half of the assertion
0N/A StringTokenizer assertionTokenizer = new StringTokenizer(filter, "=");
0N/A
0N/A if (assertionTokenizer.countTokens() != 2) {
0N/A return false;
0N/A }
0N/A
0N/A tokens[0] = assertionTokenizer.nextToken();
0N/A tokens[1] = assertionTokenizer.nextToken();
0N/A
0N/A // make sure the value does not contain a wildcard
0N/A if (tokens[1].indexOf('*') != -1) {
0N/A return false;
0N/A }
0N/A
0N/A // test for enclosing parenthesis
0N/A boolean hasParens = false;
0N/A int len = tokens[1].length();
0N/A
0N/A if ((tokens[0].charAt(0) == '(') &&
0N/A (tokens[1].charAt(len - 1) == ')')) {
0N/A hasParens = true;
0N/A
0N/A } else if ((tokens[0].charAt(0) == '(') ||
0N/A (tokens[1].charAt(len - 1) == ')')) {
0N/A return false; // unbalanced
0N/A }
0N/A
0N/A // make sure the left and right half are not expresions themselves
0N/A StringTokenizer illegalCharsTokenizer =
0N/A new StringTokenizer(tokens[0], "()&|!=~><*", true);
0N/A
0N/A if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
0N/A return false;
0N/A }
0N/A
0N/A illegalCharsTokenizer =
0N/A new StringTokenizer(tokens[1], "()&|!=~><*", true);
0N/A
0N/A if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
0N/A return false;
0N/A }
0N/A
0N/A // strip off enclosing parenthesis, if present
0N/A if (hasParens) {
0N/A tokens[0] = tokens[0].substring(1);
0N/A tokens[1] = tokens[1].substring(0, len - 1);
0N/A }
0N/A
0N/A return true;
0N/A }
0N/A
0N/A private LdapResult compare(Name name, String type, String value)
0N/A throws IOException, NamingException {
0N/A
0N/A ensureOpen();
0N/A String nm = fullyQualifiedName(name);
0N/A
0N/A LdapResult answer = clnt.compare(nm, type, value, reqCtls);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A return answer;
0N/A }
0N/A
0N/A private static SearchControls cloneSearchControls(SearchControls cons) {
0N/A if (cons == null) {
0N/A return null;
0N/A }
0N/A String[] retAttrs = cons.getReturningAttributes();
0N/A if (retAttrs != null) {
0N/A String[] attrs = new String[retAttrs.length];
0N/A System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length);
0N/A retAttrs = attrs;
0N/A }
0N/A return new SearchControls(cons.getSearchScope(),
0N/A cons.getCountLimit(),
0N/A cons.getTimeLimit(),
0N/A retAttrs,
0N/A cons.getReturningObjFlag(),
0N/A cons.getDerefLinkFlag());
0N/A }
0N/A
0N/A // -------------- Environment Properties ------------------
0N/A
0N/A /**
0N/A * Override with noncloning version.
0N/A */
0N/A protected Hashtable p_getEnvironment() {
0N/A return envprops;
0N/A }
0N/A
0N/A public Hashtable getEnvironment() throws NamingException {
0N/A return (envprops == null
0N/A ? new Hashtable(5, 0.75f)
0N/A : (Hashtable)envprops.clone());
0N/A }
0N/A
0N/A public Object removeFromEnvironment(String propName)
0N/A throws NamingException {
0N/A
0N/A // not there; just return
0N/A if (envprops == null || envprops.get(propName) == null) {
0N/A return null;
0N/A }
0N/A
0N/A if (propName.equals(REF_SEPARATOR)) {
0N/A addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
0N/A } else if (propName.equals(TYPES_ONLY)) {
0N/A typesOnly = DEFAULT_TYPES_ONLY;
0N/A } else if (propName.equals(DELETE_RDN)) {
0N/A deleteRDN = DEFAULT_DELETE_RDN;
0N/A } else if (propName.equals(DEREF_ALIASES)) {
0N/A derefAliases = DEFAULT_DEREF_ALIASES;
0N/A } else if (propName.equals(Context.BATCHSIZE)) {
0N/A batchSize = DEFAULT_BATCH_SIZE;
0N/A } else if (propName.equals(REFERRAL_LIMIT)) {
0N/A referralHopLimit = DEFAULT_REFERRAL_LIMIT;
0N/A } else if (propName.equals(Context.REFERRAL)) {
0N/A setReferralMode(null, true);
0N/A } else if (propName.equals(BINARY_ATTRIBUTES)) {
0N/A setBinaryAttributes(null);
0N/A } else if (propName.equals(CONNECT_TIMEOUT)) {
0N/A connectTimeout = -1;
0N/A } else if (propName.equals(READ_TIMEOUT)) {
0N/A readTimeout = -1;
3667N/A } else if (propName.equals(WAIT_FOR_REPLY)) {
3667N/A waitForReply = true;
3667N/A } else if (propName.equals(REPLY_QUEUE_SIZE)) {
3667N/A replyQueueSize = -1;
0N/A
0N/A// The following properties affect the connection
0N/A
0N/A } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
0N/A closeConnection(SOFT_CLOSE);
0N/A // De-activate SSL and reset the context's url and port number
0N/A if (useSsl && !hasLdapsScheme) {
0N/A useSsl = false;
0N/A url = null;
0N/A if (useDefaultPortNumber) {
0N/A port_number = DEFAULT_PORT;
0N/A }
0N/A }
0N/A } else if (propName.equals(VERSION) ||
0N/A propName.equals(SOCKET_FACTORY)) {
0N/A closeConnection(SOFT_CLOSE);
0N/A } else if(propName.equals(Context.SECURITY_AUTHENTICATION) ||
0N/A propName.equals(Context.SECURITY_PRINCIPAL) ||
0N/A propName.equals(Context.SECURITY_CREDENTIALS)) {
0N/A sharable = false;
0N/A }
0N/A
0N/A // Update environment; reconnection will use new props
0N/A envprops = (Hashtable)envprops.clone();
0N/A return envprops.remove(propName);
0N/A }
0N/A
0N/A public Object addToEnvironment(String propName, Object propVal)
0N/A throws NamingException {
0N/A
0N/A // If adding null, call remove
0N/A if (propVal == null) {
0N/A return removeFromEnvironment(propName);
0N/A }
0N/A
0N/A if (propName.equals(REF_SEPARATOR)) {
0N/A setRefSeparator((String)propVal);
0N/A } else if (propName.equals(TYPES_ONLY)) {
0N/A setTypesOnly((String)propVal);
0N/A } else if (propName.equals(DELETE_RDN)) {
0N/A setDeleteRDN((String)propVal);
0N/A } else if (propName.equals(DEREF_ALIASES)) {
0N/A setDerefAliases((String)propVal);
0N/A } else if (propName.equals(Context.BATCHSIZE)) {
0N/A setBatchSize((String)propVal);
0N/A } else if (propName.equals(REFERRAL_LIMIT)) {
0N/A setReferralLimit((String)propVal);
0N/A } else if (propName.equals(Context.REFERRAL)) {
0N/A setReferralMode((String)propVal, true);
0N/A } else if (propName.equals(BINARY_ATTRIBUTES)) {
0N/A setBinaryAttributes((String)propVal);
0N/A } else if (propName.equals(CONNECT_TIMEOUT)) {
0N/A setConnectTimeout((String)propVal);
0N/A } else if (propName.equals(READ_TIMEOUT)) {
0N/A setReadTimeout((String)propVal);
3667N/A } else if (propName.equals(WAIT_FOR_REPLY)) {
3667N/A setWaitForReply((String)propVal);
3667N/A } else if (propName.equals(REPLY_QUEUE_SIZE)) {
3667N/A setReplyQueueSize((String)propVal);
3667N/A
0N/A// The following properties affect the connection
0N/A
0N/A } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
0N/A closeConnection(SOFT_CLOSE);
0N/A // Activate SSL and reset the context's url and port number
0N/A if ("ssl".equals(propVal)) {
0N/A useSsl = true;
0N/A url = null;
0N/A if (useDefaultPortNumber) {
0N/A port_number = DEFAULT_SSL_PORT;
0N/A }
0N/A }
0N/A } else if (propName.equals(VERSION) ||
0N/A propName.equals(SOCKET_FACTORY)) {
0N/A closeConnection(SOFT_CLOSE);
0N/A } else if (propName.equals(Context.SECURITY_AUTHENTICATION) ||
0N/A propName.equals(Context.SECURITY_PRINCIPAL) ||
0N/A propName.equals(Context.SECURITY_CREDENTIALS)) {
0N/A sharable = false;
0N/A }
0N/A
0N/A // Update environment; reconnection will use new props
0N/A envprops = (envprops == null
0N/A ? new Hashtable(5, 0.75f)
0N/A : (Hashtable)envprops.clone());
0N/A return envprops.put(propName, propVal);
0N/A }
0N/A
0N/A /**
0N/A * Sets the URL that created the context in the java.naming.provider.url
0N/A * property.
0N/A */
0N/A void setProviderUrl(String providerUrl) { // called by LdapCtxFactory
0N/A if (envprops != null) {
0N/A envprops.put(Context.PROVIDER_URL, providerUrl);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the domain name for the context in the com.sun.jndi.ldap.domainname
0N/A * property.
0N/A * Used for hostname verification by Start TLS
0N/A */
0N/A void setDomainName(String domainName) { // called by LdapCtxFactory
0N/A if (envprops != null) {
0N/A envprops.put(DOMAIN_NAME, domainName);
0N/A }
0N/A }
0N/A
0N/A private void initEnv() throws NamingException {
0N/A if (envprops == null) {
0N/A // Make sure that referrals are to their default
0N/A setReferralMode(null, false);
0N/A return;
0N/A }
0N/A
0N/A // Set batch size
0N/A setBatchSize((String)envprops.get(Context.BATCHSIZE));
0N/A
0N/A // Set separator used for encoding RefAddr
0N/A setRefSeparator((String)envprops.get(REF_SEPARATOR));
0N/A
0N/A // Set whether RDN is removed when renaming object
0N/A setDeleteRDN((String)envprops.get(DELETE_RDN));
0N/A
0N/A // Set whether types are returned only
0N/A setTypesOnly((String)envprops.get(TYPES_ONLY));
0N/A
0N/A // Set how aliases are dereferenced
0N/A setDerefAliases((String)envprops.get(DEREF_ALIASES));
0N/A
0N/A // Set the limit on referral chains
0N/A setReferralLimit((String)envprops.get(REFERRAL_LIMIT));
0N/A
0N/A setBinaryAttributes((String)envprops.get(BINARY_ATTRIBUTES));
0N/A
0N/A bindCtls = cloneControls((Control[]) envprops.get(BIND_CONTROLS));
0N/A
0N/A // set referral handling
0N/A setReferralMode((String)envprops.get(Context.REFERRAL), false);
0N/A
0N/A // Set the connect timeout
0N/A setConnectTimeout((String)envprops.get(CONNECT_TIMEOUT));
0N/A
0N/A // Set the read timeout
0N/A setReadTimeout((String)envprops.get(READ_TIMEOUT));
0N/A
3667N/A // Set the flag that controls whether to block until the first reply
3667N/A // is received
3667N/A setWaitForReply((String)envprops.get(WAIT_FOR_REPLY));
3667N/A
3667N/A // Set the size of the queue of unprocessed search replies
3667N/A setReplyQueueSize((String)envprops.get(REPLY_QUEUE_SIZE));
3667N/A
0N/A // When connection is created, it will use these and other
0N/A // properties from the environment
0N/A }
0N/A
0N/A private void setDeleteRDN(String deleteRDNProp) {
0N/A if ((deleteRDNProp != null) &&
0N/A (deleteRDNProp.equalsIgnoreCase("false"))) {
0N/A deleteRDN = false;
0N/A } else {
0N/A deleteRDN = DEFAULT_DELETE_RDN;
0N/A }
0N/A }
0N/A
0N/A private void setTypesOnly(String typesOnlyProp) {
0N/A if ((typesOnlyProp != null) &&
0N/A (typesOnlyProp.equalsIgnoreCase("true"))) {
0N/A typesOnly = true;
0N/A } else {
0N/A typesOnly = DEFAULT_TYPES_ONLY;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the batch size of this context;
0N/A */
0N/A private void setBatchSize(String batchSizeProp) {
0N/A // set batchsize
0N/A if (batchSizeProp != null) {
0N/A batchSize = Integer.parseInt(batchSizeProp);
0N/A } else {
0N/A batchSize = DEFAULT_BATCH_SIZE;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the referral mode of this context to 'follow', 'throw' or 'ignore'.
0N/A * If referral mode is 'ignore' then activate the manageReferral control.
0N/A */
0N/A private void setReferralMode(String ref, boolean update) {
0N/A // First determine the referral mode
0N/A if (ref != null) {
0N/A if (ref.equals("follow")) {
0N/A handleReferrals = LdapClient.LDAP_REF_FOLLOW;
0N/A } else if (ref.equals("throw")) {
0N/A handleReferrals = LdapClient.LDAP_REF_THROW;
0N/A } else if (ref.equals("ignore")) {
0N/A handleReferrals = LdapClient.LDAP_REF_IGNORE;
0N/A } else {
0N/A throw new IllegalArgumentException(
0N/A "Illegal value for " + Context.REFERRAL + " property.");
0N/A }
0N/A } else {
0N/A handleReferrals = DEFAULT_REFERRAL_MODE;
0N/A }
0N/A
0N/A if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
0N/A // If ignoring referrals, add manageReferralControl
0N/A reqCtls = addControl(reqCtls, manageReferralControl);
0N/A
0N/A } else if (update) {
0N/A
0N/A // If we're update an existing context, remove the control
0N/A reqCtls = removeControl(reqCtls, manageReferralControl);
0N/A
0N/A } // else, leave alone; need not update
0N/A }
0N/A
0N/A /**
0N/A * Set whether aliases are derefereced during resolution and searches.
0N/A */
0N/A private void setDerefAliases(String deref) {
0N/A if (deref != null) {
0N/A if (deref.equals("never")) {
0N/A derefAliases = 0; // never de-reference aliases
0N/A } else if (deref.equals("searching")) {
0N/A derefAliases = 1; // de-reference aliases during searching
0N/A } else if (deref.equals("finding")) {
0N/A derefAliases = 2; // de-reference during name resolution
0N/A } else if (deref.equals("always")) {
0N/A derefAliases = 3; // always de-reference aliases
0N/A } else {
0N/A throw new IllegalArgumentException("Illegal value for " +
0N/A DEREF_ALIASES + " property.");
0N/A }
0N/A } else {
0N/A derefAliases = DEFAULT_DEREF_ALIASES;
0N/A }
0N/A }
0N/A
0N/A private void setRefSeparator(String sepStr) throws NamingException {
0N/A if (sepStr != null && sepStr.length() > 0) {
0N/A addrEncodingSeparator = sepStr.charAt(0);
0N/A } else {
0N/A addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the limit on referral chains
0N/A */
0N/A private void setReferralLimit(String referralLimitProp) {
0N/A // set referral limit
0N/A if (referralLimitProp != null) {
0N/A referralHopLimit = Integer.parseInt(referralLimitProp);
0N/A
0N/A // a zero setting indicates no limit
0N/A if (referralHopLimit == 0)
0N/A referralHopLimit = Integer.MAX_VALUE;
0N/A } else {
0N/A referralHopLimit = DEFAULT_REFERRAL_LIMIT;
0N/A }
0N/A }
0N/A
0N/A // For counting referral hops
0N/A void setHopCount(int hopCount) {
0N/A this.hopCount = hopCount;
0N/A }
0N/A
0N/A /**
0N/A * Sets the connect timeout value
0N/A */
0N/A private void setConnectTimeout(String connectTimeoutProp) {
0N/A if (connectTimeoutProp != null) {
0N/A connectTimeout = Integer.parseInt(connectTimeoutProp);
0N/A } else {
0N/A connectTimeout = -1;
0N/A }
0N/A }
0N/A
0N/A /**
3667N/A * Sets the size of the queue of unprocessed search replies
3667N/A */
3667N/A private void setReplyQueueSize(String replyQueueSizeProp) {
3667N/A if (replyQueueSizeProp != null) {
3667N/A replyQueueSize = Integer.parseInt(replyQueueSizeProp);
3667N/A // disallow an empty queue
3667N/A if (replyQueueSize <= 0) {
3667N/A replyQueueSize = -1; // unlimited
3667N/A }
3667N/A } else {
3667N/A replyQueueSize = -1; // unlimited
3667N/A }
3667N/A }
3667N/A
3667N/A /**
3667N/A * Sets the flag that controls whether to block until the first search
3667N/A * reply is received
3667N/A */
3667N/A private void setWaitForReply(String waitForReplyProp) {
3667N/A if (waitForReplyProp != null &&
3667N/A (waitForReplyProp.equalsIgnoreCase("false"))) {
3667N/A waitForReply = false;
3667N/A } else {
3667N/A waitForReply = true;
3667N/A }
3667N/A }
3667N/A
3667N/A /**
0N/A * Sets the read timeout value
0N/A */
0N/A private void setReadTimeout(String readTimeoutProp) {
0N/A if (readTimeoutProp != null) {
0N/A readTimeout = Integer.parseInt(readTimeoutProp);
0N/A } else {
0N/A readTimeout = -1;
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Extract URLs from a string. The format of the string is:
0N/A *
0N/A * <urlstring > ::= "Referral:" <ldapurls>
0N/A * <ldapurls> ::= <separator> <ldapurl> | <ldapurls>
0N/A * <separator> ::= ASCII linefeed character (0x0a)
0N/A * <ldapurl> ::= LDAP URL format (RFC 1959)
0N/A */
0N/A private static Vector extractURLs(String refString) {
0N/A
0N/A int separator = 0;
0N/A int urlCount = 0;
0N/A
0N/A // count the number of URLs
0N/A while ((separator = refString.indexOf('\n', separator)) >= 0) {
0N/A separator++;
0N/A urlCount++;
0N/A }
0N/A
0N/A Vector referrals = new Vector(urlCount);
0N/A int iURL;
0N/A int i = 0;
0N/A
0N/A separator = refString.indexOf('\n');
0N/A iURL = separator + 1;
0N/A while ((separator = refString.indexOf('\n', iURL)) >= 0) {
0N/A referrals.addElement(refString.substring(iURL, separator));
0N/A iURL = separator + 1;
0N/A }
0N/A referrals.addElement(refString.substring(iURL));
0N/A
0N/A return referrals;
0N/A }
0N/A
0N/A /*
0N/A * Argument is a space-separated list of attribute IDs
0N/A * Converts attribute IDs to lowercase before adding to built-in list.
0N/A */
0N/A private void setBinaryAttributes(String attrIds) {
0N/A if (attrIds == null) {
0N/A binaryAttrs = null;
0N/A } else {
0N/A binaryAttrs = new Hashtable(11, 0.75f);
0N/A StringTokenizer tokens =
0N/A new StringTokenizer(attrIds.toLowerCase(), " ");
0N/A
0N/A while (tokens.hasMoreTokens()) {
0N/A binaryAttrs.put(tokens.nextToken(), Boolean.TRUE);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // ----------------- Connection ---------------------
0N/A
0N/A protected void finalize() {
0N/A try {
0N/A close();
0N/A } catch (NamingException e) {
0N/A // ignore failures
0N/A }
0N/A }
0N/A
0N/A synchronized public void close() throws NamingException {
0N/A if (debug) {
0N/A System.err.println("LdapCtx: close() called " + this);
0N/A (new Throwable()).printStackTrace();
0N/A }
0N/A
0N/A // Event (normal and unsolicited)
0N/A if (eventSupport != null) {
0N/A eventSupport.cleanup(); // idempotent
0N/A removeUnsolicited();
0N/A }
0N/A
0N/A // Enumerations that are keeping the connection alive
0N/A if (enumCount > 0) {
0N/A if (debug)
0N/A System.err.println("LdapCtx: close deferred");
0N/A closeRequested = true;
0N/A return;
0N/A }
0N/A closeConnection(SOFT_CLOSE);
0N/A
0N/A// %%%: RL: There is no need to set these to null, as they're just
0N/A// variables whose contents and references will automatically
0N/A// be cleaned up when they're no longer referenced.
0N/A// Also, setting these to null creates problems for the attribute
0N/A// schema-related methods, which need these to work.
0N/A/*
0N/A schemaTrees = null;
0N/A envprops = null;
0N/A*/
0N/A }
0N/A
0N/A public void reconnect(Control[] connCtls) throws NamingException {
0N/A // Update environment
0N/A envprops = (envprops == null
0N/A ? new Hashtable(5, 0.75f)
0N/A : (Hashtable)envprops.clone());
0N/A
0N/A if (connCtls == null) {
0N/A envprops.remove(BIND_CONTROLS);
0N/A bindCtls = null;
0N/A } else {
0N/A envprops.put(BIND_CONTROLS, bindCtls = cloneControls(connCtls));
0N/A }
0N/A
0N/A sharable = false; // can't share with existing contexts
0N/A ensureOpen(); // open or reauthenticated
0N/A }
0N/A
0N/A private void ensureOpen() throws NamingException {
0N/A ensureOpen(false);
0N/A }
0N/A
0N/A private void ensureOpen(boolean startTLS) throws NamingException {
0N/A
0N/A try {
0N/A if (clnt == null) {
0N/A if (debug) {
0N/A System.err.println("LdapCtx: Reconnecting " + this);
0N/A }
0N/A
0N/A // reset the cache before a new connection is established
0N/A schemaTrees = new Hashtable(11, 0.75f);
0N/A connect(startTLS);
0N/A
0N/A } else if (!sharable || startTLS) {
0N/A
0N/A synchronized (clnt) {
0N/A if (!clnt.isLdapv3
0N/A || clnt.referenceCount > 1
0N/A || clnt.usingSaslStreams()) {
0N/A closeConnection(SOFT_CLOSE);
0N/A }
0N/A }
0N/A // reset the cache before a new connection is established
0N/A schemaTrees = new Hashtable(11, 0.75f);
0N/A connect(startTLS);
0N/A }
0N/A
0N/A } finally {
0N/A sharable = true; // connection is now either new or single-use
0N/A // OK for others to start sharing again
0N/A }
0N/A }
0N/A
0N/A private void connect(boolean startTLS) throws NamingException {
0N/A if (debug) { System.err.println("LdapCtx: Connecting " + this); }
0N/A
0N/A String user = null; // authenticating user
0N/A Object passwd = null; // password for authenticating user
0N/A String secProtocol = null; // security protocol (e.g. "ssl")
0N/A String socketFactory = null; // socket factory
0N/A String authMechanism = null; // authentication mechanism
0N/A String ver = null;
0N/A int ldapVersion; // LDAP protocol version
0N/A boolean usePool = false; // enable connection pooling
0N/A
0N/A if (envprops != null) {
0N/A user = (String)envprops.get(Context.SECURITY_PRINCIPAL);
0N/A passwd = envprops.get(Context.SECURITY_CREDENTIALS);
0N/A ver = (String)envprops.get(VERSION);
0N/A secProtocol =
0N/A useSsl ? "ssl" : (String)envprops.get(Context.SECURITY_PROTOCOL);
0N/A socketFactory = (String)envprops.get(SOCKET_FACTORY);
0N/A authMechanism =
0N/A (String)envprops.get(Context.SECURITY_AUTHENTICATION);
0N/A
0N/A usePool = "true".equalsIgnoreCase((String)envprops.get(ENABLE_POOL));
0N/A }
0N/A
0N/A if (socketFactory == null) {
0N/A socketFactory =
0N/A "ssl".equals(secProtocol) ? DEFAULT_SSL_FACTORY : null;
0N/A }
0N/A
0N/A if (authMechanism == null) {
0N/A authMechanism = (user == null) ? "none" : "simple";
0N/A }
0N/A
0N/A try {
0N/A boolean initial = (clnt == null);
0N/A
0N/A if (initial) {
0N/A ldapVersion = (ver != null) ? Integer.parseInt(ver) :
0N/A DEFAULT_LDAP_VERSION;
0N/A
0N/A clnt = LdapClient.getInstance(
0N/A usePool, // Whether to use connection pooling
0N/A
0N/A // Required for LdapClient constructor
0N/A hostname,
0N/A port_number,
0N/A socketFactory,
0N/A connectTimeout,
0N/A readTimeout,
0N/A trace,
0N/A
0N/A // Required for basic client identity
0N/A ldapVersion,
0N/A authMechanism,
0N/A bindCtls,
0N/A secProtocol,
0N/A
0N/A // Required for simple client identity
0N/A user,
0N/A passwd,
0N/A
0N/A // Required for SASL client identity
0N/A envprops);
0N/A
0N/A
0N/A /**
0N/A * Pooled connections are preauthenticated;
0N/A * newly created ones are not.
0N/A */
0N/A if (clnt.authenticateCalled()) {
0N/A return;
0N/A }
0N/A
0N/A } else if (sharable && startTLS) {
0N/A return; // no authentication required
0N/A
0N/A } else {
0N/A // reauthenticating over existing connection;
0N/A // only v3 supports this
0N/A ldapVersion = LdapClient.LDAP_VERSION3;
0N/A }
0N/A
0N/A LdapResult answer = clnt.authenticate(initial,
0N/A user, passwd, ldapVersion, authMechanism, bindCtls, envprops);
0N/A
0N/A respCtls = answer.resControls; // retrieve (bind) response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A if (initial) {
0N/A closeConnection(HARD_CLOSE); // hard close
0N/A }
0N/A processReturnCode(answer);
0N/A }
0N/A
0N/A } catch (LdapReferralException e) {
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw e;
0N/A
0N/A String referral;
0N/A LdapURL url;
0N/A NamingException saved_ex = null;
0N/A
0N/A // Process the referrals sequentially (top level) and
0N/A // recursively (per referral)
0N/A while (true) {
0N/A
0N/A if ((referral = e.getNextReferral()) == null) {
0N/A // No more referrals to follow
0N/A
0N/A if (saved_ex != null) {
0N/A throw (NamingException)(saved_ex.fillInStackTrace());
0N/A } else {
0N/A // No saved exception, something must have gone wrong
0N/A throw new NamingException(
0N/A "Internal error processing referral during connection");
0N/A }
0N/A }
0N/A
0N/A // Use host/port number from referral
0N/A url = new LdapURL(referral);
0N/A hostname = url.getHost();
0N/A if ((hostname != null) && (hostname.charAt(0) == '[')) {
0N/A hostname = hostname.substring(1, hostname.length() - 1);
0N/A }
0N/A port_number = url.getPort();
0N/A
0N/A // Try to connect again using new host/port number
0N/A try {
0N/A connect(startTLS);
0N/A break;
0N/A
0N/A } catch (NamingException ne) {
0N/A saved_ex = ne;
0N/A continue; // follow another referral
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A private void closeConnection(boolean hardclose) {
0N/A removeUnsolicited(); // idempotent
0N/A
0N/A if (clnt != null) {
0N/A if (debug) {
0N/A System.err.println("LdapCtx: calling clnt.close() " + this);
0N/A }
0N/A clnt.close(reqCtls, hardclose);
0N/A clnt = null;
0N/A }
0N/A }
0N/A
0N/A // Used by Enum classes to track whether it still needs context
0N/A private int enumCount = 0;
0N/A private boolean closeRequested = false;
0N/A
0N/A synchronized void incEnumCount() {
0N/A ++enumCount;
0N/A if (debug) System.err.println("LdapCtx: " + this + " enum inc: " + enumCount);
0N/A }
0N/A
0N/A synchronized void decEnumCount() {
0N/A --enumCount;
0N/A if (debug) System.err.println("LdapCtx: " + this + " enum dec: " + enumCount);
0N/A
0N/A if (enumCount == 0 && closeRequested) {
0N/A try {
0N/A close();
0N/A } catch (NamingException e) {
0N/A // ignore failures
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A // ------------ Return code and Error messages -----------------------
0N/A
0N/A protected void processReturnCode(LdapResult answer) throws NamingException {
0N/A processReturnCode(answer, null, this, null, envprops, null);
0N/A }
0N/A
0N/A void processReturnCode(LdapResult answer, Name remainName)
0N/A throws NamingException {
0N/A processReturnCode(answer,
0N/A (new CompositeName()).add(currentDN),
0N/A this,
0N/A remainName,
0N/A envprops,
0N/A fullyQualifiedName(remainName));
0N/A }
0N/A
0N/A protected void processReturnCode(LdapResult res, Name resolvedName,
0N/A Object resolvedObj, Name remainName, Hashtable envprops, String fullDN)
0N/A throws NamingException {
0N/A
0N/A String msg = LdapClient.getErrorMessage(res.status, res.errorMessage);
0N/A NamingException e;
0N/A LdapReferralException r = null;
0N/A
0N/A switch (res.status) {
0N/A
0N/A case LdapClient.LDAP_SUCCESS:
0N/A
0N/A // handle Search continuation references
0N/A if (res.referrals != null) {
0N/A
0N/A msg = "Unprocessed Continuation Reference(s)";
0N/A
0N/A if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
0N/A e = new PartialResultException(msg);
0N/A break;
0N/A }
0N/A
0N/A // handle multiple sets of URLs
0N/A int contRefCount = res.referrals.size();
0N/A LdapReferralException head = null;
0N/A LdapReferralException ptr = null;
0N/A
0N/A msg = "Continuation Reference";
0N/A
0N/A // make a chain of LdapReferralExceptions
0N/A for (int i = 0; i < contRefCount; i++) {
0N/A
0N/A r = new LdapReferralException(resolvedName, resolvedObj,
0N/A remainName, msg, envprops, fullDN, handleReferrals,
0N/A reqCtls);
0N/A r.setReferralInfo((Vector)res.referrals.elementAt(i), true);
0N/A
0N/A if (hopCount > 1) {
0N/A r.setHopCount(hopCount);
0N/A }
0N/A
0N/A if (head == null) {
0N/A head = ptr = r;
0N/A } else {
0N/A ptr.nextReferralEx = r; // append ex. to end of chain
0N/A ptr = r;
0N/A }
0N/A }
0N/A res.referrals = null; // reset
0N/A
0N/A if (res.refEx == null) {
0N/A res.refEx = head;
0N/A
0N/A } else {
0N/A ptr = res.refEx;
0N/A
0N/A while (ptr.nextReferralEx != null) {
0N/A ptr = ptr.nextReferralEx;
0N/A }
0N/A ptr.nextReferralEx = head;
0N/A }
0N/A
0N/A // check the hop limit
0N/A if (hopCount > referralHopLimit) {
0N/A NamingException lee =
0N/A new LimitExceededException("Referral limit exceeded");
0N/A lee.setRootCause(r);
0N/A throw lee;
0N/A }
0N/A }
0N/A return;
0N/A
0N/A case LdapClient.LDAP_REFERRAL:
0N/A
0N/A if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
0N/A e = new PartialResultException(msg);
0N/A break;
0N/A }
0N/A
0N/A r = new LdapReferralException(resolvedName, resolvedObj, remainName,
0N/A msg, envprops, fullDN, handleReferrals, reqCtls);
0N/A // only one set of URLs is present
0N/A r.setReferralInfo((Vector)res.referrals.elementAt(0), false);
0N/A
0N/A if (hopCount > 1) {
0N/A r.setHopCount(hopCount);
0N/A }
0N/A
0N/A // check the hop limit
0N/A if (hopCount > referralHopLimit) {
0N/A NamingException lee =
0N/A new LimitExceededException("Referral limit exceeded");
0N/A lee.setRootCause(r);
0N/A e = lee;
0N/A
0N/A } else {
0N/A e = r;
0N/A }
0N/A break;
0N/A
0N/A /*
0N/A * Handle SLAPD-style referrals.
0N/A *
0N/A * Referrals received during name resolution should be followed
0N/A * until one succeeds - the target entry is located. An exception
0N/A * is thrown now to handle these.
0N/A *
0N/A * Referrals received during a search operation point to unexplored
0N/A * parts of the directory and each should be followed. An exception
0N/A * is thrown later (during results enumeration) to handle these.
0N/A */
0N/A
0N/A case LdapClient.LDAP_PARTIAL_RESULTS:
0N/A
0N/A if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
0N/A e = new PartialResultException(msg);
0N/A break;
0N/A }
0N/A
0N/A // extract SLAPD-style referrals from errorMessage
0N/A if ((res.errorMessage != null) && (!res.errorMessage.equals(""))) {
0N/A res.referrals = extractURLs(res.errorMessage);
0N/A } else {
0N/A e = new PartialResultException(msg);
0N/A break;
0N/A }
0N/A
0N/A // build exception
0N/A r = new LdapReferralException(resolvedName,
0N/A resolvedObj,
0N/A remainName,
0N/A msg,
0N/A envprops,
0N/A fullDN,
0N/A handleReferrals,
0N/A reqCtls);
0N/A
0N/A if (hopCount > 1) {
0N/A r.setHopCount(hopCount);
0N/A }
0N/A /*
0N/A * %%%
0N/A * SLAPD-style referrals received during name resolution
0N/A * cannot be distinguished from those received during a
0N/A * search operation. Since both must be handled differently
0N/A * the following rule is applied:
0N/A *
0N/A * If 1 referral and 0 entries is received then
0N/A * assume name resolution has not yet completed.
0N/A */
0N/A if (((res.entries == null) || (res.entries.size() == 0)) &&
0N/A (res.referrals.size() == 1)) {
0N/A
0N/A r.setReferralInfo((Vector)res.referrals, false);
0N/A
0N/A // check the hop limit
0N/A if (hopCount > referralHopLimit) {
0N/A NamingException lee =
0N/A new LimitExceededException("Referral limit exceeded");
0N/A lee.setRootCause(r);
0N/A e = lee;
0N/A
0N/A } else {
0N/A e = r;
0N/A }
0N/A
0N/A } else {
0N/A r.setReferralInfo(res.referrals, true);
0N/A res.refEx = r;
0N/A return;
0N/A }
0N/A break;
0N/A
0N/A case LdapClient.LDAP_INVALID_DN_SYNTAX:
0N/A case LdapClient.LDAP_NAMING_VIOLATION:
0N/A
0N/A if (remainName != null) {
0N/A e = new
0N/A InvalidNameException(remainName.toString() + ": " + msg);
0N/A } else {
0N/A e = new InvalidNameException(msg);
0N/A }
0N/A break;
0N/A
0N/A default:
0N/A e = mapErrorCode(res.status, res.errorMessage);
0N/A break;
0N/A }
0N/A e.setResolvedName(resolvedName);
0N/A e.setResolvedObj(resolvedObj);
0N/A e.setRemainingName(remainName);
0N/A throw e;
0N/A }
0N/A
0N/A /**
0N/A * Maps an LDAP error code to an appropriate NamingException.
0N/A * %%% public; used by controls
0N/A *
0N/A * @param errorCode numeric LDAP error code
0N/A * @param errorMessage textual description of the LDAP error. May be null.
0N/A *
0N/A * @return A NamingException or null if the error code indicates success.
0N/A */
0N/A public static NamingException mapErrorCode(int errorCode,
0N/A String errorMessage) {
0N/A
0N/A if (errorCode == LdapClient.LDAP_SUCCESS)
0N/A return null;
0N/A
0N/A NamingException e = null;
0N/A String message = LdapClient.getErrorMessage(errorCode, errorMessage);
0N/A
0N/A switch (errorCode) {
0N/A
0N/A case LdapClient.LDAP_ALIAS_DEREFERENCING_PROBLEM:
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_ALIAS_PROBLEM:
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_ATTRIBUTE_OR_VALUE_EXISTS:
0N/A e = new AttributeInUseException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_AUTH_METHOD_NOT_SUPPORTED:
0N/A case LdapClient.LDAP_CONFIDENTIALITY_REQUIRED:
0N/A case LdapClient.LDAP_STRONG_AUTH_REQUIRED:
0N/A case LdapClient.LDAP_INAPPROPRIATE_AUTHENTICATION:
0N/A e = new AuthenticationNotSupportedException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_ENTRY_ALREADY_EXISTS:
0N/A e = new NameAlreadyBoundException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_INVALID_CREDENTIALS:
0N/A case LdapClient.LDAP_SASL_BIND_IN_PROGRESS:
0N/A e = new AuthenticationException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_INAPPROPRIATE_MATCHING:
0N/A e = new InvalidSearchFilterException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_INSUFFICIENT_ACCESS_RIGHTS:
0N/A e = new NoPermissionException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_INVALID_ATTRIBUTE_SYNTAX:
0N/A case LdapClient.LDAP_CONSTRAINT_VIOLATION:
0N/A e = new InvalidAttributeValueException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_LOOP_DETECT:
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_NO_SUCH_ATTRIBUTE:
0N/A e = new NoSuchAttributeException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_NO_SUCH_OBJECT:
0N/A e = new NameNotFoundException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_OBJECT_CLASS_MODS_PROHIBITED:
0N/A case LdapClient.LDAP_OBJECT_CLASS_VIOLATION:
0N/A case LdapClient.LDAP_NOT_ALLOWED_ON_RDN:
0N/A e = new SchemaViolationException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_NOT_ALLOWED_ON_NON_LEAF:
0N/A e = new ContextNotEmptyException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_OPERATIONS_ERROR:
0N/A // %%% need new exception ?
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_OTHER:
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_PROTOCOL_ERROR:
0N/A e = new CommunicationException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_SIZE_LIMIT_EXCEEDED:
0N/A e = new SizeLimitExceededException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_TIME_LIMIT_EXCEEDED:
0N/A e = new TimeLimitExceededException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
0N/A e = new OperationNotSupportedException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_UNAVAILABLE:
0N/A case LdapClient.LDAP_BUSY:
0N/A e = new ServiceUnavailableException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_UNDEFINED_ATTRIBUTE_TYPE:
0N/A e = new InvalidAttributeIdentifierException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_UNWILLING_TO_PERFORM:
0N/A e = new OperationNotSupportedException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_COMPARE_FALSE:
0N/A case LdapClient.LDAP_COMPARE_TRUE:
0N/A case LdapClient.LDAP_IS_LEAF:
0N/A // these are really not exceptions and this code probably
0N/A // never gets executed
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED:
0N/A e = new LimitExceededException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_REFERRAL:
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_PARTIAL_RESULTS:
0N/A e = new NamingException(message);
0N/A break;
0N/A
0N/A case LdapClient.LDAP_INVALID_DN_SYNTAX:
0N/A case LdapClient.LDAP_NAMING_VIOLATION:
0N/A e = new InvalidNameException(message);
0N/A break;
0N/A
0N/A default:
0N/A e = new NamingException(message);
0N/A break;
0N/A }
0N/A
0N/A return e;
0N/A }
0N/A
0N/A // ----------------- Extensions and Controls -------------------
0N/A
0N/A public ExtendedResponse extendedOperation(ExtendedRequest request)
0N/A throws NamingException {
0N/A
0N/A boolean startTLS = (request.getID().equals(STARTTLS_REQ_OID));
0N/A ensureOpen(startTLS);
0N/A
0N/A try {
0N/A
0N/A LdapResult answer =
0N/A clnt.extendedOp(request.getID(), request.getEncodedValue(),
0N/A reqCtls, startTLS);
0N/A respCtls = answer.resControls; // retrieve response controls
0N/A
0N/A if (answer.status != LdapClient.LDAP_SUCCESS) {
0N/A processReturnCode(answer, new CompositeName());
0N/A }
0N/A // %%% verify request.getID() == answer.extensionId
0N/A
0N/A int len = (answer.extensionValue == null) ?
0N/A 0 :
0N/A answer.extensionValue.length;
0N/A
0N/A ExtendedResponse er =
0N/A request.createExtendedResponse(answer.extensionId,
0N/A answer.extensionValue, 0, len);
0N/A
0N/A if (er instanceof StartTlsResponseImpl) {
0N/A // Pass the connection handle to StartTlsResponseImpl
0N/A String domainName = (String)
0N/A (envprops != null ? envprops.get(DOMAIN_NAME) : null);
0N/A ((StartTlsResponseImpl)er).setConnection(clnt.conn, domainName);
0N/A }
0N/A return er;
0N/A
0N/A } catch (LdapReferralException e) {
0N/A
0N/A if (handleReferrals == LdapClient.LDAP_REF_THROW)
0N/A throw e;
0N/A
0N/A // process the referrals sequentially
0N/A while (true) {
0N/A
0N/A LdapReferralContext refCtx =
0N/A (LdapReferralContext)e.getReferralContext(envprops, bindCtls);
0N/A
0N/A // repeat the original operation at the new context
0N/A try {
0N/A
0N/A return refCtx.extendedOperation(request);
0N/A
0N/A } catch (LdapReferralException re) {
0N/A e = re;
0N/A continue;
0N/A
0N/A } finally {
0N/A // Make sure we close referral context
0N/A refCtx.close();
0N/A }
0N/A }
0N/A
0N/A } catch (IOException e) {
0N/A NamingException e2 = new CommunicationException(e.getMessage());
0N/A e2.setRootCause(e);
0N/A throw e2;
0N/A }
0N/A }
0N/A
0N/A public void setRequestControls(Control[] reqCtls) throws NamingException {
0N/A if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
0N/A this.reqCtls = addControl(reqCtls, manageReferralControl);
0N/A } else {
0N/A this.reqCtls = cloneControls(reqCtls);
0N/A }
0N/A }
0N/A
0N/A public Control[] getRequestControls() throws NamingException {
0N/A return cloneControls(reqCtls);
0N/A }
0N/A
0N/A public Control[] getConnectControls() throws NamingException {
0N/A return cloneControls(bindCtls);
0N/A }
0N/A
0N/A public Control[] getResponseControls() throws NamingException {
0N/A return (respCtls != null)? convertControls(respCtls) : null;
0N/A }
0N/A
0N/A /**
0N/A * Narrow controls using own default factory and ControlFactory.
0N/A * @param ctls A non-null Vector
0N/A */
0N/A Control[] convertControls(Vector ctls) throws NamingException {
0N/A int count = ctls.size();
0N/A
0N/A if (count == 0) {
0N/A return null;
0N/A }
0N/A
0N/A Control[] controls = new Control[count];
0N/A
0N/A for (int i = 0; i < count; i++) {
0N/A // Try own factory first
0N/A controls[i] = myResponseControlFactory.getControlInstance(
0N/A (Control)ctls.elementAt(i));
0N/A
0N/A // Try assigned factories if own produced null
0N/A if (controls[i] == null) {
0N/A controls[i] = ControlFactory.getControlInstance(
0N/A (Control)ctls.elementAt(i), this, envprops);
0N/A }
0N/A }
0N/A return controls;
0N/A }
0N/A
0N/A private static Control[] addControl(Control[] prevCtls, Control addition) {
0N/A if (prevCtls == null) {
0N/A return new Control[]{addition};
0N/A }
0N/A
0N/A // Find it
0N/A int found = findControl(prevCtls, addition);
0N/A if (found != -1) {
0N/A return prevCtls; // no need to do it again
0N/A }
0N/A
0N/A Control[] newCtls = new Control[prevCtls.length+1];
0N/A System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length);
0N/A newCtls[prevCtls.length] = addition;
0N/A return newCtls;
0N/A }
0N/A
0N/A private static int findControl(Control[] ctls, Control target) {
0N/A for (int i = 0; i < ctls.length; i++) {
0N/A if (ctls[i] == target) {
0N/A return i;
0N/A }
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A private static Control[] removeControl(Control[] prevCtls, Control target) {
0N/A if (prevCtls == null) {
0N/A return null;
0N/A }
0N/A
0N/A // Find it
0N/A int found = findControl(prevCtls, target);
0N/A if (found == -1) {
0N/A return prevCtls; // not there
0N/A }
0N/A
0N/A // Remove it
0N/A Control[] newCtls = new Control[prevCtls.length-1];
0N/A System.arraycopy(prevCtls, 0, newCtls, 0, found);
0N/A System.arraycopy(prevCtls, found+1, newCtls, found,
0N/A prevCtls.length-found-1);
0N/A return newCtls;
0N/A }
0N/A
0N/A private static Control[] cloneControls(Control[] ctls) {
0N/A if (ctls == null) {
0N/A return null;
0N/A }
0N/A Control[] copiedCtls = new Control[ctls.length];
0N/A System.arraycopy(ctls, 0, copiedCtls, 0, ctls.length);
0N/A return copiedCtls;
0N/A }
0N/A
0N/A // -------------------- Events ------------------------
0N/A /*
0N/A * Access to eventSupport need not be synchronized even though the
0N/A * Connection thread can access it asynchronously. It is
0N/A * impossible for a race condition to occur because
0N/A * eventSupport.addNamingListener() must have been called before
0N/A * the Connection thread can call back to this ctx.
0N/A */
0N/A public void addNamingListener(Name nm, int scope, NamingListener l)
0N/A throws NamingException {
0N/A addNamingListener(getTargetName(nm), scope, l);
0N/A }
0N/A
0N/A public void addNamingListener(String nm, int scope, NamingListener l)
0N/A throws NamingException {
0N/A if (eventSupport == null)
0N/A eventSupport = new EventSupport(this);
0N/A eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
0N/A scope, l);
0N/A
0N/A // If first time asking for unsol
0N/A if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
0N/A addUnsolicited();
0N/A }
0N/A }
0N/A
0N/A public void removeNamingListener(NamingListener l) throws NamingException {
0N/A if (eventSupport == null)
0N/A return; // no activity before, so just return
0N/A
0N/A eventSupport.removeNamingListener(l);
0N/A
0N/A // If removing an Unsol listener and it is the last one, let clnt know
0N/A if (l instanceof UnsolicitedNotificationListener &&
0N/A !eventSupport.hasUnsolicited()) {
0N/A removeUnsolicited();
0N/A }
0N/A }
0N/A
0N/A public void addNamingListener(String nm, String filter, SearchControls ctls,
0N/A NamingListener l) throws NamingException {
0N/A if (eventSupport == null)
0N/A eventSupport = new EventSupport(this);
0N/A eventSupport.addNamingListener(getTargetName(new CompositeName(nm)),
0N/A filter, cloneSearchControls(ctls), l);
0N/A
0N/A // If first time asking for unsol
0N/A if (l instanceof UnsolicitedNotificationListener && !unsolicited) {
0N/A addUnsolicited();
0N/A }
0N/A }
0N/A
0N/A public void addNamingListener(Name nm, String filter, SearchControls ctls,
0N/A NamingListener l) throws NamingException {
0N/A addNamingListener(getTargetName(nm), filter, ctls, l);
0N/A }
0N/A
0N/A public void addNamingListener(Name nm, String filter, Object[] filterArgs,
0N/A SearchControls ctls, NamingListener l) throws NamingException {
0N/A addNamingListener(getTargetName(nm), filter, filterArgs, ctls, l);
0N/A }
0N/A
0N/A public void addNamingListener(String nm, String filterExpr, Object[] filterArgs,
0N/A SearchControls ctls, NamingListener l) throws NamingException {
0N/A String strfilter = SearchFilter.format(filterExpr, filterArgs);
0N/A addNamingListener(getTargetName(new CompositeName(nm)), strfilter, ctls, l);
0N/A }
0N/A
0N/A public boolean targetMustExist() {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the target name for which the listener is registering.
0N/A * If nm is a CompositeName, use its first and only component. It
0N/A * cannot have more than one components because a target be outside of
0N/A * this namespace. If nm is not a CompositeName, then treat it as a
0N/A * compound name.
0N/A * @param nm The non-null target name.
0N/A */
0N/A private static String getTargetName(Name nm) throws NamingException {
0N/A if (nm instanceof CompositeName) {
0N/A if (nm.size() > 1) {
0N/A throw new InvalidNameException(
0N/A "Target cannot span multiple namespaces: " + nm);
0N/A } else if (nm.size() == 0) {
0N/A return "";
0N/A } else {
0N/A return nm.get(0);
0N/A }
0N/A } else {
0N/A // treat as compound name
0N/A return nm.toString();
0N/A }
0N/A }
0N/A
0N/A // ------------------ Unsolicited Notification ---------------
0N/A // package private methods for handling unsolicited notification
0N/A
0N/A /**
0N/A * Registers this context with the underlying LdapClient.
0N/A * When the underlying LdapClient receives an unsolicited notification,
0N/A * it will invoke LdapCtx.fireUnsolicited() so that this context
0N/A * can (using EventSupport) notified any registered listeners.
0N/A * This method is called by EventSupport when an unsolicited listener
0N/A * first registers with this context (should be called just once).
0N/A * @see #removeUnsolicited
0N/A * @see #fireUnsolicited
0N/A */
0N/A private void addUnsolicited() throws NamingException {
0N/A if (debug) {
0N/A System.out.println("LdapCtx.addUnsolicited: " + this);
0N/A }
0N/A
0N/A // addNamingListener must have created EventSupport already
0N/A ensureOpen();
0N/A synchronized (eventSupport) {
0N/A clnt.addUnsolicited(this);
0N/A unsolicited = true;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Removes this context from registering interest in unsolicited
0N/A * notifications from the underlying LdapClient. This method is called
0N/A * under any one of the following conditions:
0N/A * <ul>
0N/A * <li>All unsolicited listeners have been removed. (see removingNamingListener)
0N/A * <li>This context is closed.
0N/A * <li>This context's underlying LdapClient changes.
0N/A *</ul>
0N/A * After this method has been called, this context will not pass
0N/A * on any events related to unsolicited notifications to EventSupport and
0N/A * and its listeners.
0N/A */
0N/A
0N/A private void removeUnsolicited() {
0N/A if (debug) {
0N/A System.out.println("LdapCtx.removeUnsolicited: " + unsolicited);
0N/A }
0N/A if (eventSupport == null) {
0N/A return;
0N/A }
0N/A
0N/A // addNamingListener must have created EventSupport already
0N/A synchronized(eventSupport) {
0N/A if (unsolicited && clnt != null) {
0N/A clnt.removeUnsolicited(this);
0N/A }
0N/A unsolicited = false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Uses EventSupport to fire an event related to an unsolicited notification.
0N/A * Called by LdapClient when LdapClient receives an unsolicited notification.
0N/A */
0N/A void fireUnsolicited(Object obj) {
0N/A if (debug) {
0N/A System.out.println("LdapCtx.fireUnsolicited: " + obj);
0N/A }
0N/A // addNamingListener must have created EventSupport already
0N/A synchronized(eventSupport) {
0N/A if (unsolicited) {
0N/A eventSupport.fireUnsolicited(obj);
0N/A
0N/A if (obj instanceof NamingException) {
0N/A unsolicited = false;
0N/A // No need to notify clnt because clnt is the
0N/A // only one that can fire a NamingException to
0N/A // unsol listeners and it will handle its own cleanup
0N/A }
0N/A }
0N/A }
0N/A }
0N/A}