Source Code Cross Referenced for LdapCtx.java in  » 6.0-JDK-Modules-com.sun » jndi » com » sun » jndi » ldap » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules com.sun » jndi » com.sun.jndi.ldap 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 1999-2005 Sun Microsystems, Inc.  All Rights Reserved.
0003:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004:         *
0005:         * This code is free software; you can redistribute it and/or modify it
0006:         * under the terms of the GNU General Public License version 2 only, as
0007:         * published by the Free Software Foundation.  Sun designates this
0008:         * particular file as subject to the "Classpath" exception as provided
0009:         * by Sun in the LICENSE file that accompanied this code.
0010:         *
0011:         * This code is distributed in the hope that it will be useful, but WITHOUT
0012:         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013:         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014:         * version 2 for more details (a copy is included in the LICENSE file that
0015:         * accompanied this code).
0016:         *
0017:         * You should have received a copy of the GNU General Public License version
0018:         * 2 along with this work; if not, write to the Free Software Foundation,
0019:         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020:         *
0021:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022:         * CA 95054 USA or visit www.sun.com if you need additional information or
0023:         * have any questions.
0024:         */
0025:
0026:        package com.sun.jndi.ldap;
0027:
0028:        import javax.naming.*;
0029:        import javax.naming.directory.*;
0030:        import javax.naming.spi.*;
0031:        import javax.naming.event.*;
0032:        import javax.naming.ldap.*;
0033:        import javax.naming.ldap.LdapName;
0034:        import javax.naming.ldap.Rdn;
0035:
0036:        import java.util.Vector;
0037:        import java.util.Hashtable;
0038:        import java.util.List;
0039:        import java.util.StringTokenizer;
0040:        import java.util.Enumeration;
0041:
0042:        import java.io.IOException;
0043:        import java.io.OutputStream;
0044:
0045:        import com.sun.jndi.toolkit.ctx.*;
0046:        import com.sun.jndi.toolkit.dir.HierMemDirCtx;
0047:        import com.sun.jndi.toolkit.dir.SearchFilter;
0048:        import com.sun.jndi.ldap.ext.StartTlsResponseImpl;
0049:
0050:        /**
0051:         * The LDAP context implementation.
0052:         *
0053:         * Implementation is not thread-safe. Caller must sync as per JNDI spec.
0054:         * Members that are used directly or indirectly by internal worker threads
0055:         * (Connection, EventQueue, NamingEventNotifier) must be thread-safe.
0056:         * Connection - calls LdapClient.processUnsolicited(), which in turn calls
0057:         *   LdapCtx.convertControls() and LdapCtx.fireUnsolicited().
0058:         *   convertControls() - no sync; reads envprops and 'this'
0059:         *   fireUnsolicited() - sync on eventSupport for all references to 'unsolicited'
0060:         *      (even those in other methods);  don't sync on LdapCtx in case caller
0061:         *      is already sync'ing on it - this would prevent Unsol events from firing
0062:         *      and the Connection thread to block (thus preventing any other data
0063:         *      from being read from the connection)
0064:         *      References to 'eventSupport' need not be sync'ed because these 
0065:         *      methods can only be called after eventSupport has been set first 
0066:         *      (via addNamingListener()).
0067:         * EventQueue - no direct or indirect calls to LdapCtx
0068:         * NamingEventNotifier - calls newInstance() to get instance for run() to use;
0069:         *	no sync needed for methods invoked on new instance;
0070:         *
0071:         * LdapAttribute links to LdapCtx in order to process getAttributeDefinition()
0072:         * and getAttributeSyntaxDefinition() calls. It invokes LdapCtx.getSchema(),
0073:         * which uses schemaTrees (a Hashtable - already sync). Potential conflict
0074:         * of duplicating construction of tree for same subschemasubentry
0075:         * but no inconsistency problems.
0076:         *
0077:         * NamingEnumerations link to LdapCtx for the following:
0078:         * 1. increment/decrement enum count so that ctx doesn't close the 
0079:         *    underlying connection 
0080:         * 2. LdapClient handle to get next batch of results
0081:         * 3. Sets LdapCtx's response controls
0082:         * 4. Process return code
0083:         * 5. For narrowing response controls (using ctx's factories)
0084:         * Since processing of NamingEnumeration by client is treated the same as method
0085:         * invocation on LdapCtx, caller is responsible for locking.
0086:         * 
0087:         * @author Vincent Ryan
0088:         * @author Rosanna Lee
0089:         */
0090:
0091:        final public class LdapCtx extends ComponentDirContext implements 
0092:                EventDirContext, LdapContext {
0093:
0094:            /*
0095:             * Used to store arguments to the search method. 
0096:             */
0097:            final static class SearchArgs {
0098:                Name name;
0099:                String filter;
0100:                SearchControls cons;
0101:                String[] reqAttrs; // those attributes originally requested
0102:
0103:                SearchArgs(Name name, String filter, SearchControls cons,
0104:                        String[] ra) {
0105:                    this .name = name;
0106:                    this .filter = filter;
0107:                    this .cons = cons;
0108:                    this .reqAttrs = ra;
0109:                }
0110:            }
0111:
0112:            private static final boolean debug = false;
0113:
0114:            private static final boolean HARD_CLOSE = true;
0115:            private static final boolean SOFT_CLOSE = false;
0116:
0117:            // -----------------  Constants  -----------------
0118:
0119:            public static final int DEFAULT_PORT = 389;
0120:            public static final int DEFAULT_SSL_PORT = 636;
0121:            public static final String DEFAULT_HOST = "localhost";
0122:
0123:            private static final boolean DEFAULT_DELETE_RDN = true;
0124:            private static final boolean DEFAULT_TYPES_ONLY = false;
0125:            private static final int DEFAULT_DEREF_ALIASES = 3; // always deref
0126:            private static final int DEFAULT_LDAP_VERSION = LdapClient.LDAP_VERSION3_VERSION2;
0127:            private static final int DEFAULT_BATCH_SIZE = 1;
0128:            private static final int DEFAULT_REFERRAL_MODE = LdapClient.LDAP_REF_IGNORE;
0129:            private static final char DEFAULT_REF_SEPARATOR = '#';
0130:
0131:            // Used by LdapPoolManager
0132:            static final String DEFAULT_SSL_FACTORY = "javax.net.ssl.SSLSocketFactory"; // use Sun's SSL
0133:            private static final int DEFAULT_REFERRAL_LIMIT = 10;
0134:            private static final String STARTTLS_REQ_OID = "1.3.6.1.4.1.1466.20037";
0135:
0136:            // schema operational and user attributes
0137:            private static final String[] SCHEMA_ATTRIBUTES = {
0138:                    "objectClasses", "attributeTypes", "matchingRules",
0139:                    "ldapSyntaxes" };
0140:
0141:            // --------------- Environment property names ----------
0142:
0143:            // LDAP protocol version: "2", "3"
0144:            private static final String VERSION = "java.naming.ldap.version";
0145:
0146:            // Binary-valued attributes. Space separated string of attribute names.
0147:            private static final String BINARY_ATTRIBUTES = "java.naming.ldap.attributes.binary";
0148:
0149:            // Delete old RDN during modifyDN: "true", "false"
0150:            private static final String DELETE_RDN = "java.naming.ldap.deleteRDN";
0151:
0152:            // De-reference aliases: "never", "searching", "finding", "always"
0153:            private static final String DEREF_ALIASES = "java.naming.ldap.derefAliases";
0154:
0155:            // Return only attribute types (no values)
0156:            private static final String TYPES_ONLY = "java.naming.ldap.typesOnly";
0157:
0158:            // Separator character for encoding Reference's RefAddrs; default is '#'
0159:            private static final String REF_SEPARATOR = "java.naming.ldap.ref.separator";
0160:
0161:            // Socket factory
0162:            private static final String SOCKET_FACTORY = "java.naming.ldap.factory.socket";
0163:
0164:            // Bind Controls (used by LdapReferralException)
0165:            static final String BIND_CONTROLS = "java.naming.ldap.control.connect";
0166:
0167:            private static final String REFERRAL_LIMIT = "java.naming.ldap.referral.limit";
0168:
0169:            // trace BER (java.io.OutputStream)
0170:            private static final String TRACE_BER = "com.sun.jndi.ldap.trace.ber";
0171:
0172:            // Get around Netscape Schema Bugs
0173:            private static final String NETSCAPE_SCHEMA_BUG = "com.sun.jndi.ldap.netscape.schemaBugs";
0174:            // deprecated
0175:            private static final String OLD_NETSCAPE_SCHEMA_BUG = "com.sun.naming.netscape.schemaBugs"; // for backward compatability
0176:
0177:            // Timeout for socket connect
0178:            private static final String CONNECT_TIMEOUT = "com.sun.jndi.ldap.connect.timeout";
0179:
0180:            // Timeout for reading responses
0181:            private static final String READ_TIMEOUT = "com.sun.jndi.ldap.read.timeout";
0182:
0183:            // Environment property for connection pooling
0184:            private static final String ENABLE_POOL = "com.sun.jndi.ldap.connect.pool";
0185:
0186:            // Environment property for the domain name (derived from this context's DN)
0187:            private static final String DOMAIN_NAME = "com.sun.jndi.ldap.domainname";
0188:
0189:            // ----------------- Fields that don't change -----------------------
0190:            private static final NameParser parser = new LdapNameParser();
0191:
0192:            // controls that Provider needs
0193:            private static final ControlFactory myResponseControlFactory = new DefaultResponseControlFactory();
0194:            private static final Control manageReferralControl = new ManageReferralControl(
0195:                    false);
0196:
0197:            private static final HierMemDirCtx EMPTY_SCHEMA = new HierMemDirCtx();
0198:            static {
0199:                EMPTY_SCHEMA.setReadOnly(new SchemaViolationException(
0200:                        "Cannot update schema object"));
0201:            }
0202:
0203:            // ------------ Package private instance variables ----------------
0204:            // Cannot be private; used by enums
0205:
0206:            // ------- Inherited by derived context instances
0207:
0208:            int port_number; // port number of server
0209:            String hostname = null; // host name of server (no brackets
0210:            //   for IPv6 literals)
0211:            LdapClient clnt = null; // connection handle 
0212:            Hashtable envprops = null; // environment properties of context
0213:            int handleReferrals = DEFAULT_REFERRAL_MODE; // how referral is handled
0214:            boolean hasLdapsScheme = false; // true if the context was created
0215:            //  using an LDAPS URL.
0216:
0217:            // ------- Not inherited by derived context instances
0218:
0219:            String currentDN; // DN of this context
0220:            Name currentParsedDN; // DN of this context
0221:            Vector respCtls = null; // Response controls read
0222:            Control[] reqCtls = null; // Controls to be sent with each request
0223:
0224:            // ------------- Private instance variables ------------------------
0225:
0226:            // ------- Inherited by derived context instances
0227:
0228:            private OutputStream trace = null; // output stream for BER debug output
0229:            private boolean netscapeSchemaBug = false; // workaround 
0230:            private Control[] bindCtls = null; // Controls to be sent with LDAP "bind"
0231:            private int referralHopLimit = DEFAULT_REFERRAL_LIMIT; // max referral
0232:            private Hashtable schemaTrees = null; // schema root of this context
0233:            private int batchSize = DEFAULT_BATCH_SIZE; // batch size for search results
0234:            private boolean deleteRDN = DEFAULT_DELETE_RDN; // delete the old RDN when modifying DN
0235:            private boolean typesOnly = DEFAULT_TYPES_ONLY; // return attribute types (no values)
0236:            private int derefAliases = DEFAULT_DEREF_ALIASES;// de-reference alias entries during searching
0237:            private char addrEncodingSeparator = DEFAULT_REF_SEPARATOR; // encoding RefAddr
0238:
0239:            private Hashtable binaryAttrs = null; // attr values returned as byte[]
0240:            private int connectTimeout = -1; // no timeout value
0241:            private int readTimeout = -1; // no timeout value
0242:            private boolean useSsl = false; // true if SSL protocol is active
0243:            private boolean useDefaultPortNumber = false; // no port number was supplied
0244:
0245:            // ------- Not inherited by derived context instances
0246:
0247:            // True if this context was created by another LdapCtx.
0248:            private boolean parentIsLdapCtx = false; // see composeName()
0249:
0250:            private int hopCount = 1; // current referral hop count
0251:            private String url = null; // URL of context; see getURL()
0252:            private EventSupport eventSupport; // Event support helper for this ctx
0253:            private boolean unsolicited = false; // if there unsolicited listeners
0254:            private boolean sharable = true; // can share connection with other ctx
0255:
0256:            // -------------- Constructors  -----------------------------------
0257:
0258:            public LdapCtx(String dn, String host, int port_number,
0259:                    Hashtable props, boolean useSsl) throws NamingException {
0260:
0261:                this .useSsl = this .hasLdapsScheme = useSsl;
0262:
0263:                if (props != null) {
0264:                    envprops = (Hashtable) props.clone();
0265:
0266:                    // SSL env prop overrides the useSsl argument
0267:                    if ("ssl".equals(envprops.get(Context.SECURITY_PROTOCOL))) {
0268:                        this .useSsl = true;
0269:                    }
0270:
0271:                    // %%% These are only examined when the context is created
0272:                    // %%% because they are only for debugging or workaround purposes.
0273:                    trace = (OutputStream) envprops.get(TRACE_BER);
0274:
0275:                    if (props.get(NETSCAPE_SCHEMA_BUG) != null
0276:                            || props.get(OLD_NETSCAPE_SCHEMA_BUG) != null) {
0277:                        netscapeSchemaBug = true;
0278:                    }
0279:                }
0280:
0281:                currentDN = (dn != null) ? dn : "";
0282:                currentParsedDN = parser.parse(currentDN);
0283:
0284:                hostname = (host != null && host.length() > 0) ? host
0285:                        : DEFAULT_HOST;
0286:                if (hostname.charAt(0) == '[') {
0287:                    hostname = hostname.substring(1, hostname.length() - 1);
0288:                }
0289:
0290:                if (port_number > 0) {
0291:                    this .port_number = port_number;
0292:                } else {
0293:                    this .port_number = this .useSsl ? DEFAULT_SSL_PORT
0294:                            : DEFAULT_PORT;
0295:                    this .useDefaultPortNumber = true;
0296:                }
0297:
0298:                schemaTrees = new Hashtable(11, 0.75f);
0299:                initEnv();
0300:                connect(false);
0301:            }
0302:
0303:            LdapCtx(LdapCtx existing, String newDN) throws NamingException {
0304:                useSsl = existing.useSsl;
0305:                hasLdapsScheme = existing.hasLdapsScheme;
0306:                useDefaultPortNumber = existing.useDefaultPortNumber;
0307:
0308:                hostname = existing.hostname;
0309:                port_number = existing.port_number;
0310:                currentDN = newDN;
0311:                if (existing.currentDN == currentDN) {
0312:                    currentParsedDN = existing.currentParsedDN;
0313:                } else {
0314:                    currentParsedDN = parser.parse(currentDN);
0315:                }
0316:
0317:                envprops = existing.envprops;
0318:                schemaTrees = existing.schemaTrees;
0319:
0320:                clnt = existing.clnt;
0321:                clnt.incRefCount();
0322:
0323:                parentIsLdapCtx = ((newDN == null || newDN
0324:                        .equals(existing.currentDN)) ? existing.parentIsLdapCtx
0325:                        : true);
0326:
0327:                // inherit these debugging/workaround flags
0328:                trace = existing.trace;
0329:                netscapeSchemaBug = existing.netscapeSchemaBug;
0330:
0331:                initEnv();
0332:            }
0333:
0334:            public LdapContext newInstance(Control[] reqCtls)
0335:                    throws NamingException {
0336:
0337:                LdapContext clone = new LdapCtx(this , currentDN);
0338:
0339:                // Connection controls are inherited from environment
0340:
0341:                // Set clone's request controls
0342:                // setRequestControls() will clone reqCtls
0343:                clone.setRequestControls(reqCtls);
0344:                return clone;
0345:            }
0346:
0347:            // --------------- Namespace Updates ---------------------
0348:            // -- bind/rebind/unbind 
0349:            // -- rename 
0350:            // -- createSubcontext/destroySubcontext
0351:
0352:            protected void c_bind(Name name, Object obj, Continuation cont)
0353:                    throws NamingException {
0354:                c_bind(name, obj, null, cont);
0355:            }
0356:
0357:            /*
0358:             * attrs == null
0359:             *      if obj is DirContext, attrs = obj.getAttributes()
0360:             * if attrs == null && obj == null
0361:             *      disallow (cannot determine objectclass to use)
0362:             * if obj == null
0363:             *      just create entry using attrs
0364:             * else 
0365:             *      objAttrs = create attributes for representing obj
0366:             *	    attrs += objAttrs
0367:             *      create entry using attrs
0368:             */
0369:            protected void c_bind(Name name, Object obj, Attributes attrs,
0370:                    Continuation cont) throws NamingException {
0371:
0372:                cont.setError(this , name);
0373:
0374:                Attributes inputAttrs = attrs; // Attributes supplied by caller
0375:                try {
0376:                    ensureOpen();
0377:
0378:                    if (obj == null) {
0379:                        if (attrs == null) {
0380:                            throw new IllegalArgumentException(
0381:                                    "cannot bind null object with no attributes");
0382:                        }
0383:                    } else {
0384:                        attrs = Obj.determineBindAttrs(addrEncodingSeparator,
0385:                                obj, attrs, false, name, this , envprops); // not cloned
0386:                    }
0387:
0388:                    String newDN = fullyQualifiedName(name);
0389:                    attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
0390:                    LdapEntry entry = new LdapEntry(newDN, attrs);
0391:
0392:                    LdapResult answer = clnt.add(entry, reqCtls);
0393:                    respCtls = answer.resControls; // retrieve response controls
0394:
0395:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
0396:                        processReturnCode(answer, name);
0397:                    }
0398:
0399:                } catch (LdapReferralException e) {
0400:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
0401:                        throw cont.fillInException(e);
0402:
0403:                    // process the referrals sequentially
0404:                    while (true) {
0405:
0406:                        LdapReferralContext refCtx = (LdapReferralContext) e
0407:                                .getReferralContext(envprops, bindCtls);
0408:
0409:                        // repeat the original operation at the new context
0410:                        try {
0411:
0412:                            refCtx.bind(name, obj, inputAttrs);
0413:                            return;
0414:
0415:                        } catch (LdapReferralException re) {
0416:                            e = re;
0417:                            continue;
0418:
0419:                        } finally {
0420:                            // Make sure we close referral context
0421:                            refCtx.close();
0422:                        }
0423:                    }
0424:
0425:                } catch (IOException e) {
0426:                    NamingException e2 = new CommunicationException(e
0427:                            .getMessage());
0428:                    e2.setRootCause(e);
0429:                    throw cont.fillInException(e2);
0430:
0431:                } catch (NamingException e) {
0432:                    throw cont.fillInException(e);
0433:                }
0434:            }
0435:
0436:            protected void c_rebind(Name name, Object obj, Continuation cont)
0437:                    throws NamingException {
0438:                c_rebind(name, obj, null, cont);
0439:            }
0440:
0441:            /*
0442:             * attrs == null
0443:             *    if obj is DirContext, attrs = obj.getAttributes().
0444:             * if attrs == null
0445:             *	  leave any existing attributes alone
0446:             *	  (set attrs = {objectclass=top} if object doesn't exist)
0447:             * else
0448:             *    replace all existing attributes with attrs
0449:             * if obj == null
0450:             *      just create entry using attrs
0451:             * else 
0452:             *      objAttrs = create attributes for representing obj
0453:             *	    attrs += objAttrs
0454:             *      create entry using attrs
0455:             */
0456:            protected void c_rebind(Name name, Object obj, Attributes attrs,
0457:                    Continuation cont) throws NamingException {
0458:
0459:                cont.setError(this , name);
0460:
0461:                Attributes inputAttrs = attrs;
0462:
0463:                try {
0464:                    Attributes origAttrs = null;
0465:
0466:                    // Check if name is bound
0467:                    try {
0468:                        origAttrs = c_getAttributes(name, null, cont);
0469:                    } catch (NameNotFoundException e) {
0470:                    }
0471:
0472:                    // Name not bound, just add it
0473:                    if (origAttrs == null) {
0474:                        c_bind(name, obj, attrs, cont);
0475:                        return;
0476:                    }
0477:
0478:                    // there's an object there already, need to figure out
0479:                    // what to do about its attributes
0480:
0481:                    if (attrs == null && obj instanceof  DirContext) {
0482:                        attrs = ((DirContext) obj).getAttributes("");
0483:                    }
0484:                    Attributes keepAttrs = (Attributes) origAttrs.clone();
0485:
0486:                    if (attrs == null) {
0487:                        // we're not changing any attrs, leave old attributes alone
0488:
0489:                        // Remove Java-related object classes from objectclass attribute
0490:                        Attribute origObjectClass = origAttrs
0491:                                .get(Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS]);
0492:
0493:                        if (origObjectClass != null) {
0494:                            // clone so that keepAttrs is not affected
0495:                            origObjectClass = (Attribute) origObjectClass
0496:                                    .clone();
0497:                            for (int i = 0; i < Obj.JAVA_OBJECT_CLASSES.length; i++) {
0498:                                origObjectClass
0499:                                        .remove(Obj.JAVA_OBJECT_CLASSES_LOWER[i]);
0500:                                origObjectClass
0501:                                        .remove(Obj.JAVA_OBJECT_CLASSES[i]);
0502:                            }
0503:                            // update;
0504:                            origAttrs.put(origObjectClass);
0505:                        }
0506:
0507:                        // remove all Java-related attributes except objectclass
0508:                        for (int i = 1; i < Obj.JAVA_ATTRIBUTES.length; i++) {
0509:                            origAttrs.remove(Obj.JAVA_ATTRIBUTES[i]);
0510:                        }
0511:
0512:                        attrs = origAttrs;
0513:                    }
0514:                    if (obj != null) {
0515:                        attrs = Obj.determineBindAttrs(addrEncodingSeparator,
0516:                                obj, attrs, inputAttrs != attrs, name, this ,
0517:                                envprops);
0518:                    }
0519:
0520:                    String newDN = fullyQualifiedName(name);
0521:                    // remove entry
0522:                    LdapResult answer = clnt.delete(newDN, reqCtls);
0523:                    respCtls = answer.resControls; // retrieve response controls
0524:
0525:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
0526:                        processReturnCode(answer, name);
0527:                        return;
0528:                    }
0529:
0530:                    Exception addEx = null;
0531:                    try {
0532:                        attrs = addRdnAttributes(newDN, attrs,
0533:                                inputAttrs != attrs);
0534:
0535:                        // add it back using updated attrs
0536:                        LdapEntry entry = new LdapEntry(newDN, attrs);
0537:                        answer = clnt.add(entry, reqCtls);
0538:                        if (answer.resControls != null) {
0539:                            respCtls = appendVector(respCtls,
0540:                                    answer.resControls);
0541:                        }
0542:                    } catch (NamingException ae) {
0543:                        addEx = ae;
0544:                    } catch (IOException ae) {
0545:                        addEx = ae;
0546:                    }
0547:
0548:                    if ((addEx != null && !(addEx instanceof  LdapReferralException))
0549:                            || answer.status != LdapClient.LDAP_SUCCESS) {
0550:                        // Attempt to restore old entry
0551:                        LdapResult answer2 = clnt.add(new LdapEntry(newDN,
0552:                                keepAttrs), reqCtls);
0553:                        if (answer2.resControls != null) {
0554:                            respCtls = appendVector(respCtls,
0555:                                    answer2.resControls);
0556:                        }
0557:
0558:                        if (addEx == null) {
0559:                            processReturnCode(answer, name);
0560:                        }
0561:                    }
0562:
0563:                    // Rethrow exception
0564:                    if (addEx instanceof  NamingException) {
0565:                        throw (NamingException) addEx;
0566:                    } else if (addEx instanceof  IOException) {
0567:                        throw (IOException) addEx;
0568:                    }
0569:
0570:                } catch (LdapReferralException e) {
0571:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
0572:                        throw cont.fillInException(e);
0573:
0574:                    // process the referrals sequentially
0575:                    while (true) {
0576:
0577:                        LdapReferralContext refCtx = (LdapReferralContext) e
0578:                                .getReferralContext(envprops, bindCtls);
0579:
0580:                        // repeat the original operation at the new context
0581:                        try {
0582:
0583:                            refCtx.rebind(name, obj, inputAttrs);
0584:                            return;
0585:
0586:                        } catch (LdapReferralException re) {
0587:                            e = re;
0588:                            continue;
0589:
0590:                        } finally {
0591:                            // Make sure we close referral context
0592:                            refCtx.close();
0593:                        }
0594:                    }
0595:
0596:                } catch (IOException e) {
0597:                    NamingException e2 = new CommunicationException(e
0598:                            .getMessage());
0599:                    e2.setRootCause(e);
0600:                    throw cont.fillInException(e2);
0601:
0602:                } catch (NamingException e) {
0603:                    throw cont.fillInException(e);
0604:                }
0605:            }
0606:
0607:            protected void c_unbind(Name name, Continuation cont)
0608:                    throws NamingException {
0609:                cont.setError(this , name);
0610:
0611:                try {
0612:                    ensureOpen();
0613:
0614:                    String fname = fullyQualifiedName(name);
0615:                    LdapResult answer = clnt.delete(fname, reqCtls);
0616:                    respCtls = answer.resControls; // retrieve response controls
0617:
0618:                    adjustDeleteStatus(fname, answer);
0619:
0620:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
0621:                        processReturnCode(answer, name);
0622:                    }
0623:
0624:                } catch (LdapReferralException e) {
0625:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
0626:                        throw cont.fillInException(e);
0627:
0628:                    // process the referrals sequentially
0629:                    while (true) {
0630:
0631:                        LdapReferralContext refCtx = (LdapReferralContext) e
0632:                                .getReferralContext(envprops, bindCtls);
0633:
0634:                        // repeat the original operation at the new context
0635:                        try {
0636:
0637:                            refCtx.unbind(name);
0638:                            return;
0639:
0640:                        } catch (LdapReferralException re) {
0641:                            e = re;
0642:                            continue;
0643:
0644:                        } finally {
0645:                            // Make sure we close referral context
0646:                            refCtx.close();
0647:                        }
0648:                    }
0649:
0650:                } catch (IOException e) {
0651:                    NamingException e2 = new CommunicationException(e
0652:                            .getMessage());
0653:                    e2.setRootCause(e);
0654:                    throw cont.fillInException(e2);
0655:
0656:                } catch (NamingException e) {
0657:                    throw cont.fillInException(e);
0658:                }
0659:            }
0660:
0661:            protected void c_rename(Name oldName, Name newName,
0662:                    Continuation cont) throws NamingException {
0663:                Name oldParsed, newParsed;
0664:                Name oldParent, newParent;
0665:                String newRDN = null;
0666:                String newSuperior = null;
0667:
0668:                // assert (oldName instanceOf CompositeName);
0669:
0670:                cont.setError(this , oldName);
0671:
0672:                try {
0673:                    ensureOpen();
0674:
0675:                    // permit oldName to be empty (for processing referral contexts)
0676:                    if (oldName.isEmpty()) {
0677:                        oldParent = parser.parse("");
0678:                    } else {
0679:                        oldParsed = parser.parse(oldName.get(0)); // extract DN & parse
0680:                        oldParent = oldParsed.getPrefix(oldParsed.size() - 1);
0681:                    }
0682:
0683:                    if (newName instanceof  CompositeName) {
0684:                        newParsed = parser.parse(newName.get(0)); // extract DN & parse
0685:                    } else {
0686:                        newParsed = newName; // CompoundName/LdapName is already parsed
0687:                    }
0688:                    newParent = newParsed.getPrefix(newParsed.size() - 1);
0689:
0690:                    if (!oldParent.equals(newParent)) {
0691:                        if (!clnt.isLdapv3) {
0692:                            throw new InvalidNameException(
0693:                                    "LDAPv2 doesn't support changing "
0694:                                            + "the parent as a result of a rename");
0695:                        } else {
0696:                            newSuperior = fullyQualifiedName(newParent
0697:                                    .toString());
0698:                        }
0699:                    }
0700:
0701:                    newRDN = newParsed.get(newParsed.size() - 1);
0702:
0703:                    LdapResult answer = clnt.moddn(fullyQualifiedName(oldName),
0704:                            newRDN, deleteRDN, newSuperior, reqCtls);
0705:                    respCtls = answer.resControls; // retrieve response controls
0706:
0707:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
0708:                        processReturnCode(answer, oldName);
0709:                    }
0710:
0711:                } catch (LdapReferralException e) {
0712:
0713:                    // Record the new RDN (for use after the referral is followed).
0714:                    e.setNewRdn(newRDN);
0715:
0716:                    // Cannot continue when a referral has been received and a
0717:                    // newSuperior name was supplied (because the newSuperior is
0718:                    // relative to a naming context BEFORE the referral is followed).
0719:                    if (newSuperior != null) {
0720:                        PartialResultException pre = new PartialResultException(
0721:                                "Cannot continue referral processing when newSuperior is "
0722:                                        + "nonempty: " + newSuperior);
0723:                        pre.setRootCause(cont.fillInException(e));
0724:                        throw cont.fillInException(pre);
0725:                    }
0726:
0727:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
0728:                        throw cont.fillInException(e);
0729:
0730:                    // process the referrals sequentially
0731:                    while (true) {
0732:
0733:                        LdapReferralContext refCtx = (LdapReferralContext) e
0734:                                .getReferralContext(envprops, bindCtls);
0735:
0736:                        // repeat the original operation at the new context
0737:                        try {
0738:
0739:                            refCtx.rename(oldName, newName);
0740:                            return;
0741:
0742:                        } catch (LdapReferralException re) {
0743:                            e = re;
0744:                            continue;
0745:
0746:                        } finally {
0747:                            // Make sure we close referral context
0748:                            refCtx.close();
0749:                        }
0750:                    }
0751:
0752:                } catch (IOException e) {
0753:                    NamingException e2 = new CommunicationException(e
0754:                            .getMessage());
0755:                    e2.setRootCause(e);
0756:                    throw cont.fillInException(e2);
0757:
0758:                } catch (NamingException e) {
0759:                    throw cont.fillInException(e);
0760:                }
0761:            }
0762:
0763:            protected Context c_createSubcontext(Name name, Continuation cont)
0764:                    throws NamingException {
0765:                return c_createSubcontext(name, null, cont);
0766:            }
0767:
0768:            protected DirContext c_createSubcontext(Name name,
0769:                    Attributes attrs, Continuation cont) throws NamingException {
0770:                cont.setError(this , name);
0771:
0772:                Attributes inputAttrs = attrs;
0773:                try {
0774:                    ensureOpen();
0775:                    if (attrs == null) {
0776:                        // add structural objectclass; name needs to have "cn"
0777:                        Attribute oc = new BasicAttribute(
0778:                                Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS],
0779:                                Obj.JAVA_OBJECT_CLASSES[Obj.STRUCTURAL]);
0780:                        oc.add("top");
0781:                        attrs = new BasicAttributes(true); // case ignore
0782:                        attrs.put(oc);
0783:                    }
0784:                    String newDN = fullyQualifiedName(name);
0785:                    attrs = addRdnAttributes(newDN, attrs, inputAttrs != attrs);
0786:
0787:                    LdapEntry entry = new LdapEntry(newDN, attrs);
0788:
0789:                    LdapResult answer = clnt.add(entry, reqCtls);
0790:                    respCtls = answer.resControls; // retrieve response controls
0791:
0792:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
0793:                        processReturnCode(answer, name);
0794:                        return null;
0795:                    }
0796:
0797:                    // creation successful, get back live object
0798:                    return new LdapCtx(this , newDN);
0799:
0800:                } catch (LdapReferralException e) {
0801:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
0802:                        throw cont.fillInException(e);
0803:
0804:                    // process the referrals sequentially
0805:                    while (true) {
0806:
0807:                        LdapReferralContext refCtx = (LdapReferralContext) e
0808:                                .getReferralContext(envprops, bindCtls);
0809:
0810:                        // repeat the original operation at the new context
0811:                        try {
0812:
0813:                            return refCtx.createSubcontext(name, inputAttrs);
0814:
0815:                        } catch (LdapReferralException re) {
0816:                            e = re;
0817:                            continue;
0818:
0819:                        } finally {
0820:                            // Make sure we close referral context
0821:                            refCtx.close();
0822:                        }
0823:                    }
0824:
0825:                } catch (IOException e) {
0826:                    NamingException e2 = new CommunicationException(e
0827:                            .getMessage());
0828:                    e2.setRootCause(e);
0829:                    throw cont.fillInException(e2);
0830:
0831:                } catch (NamingException e) {
0832:                    throw cont.fillInException(e);
0833:                }
0834:            }
0835:
0836:            protected void c_destroySubcontext(Name name, Continuation cont)
0837:                    throws NamingException {
0838:                cont.setError(this , name);
0839:
0840:                try {
0841:                    ensureOpen();
0842:
0843:                    String fname = fullyQualifiedName(name);
0844:                    LdapResult answer = clnt.delete(fname, reqCtls);
0845:                    respCtls = answer.resControls; // retrieve response controls
0846:
0847:                    adjustDeleteStatus(fname, answer);
0848:
0849:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
0850:                        processReturnCode(answer, name);
0851:                    }
0852:
0853:                } catch (LdapReferralException e) {
0854:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
0855:                        throw cont.fillInException(e);
0856:
0857:                    // process the referrals sequentially
0858:                    while (true) {
0859:
0860:                        LdapReferralContext refCtx = (LdapReferralContext) e
0861:                                .getReferralContext(envprops, bindCtls);
0862:
0863:                        // repeat the original operation at the new context
0864:                        try {
0865:
0866:                            refCtx.destroySubcontext(name);
0867:                            return;
0868:                        } catch (LdapReferralException re) {
0869:                            e = re;
0870:                            continue;
0871:                        } finally {
0872:                            // Make sure we close referral context
0873:                            refCtx.close();
0874:                        }
0875:                    }
0876:                } catch (IOException e) {
0877:                    NamingException e2 = new CommunicationException(e
0878:                            .getMessage());
0879:                    e2.setRootCause(e);
0880:                    throw cont.fillInException(e2);
0881:                } catch (NamingException e) {
0882:                    throw cont.fillInException(e);
0883:                }
0884:            }
0885:
0886:            /**
0887:             * Adds attributes from RDN to attrs if not already present.
0888:             * Note that if attrs already contains an attribute by the same name,
0889:             * or if the distinguished name is empty, then leave attrs unchanged.
0890:             *
0891:             * @param dn The non-null DN of the entry to add
0892:             * @param attrs The non-null attributes of entry to add
0893:             * @param directUpdate Whether attrs can be updated directly
0894:             * @returns Non-null attributes with attributes from the RDN added
0895:             */
0896:            private static Attributes addRdnAttributes(String dn,
0897:                    Attributes attrs, boolean directUpdate)
0898:                    throws NamingException {
0899:
0900:                // Handle the empty name
0901:                if (dn.equals("")) {
0902:                    return attrs;
0903:                }
0904:
0905:                // Parse string name into list of RDNs
0906:                //List<Rdn> rdnList = (new LdapName(dn)).rdns();
0907:                List rdnList = (new LdapName(dn)).getRdns();
0908:
0909:                // Get leaf RDN
0910:                //Rdn rdn = rdnList.get(rdnList.size() - 1);
0911:                Rdn rdn = (Rdn) rdnList.get(rdnList.size() - 1);
0912:                Attributes nameAttrs = rdn.toAttributes();
0913:
0914:                // Add attributes of RDN to attrs if not already there
0915:                NamingEnumeration enum_ = nameAttrs.getAll();
0916:                Attribute nameAttr;
0917:                while (enum_.hasMore()) {
0918:                    nameAttr = (Attribute) enum_.next();
0919:
0920:                    // If attrs already has the attribute, don't change or add to it
0921:                    if (attrs.get(nameAttr.getID()) == null) {
0922:
0923:                        /**
0924:                         * When attrs.isCaseIgnored() is false, attrs.get() will
0925:                         * return null when the case mis-matches for otherwise
0926:                         * equal attrIDs.
0927:                         * As the attrIDs' case is irrelevant for LDAP, ignore 
0928:                         * the case of attrIDs even when attrs.isCaseIgnored() is
0929:                         * false. This is done by explicitly comparing the elements in
0930:                         * the enumeration of IDs with their case ignored.
0931:                         */
0932:                        if (!attrs.isCaseIgnored()
0933:                                && containsIgnoreCase(attrs.getIDs(), nameAttr
0934:                                        .getID())) {
0935:                            continue;
0936:                        }
0937:
0938:                        if (!directUpdate) {
0939:                            attrs = (Attributes) attrs.clone();
0940:                            directUpdate = true;
0941:                        }
0942:                        attrs.put(nameAttr);
0943:                    }
0944:                }
0945:
0946:                return attrs;
0947:            }
0948:
0949:            private static boolean containsIgnoreCase(
0950:                    NamingEnumeration enumStr, String str)
0951:                    throws NamingException {
0952:                String strEntry;
0953:
0954:                while (enumStr.hasMore()) {
0955:                    strEntry = (String) enumStr.next();
0956:                    if (strEntry.equalsIgnoreCase(str)) {
0957:                        return true;
0958:                    }
0959:                }
0960:                return false;
0961:            }
0962:
0963:            private void adjustDeleteStatus(String fname, LdapResult answer) {
0964:                if (answer.status == LdapClient.LDAP_NO_SUCH_OBJECT
0965:                        && answer.matchedDN != null) {
0966:                    try {
0967:                        // %%% RL: are there any implications for referrals?
0968:
0969:                        Name orig = parser.parse(fname);
0970:                        Name matched = parser.parse(answer.matchedDN);
0971:                        if ((orig.size() - matched.size()) == 1)
0972:                            answer.status = LdapClient.LDAP_SUCCESS;
0973:                    } catch (NamingException e) {
0974:                    }
0975:                }
0976:            }
0977:
0978:            /*
0979:             * Append the the second Vector onto the first Vector
0980:             * (v2 must be non-null)
0981:             */
0982:            private static Vector appendVector(Vector v1, Vector v2) {
0983:                if (v1 == null) {
0984:                    v1 = v2;
0985:                } else {
0986:                    for (int i = 0; i < v2.size(); i++) {
0987:                        v1.addElement(v2.elementAt(i));
0988:                    }
0989:                }
0990:                return v1;
0991:            }
0992:
0993:            // ------------- Lookups and Browsing -------------------------
0994:            // lookup/lookupLink
0995:            // list/listBindings
0996:
0997:            protected Object c_lookupLink(Name name, Continuation cont)
0998:                    throws NamingException {
0999:                return c_lookup(name, cont);
1000:            }
1001:
1002:            protected Object c_lookup(Name name, Continuation cont)
1003:                    throws NamingException {
1004:                cont.setError(this , name);
1005:                Object obj = null;
1006:                Attributes attrs;
1007:
1008:                try {
1009:                    SearchControls cons = new SearchControls();
1010:                    cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1011:                    cons.setReturningAttributes(null); // ask for all attributes
1012:                    cons.setReturningObjFlag(true); // need values to construct obj
1013:
1014:                    LdapResult answer = doSearchOnce(name, "(objectClass=*)",
1015:                            cons, true);
1016:                    respCtls = answer.resControls; // retrieve response controls
1017:
1018:                    // should get back 1 SearchResponse and 1 SearchResult
1019:
1020:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
1021:                        processReturnCode(answer, name);
1022:                    }
1023:
1024:                    if (answer.entries == null || answer.entries.size() != 1) {
1025:                        // found it but got no attributes
1026:                        attrs = new BasicAttributes(LdapClient.caseIgnore);
1027:                    } else {
1028:                        LdapEntry entry = (LdapEntry) answer.entries
1029:                                .elementAt(0);
1030:                        attrs = entry.attributes;
1031:
1032:                        Vector entryCtls = entry.respCtls; // retrieve entry controls
1033:                        if (entryCtls != null) {
1034:                            appendVector(respCtls, entryCtls); // concatenate controls
1035:                        }
1036:                    }
1037:
1038:                    if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
1039:                        // serialized object or object reference
1040:                        obj = Obj.decodeObject(attrs);
1041:                    }
1042:                    if (obj == null) {
1043:                        obj = new LdapCtx(this , fullyQualifiedName(name));
1044:                    }
1045:                } catch (LdapReferralException e) {
1046:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1047:                        throw cont.fillInException(e);
1048:
1049:                    // process the referrals sequentially
1050:                    while (true) {
1051:
1052:                        LdapReferralContext refCtx = (LdapReferralContext) e
1053:                                .getReferralContext(envprops, bindCtls);
1054:                        // repeat the original operation at the new context
1055:                        try {
1056:
1057:                            return refCtx.lookup(name);
1058:
1059:                        } catch (LdapReferralException re) {
1060:                            e = re;
1061:                            continue;
1062:
1063:                        } finally {
1064:                            // Make sure we close referral context
1065:                            refCtx.close();
1066:                        }
1067:                    }
1068:
1069:                } catch (NamingException e) {
1070:                    throw cont.fillInException(e);
1071:                }
1072:
1073:                try {
1074:                    return DirectoryManager.getObjectInstance(obj, name, this ,
1075:                            envprops, attrs);
1076:
1077:                } catch (NamingException e) {
1078:                    throw cont.fillInException(e);
1079:
1080:                } catch (Exception e) {
1081:                    NamingException e2 = new NamingException(
1082:                            "problem generating object using object factory");
1083:                    e2.setRootCause(e);
1084:                    throw cont.fillInException(e2);
1085:                }
1086:            }
1087:
1088:            protected NamingEnumeration c_list(Name name, Continuation cont)
1089:                    throws NamingException {
1090:                SearchControls cons = new SearchControls();
1091:                String[] classAttrs = new String[2];
1092:
1093:                classAttrs[0] = Obj.JAVA_ATTRIBUTES[Obj.OBJECT_CLASS];
1094:                classAttrs[1] = Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME];
1095:                cons.setReturningAttributes(classAttrs);
1096:
1097:                // set this flag to override the typesOnly flag
1098:                cons.setReturningObjFlag(true);
1099:
1100:                cont.setError(this , name);
1101:
1102:                LdapResult answer = null;
1103:
1104:                try {
1105:                    answer = doSearch(name, "(objectClass=*)", cons, true, true);
1106:
1107:                    // list result may contain continuation references
1108:                    if ((answer.status != LdapClient.LDAP_SUCCESS)
1109:                            || (answer.referrals != null)) {
1110:                        processReturnCode(answer, name);
1111:                    }
1112:
1113:                    return new LdapNamingEnumeration(this , answer, name, cont);
1114:
1115:                } catch (LdapReferralException e) {
1116:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1117:                        throw cont.fillInException(e);
1118:
1119:                    // process the referrals sequentially
1120:                    while (true) {
1121:
1122:                        LdapReferralContext refCtx = (LdapReferralContext) e
1123:                                .getReferralContext(envprops, bindCtls);
1124:
1125:                        // repeat the original operation at the new context
1126:                        try {
1127:
1128:                            return refCtx.list(name);
1129:
1130:                        } catch (LdapReferralException re) {
1131:                            e = re;
1132:                            continue;
1133:
1134:                        } finally {
1135:                            // Make sure we close referral context
1136:                            refCtx.close();
1137:                        }
1138:                    }
1139:
1140:                } catch (LimitExceededException e) {
1141:                    LdapNamingEnumeration res = new LdapNamingEnumeration(this ,
1142:                            answer, name, cont);
1143:
1144:                    res.setNamingException((LimitExceededException) cont
1145:                            .fillInException(e));
1146:                    return res;
1147:
1148:                } catch (PartialResultException e) {
1149:                    LdapNamingEnumeration res = new LdapNamingEnumeration(this ,
1150:                            answer, name, cont);
1151:
1152:                    res.setNamingException((PartialResultException) cont
1153:                            .fillInException(e));
1154:                    return res;
1155:
1156:                } catch (NamingException e) {
1157:                    throw cont.fillInException(e);
1158:                }
1159:            }
1160:
1161:            protected NamingEnumeration c_listBindings(Name name,
1162:                    Continuation cont) throws NamingException {
1163:
1164:                SearchControls cons = new SearchControls();
1165:                cons.setReturningAttributes(null); // ask for all attributes
1166:                cons.setReturningObjFlag(true); // need values to construct obj
1167:
1168:                cont.setError(this , name);
1169:
1170:                LdapResult answer = null;
1171:
1172:                try {
1173:                    answer = doSearch(name, "(objectClass=*)", cons, true, true);
1174:
1175:                    // listBindings result may contain continuation references
1176:                    if ((answer.status != LdapClient.LDAP_SUCCESS)
1177:                            || (answer.referrals != null)) {
1178:                        processReturnCode(answer, name);
1179:                    }
1180:
1181:                    return new LdapBindingEnumeration(this , answer, name, cont);
1182:
1183:                } catch (LdapReferralException e) {
1184:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1185:                        throw cont.fillInException(e);
1186:
1187:                    // process the referrals sequentially
1188:                    while (true) {
1189:
1190:                        LdapReferralContext refCtx = (LdapReferralContext) e
1191:                                .getReferralContext(envprops, bindCtls);
1192:
1193:                        // repeat the original operation at the new context
1194:                        try {
1195:
1196:                            return refCtx.listBindings(name);
1197:
1198:                        } catch (LdapReferralException re) {
1199:                            e = re;
1200:                            continue;
1201:
1202:                        } finally {
1203:                            // Make sure we close referral context
1204:                            refCtx.close();
1205:                        }
1206:                    }
1207:                } catch (LimitExceededException e) {
1208:                    LdapBindingEnumeration res = new LdapBindingEnumeration(
1209:                            this , answer, name, cont);
1210:
1211:                    res.setNamingException((LimitExceededException) cont
1212:                            .fillInException(e));
1213:                    return res;
1214:
1215:                } catch (PartialResultException e) {
1216:                    LdapBindingEnumeration res = new LdapBindingEnumeration(
1217:                            this , answer, name, cont);
1218:
1219:                    res.setNamingException((PartialResultException) cont
1220:                            .fillInException(e));
1221:                    return res;
1222:
1223:                } catch (NamingException e) {
1224:                    throw cont.fillInException(e);
1225:                }
1226:            }
1227:
1228:            // --------------- Name-related Methods -----------------------
1229:            // -- getNameParser/getNameInNamespace/composeName
1230:
1231:            protected NameParser c_getNameParser(Name name, Continuation cont)
1232:                    throws NamingException {
1233:                // ignore name, always return same parser
1234:                cont.setSuccess();
1235:                return parser;
1236:            }
1237:
1238:            public String getNameInNamespace() {
1239:                return currentDN;
1240:            }
1241:
1242:            public Name composeName(Name name, Name prefix)
1243:                    throws NamingException {
1244:                Name result;
1245:
1246:                // Handle compound names.  A pair of LdapNames is an easy case.
1247:                if ((name instanceof  LdapName) && (prefix instanceof  LdapName)) {
1248:                    result = (Name) (prefix.clone());
1249:                    result.addAll(name);
1250:                    return new CompositeName().add(result.toString());
1251:                }
1252:                if (!(name instanceof  CompositeName)) {
1253:                    name = new CompositeName().add(name.toString());
1254:                }
1255:                if (!(prefix instanceof  CompositeName)) {
1256:                    prefix = new CompositeName().add(prefix.toString());
1257:                }
1258:
1259:                int prefixLast = prefix.size() - 1;
1260:
1261:                if (name.isEmpty() || prefix.isEmpty()
1262:                        || name.get(0).equals("")
1263:                        || prefix.get(prefixLast).equals("")) {
1264:                    return super .composeName(name, prefix);
1265:                }
1266:
1267:                result = (Name) (prefix.clone());
1268:                result.addAll(name);
1269:
1270:                if (parentIsLdapCtx) {
1271:                    String ldapComp = concatNames(result.get(prefixLast + 1),
1272:                            result.get(prefixLast));
1273:                    result.remove(prefixLast + 1);
1274:                    result.remove(prefixLast);
1275:                    result.add(prefixLast, ldapComp);
1276:                }
1277:                return result;
1278:            }
1279:
1280:            private String fullyQualifiedName(Name rel) {
1281:                return rel.isEmpty() ? currentDN : fullyQualifiedName(rel
1282:                        .get(0));
1283:            }
1284:
1285:            private String fullyQualifiedName(String rel) {
1286:                return (concatNames(rel, currentDN));
1287:            }
1288:
1289:            // used by LdapSearchEnumeration
1290:            private static String concatNames(String lesser, String greater) {
1291:                if (lesser == null || lesser.equals("")) {
1292:                    return greater;
1293:                } else if (greater == null || greater.equals("")) {
1294:                    return lesser;
1295:                } else {
1296:                    return (lesser + "," + greater);
1297:                }
1298:            }
1299:
1300:            // --------------- Reading and Updating Attributes
1301:            // getAttributes/modifyAttributes
1302:
1303:            protected Attributes c_getAttributes(Name name, String[] attrIds,
1304:                    Continuation cont) throws NamingException {
1305:                cont.setError(this , name);
1306:
1307:                SearchControls cons = new SearchControls();
1308:                cons.setSearchScope(SearchControls.OBJECT_SCOPE);
1309:                cons.setReturningAttributes(attrIds);
1310:
1311:                try {
1312:                    LdapResult answer = doSearchOnce(name, "(objectClass=*)",
1313:                            cons, true);
1314:                    respCtls = answer.resControls; // retrieve response controls
1315:
1316:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
1317:                        processReturnCode(answer, name);
1318:                    }
1319:
1320:                    if (answer.entries == null || answer.entries.size() != 1) {
1321:                        return new BasicAttributes(LdapClient.caseIgnore);
1322:                    }
1323:
1324:                    // get attributes from result
1325:                    LdapEntry entry = (LdapEntry) answer.entries.elementAt(0);
1326:
1327:                    Vector entryCtls = entry.respCtls; // retrieve entry controls
1328:                    if (entryCtls != null) {
1329:                        appendVector(respCtls, entryCtls); // concatenate controls
1330:                    }
1331:
1332:                    // do this so attributes can find their schema
1333:                    setParents(entry.attributes, (Name) name.clone());
1334:
1335:                    return (entry.attributes);
1336:
1337:                } catch (LdapReferralException e) {
1338:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1339:                        throw cont.fillInException(e);
1340:
1341:                    // process the referrals sequentially
1342:                    while (true) {
1343:
1344:                        LdapReferralContext refCtx = (LdapReferralContext) e
1345:                                .getReferralContext(envprops, bindCtls);
1346:
1347:                        // repeat the original operation at the new context
1348:                        try {
1349:
1350:                            return refCtx.getAttributes(name, attrIds);
1351:
1352:                        } catch (LdapReferralException re) {
1353:                            e = re;
1354:                            continue;
1355:
1356:                        } finally {
1357:                            // Make sure we close referral context
1358:                            refCtx.close();
1359:                        }
1360:                    }
1361:
1362:                } catch (NamingException e) {
1363:                    throw cont.fillInException(e);
1364:                }
1365:            }
1366:
1367:            protected void c_modifyAttributes(Name name, int mod_op,
1368:                    Attributes attrs, Continuation cont) throws NamingException {
1369:
1370:                cont.setError(this , name);
1371:
1372:                try {
1373:                    ensureOpen();
1374:
1375:                    if (attrs == null || attrs.size() == 0) {
1376:                        return; // nothing to do
1377:                    }
1378:                    String newDN = fullyQualifiedName(name);
1379:                    int jmod_op = convertToLdapModCode(mod_op);
1380:
1381:                    // construct mod list
1382:                    int[] jmods = new int[attrs.size()];
1383:                    Attribute[] jattrs = new Attribute[attrs.size()];
1384:
1385:                    NamingEnumeration ae = attrs.getAll();
1386:                    for (int i = 0; i < jmods.length && ae.hasMore(); i++) {
1387:                        jmods[i] = jmod_op;
1388:                        jattrs[i] = (Attribute) ae.next();
1389:                    }
1390:
1391:                    LdapResult answer = clnt.modify(newDN, jmods, jattrs,
1392:                            reqCtls);
1393:                    respCtls = answer.resControls; // retrieve response controls
1394:
1395:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
1396:                        processReturnCode(answer, name);
1397:                        return;
1398:                    }
1399:
1400:                } catch (LdapReferralException e) {
1401:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1402:                        throw cont.fillInException(e);
1403:
1404:                    // process the referrals sequentially
1405:                    while (true) {
1406:
1407:                        LdapReferralContext refCtx = (LdapReferralContext) e
1408:                                .getReferralContext(envprops, bindCtls);
1409:
1410:                        // repeat the original operation at the new context
1411:                        try {
1412:
1413:                            refCtx.modifyAttributes(name, mod_op, attrs);
1414:                            return;
1415:
1416:                        } catch (LdapReferralException re) {
1417:                            e = re;
1418:                            continue;
1419:
1420:                        } finally {
1421:                            // Make sure we close referral context
1422:                            refCtx.close();
1423:                        }
1424:                    }
1425:
1426:                } catch (IOException e) {
1427:                    NamingException e2 = new CommunicationException(e
1428:                            .getMessage());
1429:                    e2.setRootCause(e);
1430:                    throw cont.fillInException(e2);
1431:
1432:                } catch (NamingException e) {
1433:                    throw cont.fillInException(e);
1434:                }
1435:            }
1436:
1437:            protected void c_modifyAttributes(Name name,
1438:                    ModificationItem[] mods, Continuation cont)
1439:                    throws NamingException {
1440:                cont.setError(this , name);
1441:
1442:                try {
1443:                    ensureOpen();
1444:
1445:                    if (mods == null || mods.length == 0) {
1446:                        return; // nothing to do
1447:                    }
1448:                    String newDN = fullyQualifiedName(name);
1449:
1450:                    // construct mod list
1451:                    int[] jmods = new int[mods.length];
1452:                    Attribute[] jattrs = new Attribute[mods.length];
1453:                    ModificationItem mod;
1454:                    for (int i = 0; i < jmods.length; i++) {
1455:                        mod = mods[i];
1456:                        jmods[i] = convertToLdapModCode(mod.getModificationOp());
1457:                        jattrs[i] = mod.getAttribute();
1458:                    }
1459:
1460:                    LdapResult answer = clnt.modify(newDN, jmods, jattrs,
1461:                            reqCtls);
1462:                    respCtls = answer.resControls; // retrieve response controls
1463:
1464:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
1465:                        processReturnCode(answer, name);
1466:                    }
1467:
1468:                } catch (LdapReferralException e) {
1469:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1470:                        throw cont.fillInException(e);
1471:
1472:                    // process the referrals sequentially
1473:                    while (true) {
1474:
1475:                        LdapReferralContext refCtx = (LdapReferralContext) e
1476:                                .getReferralContext(envprops, bindCtls);
1477:
1478:                        // repeat the original operation at the new context
1479:                        try {
1480:
1481:                            refCtx.modifyAttributes(name, mods);
1482:                            return;
1483:
1484:                        } catch (LdapReferralException re) {
1485:                            e = re;
1486:                            continue;
1487:
1488:                        } finally {
1489:                            // Make sure we close referral context
1490:                            refCtx.close();
1491:                        }
1492:                    }
1493:
1494:                } catch (IOException e) {
1495:                    NamingException e2 = new CommunicationException(e
1496:                            .getMessage());
1497:                    e2.setRootCause(e);
1498:                    throw cont.fillInException(e2);
1499:
1500:                } catch (NamingException e) {
1501:                    throw cont.fillInException(e);
1502:                }
1503:            }
1504:
1505:            private static int convertToLdapModCode(int mod_op) {
1506:                switch (mod_op) {
1507:                case DirContext.ADD_ATTRIBUTE:
1508:                    return (LdapClient.ADD);
1509:
1510:                case DirContext.REPLACE_ATTRIBUTE:
1511:                    return (LdapClient.REPLACE);
1512:
1513:                case DirContext.REMOVE_ATTRIBUTE:
1514:                    return (LdapClient.DELETE);
1515:
1516:                default:
1517:                    throw new IllegalArgumentException(
1518:                            "Invalid modification code");
1519:                }
1520:            }
1521:
1522:            // ------------------- Schema -----------------------
1523:
1524:            protected DirContext c_getSchema(Name name, Continuation cont)
1525:                    throws NamingException {
1526:                cont.setError(this , name);
1527:                try {
1528:                    return getSchemaTree(name);
1529:
1530:                } catch (NamingException e) {
1531:                    throw cont.fillInException(e);
1532:                }
1533:            }
1534:
1535:            protected DirContext c_getSchemaClassDefinition(Name name,
1536:                    Continuation cont) throws NamingException {
1537:                cont.setError(this , name);
1538:
1539:                try {
1540:                    // retrieve the objectClass attribute from LDAP
1541:                    Attribute objectClassAttr = c_getAttributes(name,
1542:                            new String[] { "objectclass" }, cont).get(
1543:                            "objectclass");
1544:                    if (objectClassAttr == null || objectClassAttr.size() == 0) {
1545:                        return EMPTY_SCHEMA;
1546:                    }
1547:
1548:                    // retrieve the root of the ObjectClass schema tree
1549:                    Context ocSchema = (Context) c_getSchema(name, cont)
1550:                            .lookup(
1551:                                    LdapSchemaParser.OBJECTCLASS_DEFINITION_NAME);
1552:
1553:                    // create a context to hold the schema objects representing the object
1554:                    // classes
1555:                    HierMemDirCtx objectClassCtx = new HierMemDirCtx();
1556:                    DirContext objectClassDef;
1557:                    String objectClassName;
1558:                    for (Enumeration objectClasses = objectClassAttr.getAll(); objectClasses
1559:                            .hasMoreElements();) {
1560:                        objectClassName = (String) objectClasses.nextElement();
1561:                        // %%% Should we fail if not found, or just continue?
1562:                        objectClassDef = (DirContext) ocSchema
1563:                                .lookup(objectClassName);
1564:                        objectClassCtx.bind(objectClassName, objectClassDef);
1565:                    }
1566:
1567:                    // Make context read-only
1568:                    objectClassCtx.setReadOnly(new SchemaViolationException(
1569:                            "Cannot update schema object"));
1570:                    return (DirContext) objectClassCtx;
1571:
1572:                } catch (NamingException e) {
1573:                    throw cont.fillInException(e);
1574:                }
1575:            }
1576:
1577:            /*
1578:             * getSchemaTree first looks to see if we have already built a 
1579:             * schema tree for the given entry. If not, it builds a new one and
1580:             * stores it in our private hash table
1581:             */
1582:            private DirContext getSchemaTree(Name name) throws NamingException {
1583:                String subschemasubentry = getSchemaEntry(name, true);
1584:
1585:                DirContext schemaTree = (DirContext) schemaTrees
1586:                        .get(subschemasubentry);
1587:
1588:                if (schemaTree == null) {
1589:                    if (debug) {
1590:                        System.err.println("LdapCtx: building new schema tree "
1591:                                + this );
1592:                    }
1593:                    schemaTree = buildSchemaTree(subschemasubentry);
1594:                    schemaTrees.put(subschemasubentry, schemaTree);
1595:                }
1596:
1597:                return schemaTree;
1598:            }
1599:
1600:            /*
1601:             * buildSchemaTree builds the schema tree corresponding to the
1602:             * given subschemasubentree
1603:             */
1604:            private DirContext buildSchemaTree(String subschemasubentry)
1605:                    throws NamingException {
1606:
1607:                // get the schema entry itself
1608:                // DO ask for return object here because we need it to
1609:                // create context. Since asking for all attrs, we won't
1610:                // be transmitting any specific attrIDs (like Java-specific ones).
1611:                SearchControls constraints = new SearchControls(
1612:                        SearchControls.OBJECT_SCOPE, 0, 0, /* count and time limits */
1613:                        SCHEMA_ATTRIBUTES /* return schema attrs */,
1614:                        true /* return obj */, false /*deref link */);
1615:
1616:                Name sse = (new CompositeName()).add(subschemasubentry);
1617:                NamingEnumeration results = searchAux(sse,
1618:                        "(objectClass=subschema)", constraints, false, true,
1619:                        new Continuation());
1620:
1621:                if (!results.hasMore()) {
1622:                    throw new OperationNotSupportedException(
1623:                            "Cannot get read subschemasubentry: "
1624:                                    + subschemasubentry);
1625:                }
1626:                SearchResult result = (SearchResult) results.next();
1627:                results.close();
1628:
1629:                Object obj = result.getObject();
1630:                if (!(obj instanceof  LdapCtx)) {
1631:                    throw new NamingException(
1632:                            "Cannot get schema object as DirContext: "
1633:                                    + subschemasubentry);
1634:                }
1635:
1636:                return LdapSchemaCtx.createSchemaTree(envprops,
1637:                        subschemasubentry, (LdapCtx) obj /* schema entry */,
1638:                        result.getAttributes() /* schema attributes */,
1639:                        netscapeSchemaBug);
1640:            }
1641:
1642:            /*
1643:             * getSchemaEntree returns the DN of the subschemasubentree for the
1644:             * given entree. It first looks to see if the given entry has
1645:             * a subschema different from that of the root DIT (by looking for
1646:             * a "subschemasubentry" attribute). If it doesn't find one, it returns
1647:             * the one for the root of the DIT (by looking for the root's
1648:             * "subschemasubentry" attribute).
1649:             *
1650:             * This function is called regardless of the server's version, since
1651:             * an administrator may have setup the server to support client schema
1652:             * queries. If this function trys a serarch on a v2 server that
1653:             * doesn't support schema, one of these two things will happen:
1654:             * 1) It will get an exception when querying the root DSE
1655:             * 2) It will not find a subschemasubentry on the root DSE
1656:             * If either of these things occur and the server is not v3, we
1657:             * throw OperationNotSupported.
1658:             *
1659:             * the relative flag tells whether the given name is relative to this
1660:             * context.
1661:             */
1662:            private String getSchemaEntry(Name name, boolean relative)
1663:                    throws NamingException {
1664:
1665:                // Asks for operational attribute "subschemasubentry"
1666:                SearchControls constraints = new SearchControls(
1667:                        SearchControls.OBJECT_SCOPE,
1668:                        0,
1669:                        0, /* count and time limits */
1670:                        new String[] { "subschemasubentry" } /* attr to return */,
1671:                        false /* returning obj */, false /* deref link */);
1672:
1673:                NamingEnumeration results;
1674:                try {
1675:                    results = searchAux(name, "objectclass=*", constraints,
1676:                            relative, true, new Continuation());
1677:
1678:                } catch (NamingException ne) {
1679:                    if (!clnt.isLdapv3 && currentDN.length() == 0
1680:                            && name.isEmpty()) {
1681:                        // we got an error looking for a root entry on an ldapv2
1682:                        // server. The server must not support schema.
1683:                        throw new OperationNotSupportedException(
1684:                                "Cannot get schema information from server");
1685:                    } else {
1686:                        throw ne;
1687:                    }
1688:                }
1689:
1690:                if (!results.hasMoreElements()) {
1691:                    throw new ConfigurationException(
1692:                            "Requesting schema of nonexistent entry: " + name);
1693:                }
1694:
1695:                SearchResult result = (SearchResult) results.next();
1696:                results.close();
1697:
1698:                Attribute schemaEntryAttr = result.getAttributes().get(
1699:                        "subschemasubentry");
1700:                //System.err.println("schema entry attrs: " + schemaEntryAttr);
1701:
1702:                if (schemaEntryAttr == null || schemaEntryAttr.size() < 0) {
1703:                    if (currentDN.length() == 0 && name.isEmpty()) {
1704:                        // the server doesn't have a subschemasubentry in its root DSE.
1705:                        // therefore, it doesn't support schema.
1706:                        throw new OperationNotSupportedException(
1707:                                "Cannot read subschemasubentry of root DSE");
1708:                    } else {
1709:                        return getSchemaEntry(new CompositeName(), false);
1710:                    }
1711:                }
1712:
1713:                return (String) (schemaEntryAttr.get()); // return schema entry name
1714:            }
1715:
1716:            // package-private; used by search enum.
1717:            // Set attributes to point to this context in case some one
1718:            // asked for their schema
1719:            void setParents(Attributes attrs, Name name) throws NamingException {
1720:                NamingEnumeration ae = attrs.getAll();
1721:                while (ae.hasMore()) {
1722:                    ((LdapAttribute) ae.next()).setParent(this , name);
1723:                }
1724:            }
1725:
1726:            /*
1727:             * Returns the URL associated with this context; used by LdapAttribute
1728:             * after deserialization to get pointer to this context.
1729:             */
1730:            String getURL() {
1731:                if (url == null) {
1732:                    url = LdapURL.toUrlString(hostname, port_number, currentDN,
1733:                            hasLdapsScheme);
1734:                }
1735:
1736:                return url;
1737:            }
1738:
1739:            // --------------------- Searches -----------------------------
1740:            protected NamingEnumeration c_search(Name name,
1741:                    Attributes matchingAttributes, Continuation cont)
1742:                    throws NamingException {
1743:                return c_search(name, matchingAttributes, null, cont);
1744:            }
1745:
1746:            protected NamingEnumeration c_search(Name name,
1747:                    Attributes matchingAttributes, String[] attributesToReturn,
1748:                    Continuation cont) throws NamingException {
1749:                SearchControls cons = new SearchControls();
1750:                cons.setReturningAttributes(attributesToReturn);
1751:                String filter;
1752:                try {
1753:                    filter = SearchFilter.format(matchingAttributes);
1754:                } catch (NamingException e) {
1755:                    cont.setError(this , name);
1756:                    throw cont.fillInException(e);
1757:                }
1758:                return c_search(name, filter, cons, cont);
1759:            }
1760:
1761:            protected NamingEnumeration c_search(Name name, String filter,
1762:                    SearchControls cons, Continuation cont)
1763:                    throws NamingException {
1764:                return searchAux(name, filter, cloneSearchControls(cons), true,
1765:                        true, cont);
1766:            }
1767:
1768:            protected NamingEnumeration c_search(Name name, String filterExpr,
1769:                    Object[] filterArgs, SearchControls cons, Continuation cont)
1770:                    throws NamingException {
1771:                String strfilter;
1772:                try {
1773:                    strfilter = SearchFilter.format(filterExpr, filterArgs);
1774:                } catch (NamingException e) {
1775:                    cont.setError(this , name);
1776:                    throw cont.fillInException(e);
1777:                }
1778:                return c_search(name, strfilter, cons, cont);
1779:            }
1780:
1781:            // Used by NamingNotifier
1782:            NamingEnumeration searchAux(Name name, String filter,
1783:                    SearchControls cons, boolean relative,
1784:                    boolean waitForReply, Continuation cont)
1785:                    throws NamingException {
1786:
1787:                LdapResult answer = null;
1788:                String[] tokens = new String[2]; // stores ldap compare op. values
1789:                String[] reqAttrs; // remember what was asked
1790:
1791:                if (cons == null) {
1792:                    cons = new SearchControls();
1793:                }
1794:                reqAttrs = cons.getReturningAttributes();
1795:
1796:                // if objects are requested then request the Java attributes too
1797:                // so that the objects can be constructed
1798:                if (cons.getReturningObjFlag()) {
1799:                    if (reqAttrs != null) {
1800:
1801:                        // check for presence of "*" (user attributes wildcard)
1802:                        boolean hasWildcard = false;
1803:                        for (int i = reqAttrs.length - 1; i >= 0; i--) {
1804:                            if (reqAttrs[i].equals("*")) {
1805:                                hasWildcard = true;
1806:                                break;
1807:                            }
1808:                        }
1809:                        if (!hasWildcard) {
1810:                            String[] totalAttrs = new String[reqAttrs.length
1811:                                    + Obj.JAVA_ATTRIBUTES.length];
1812:                            System.arraycopy(reqAttrs, 0, totalAttrs, 0,
1813:                                    reqAttrs.length);
1814:                            System.arraycopy(Obj.JAVA_ATTRIBUTES, 0,
1815:                                    totalAttrs, reqAttrs.length,
1816:                                    Obj.JAVA_ATTRIBUTES.length);
1817:
1818:                            cons.setReturningAttributes(totalAttrs);
1819:                        }
1820:                    }
1821:                }
1822:
1823:                LdapCtx.SearchArgs args = new LdapCtx.SearchArgs(name, filter,
1824:                        cons, reqAttrs);
1825:
1826:                cont.setError(this , name);
1827:                try {
1828:                    // see if this can be done as a compare, otherwise do a search
1829:                    if (searchToCompare(filter, cons, tokens)) {
1830:                        //System.err.println("compare triggered");
1831:                        answer = compare(name, tokens[0], tokens[1]);
1832:                        if (!(answer
1833:                                .compareToSearchResult(fullyQualifiedName(name)))) {
1834:                            processReturnCode(answer, name);
1835:                        }
1836:                    } else {
1837:                        answer = doSearch(name, filter, cons, relative,
1838:                                waitForReply);
1839:                        // search result may contain referrals
1840:                        processReturnCode(answer, name);
1841:                    }
1842:                    return new LdapSearchEnumeration(this , answer,
1843:                            fullyQualifiedName(name), args, cont);
1844:
1845:                } catch (LdapReferralException e) {
1846:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
1847:                        throw cont.fillInException(e);
1848:
1849:                    // process the referrals sequentially
1850:                    while (true) {
1851:
1852:                        LdapReferralContext refCtx = (LdapReferralContext) e
1853:                                .getReferralContext(envprops, bindCtls);
1854:
1855:                        // repeat the original operation at the new context
1856:                        try {
1857:
1858:                            return refCtx.search(name, filter, cons);
1859:
1860:                        } catch (LdapReferralException re) {
1861:                            e = re;
1862:                            continue;
1863:
1864:                        } finally {
1865:                            // Make sure we close referral context
1866:                            refCtx.close();
1867:                        }
1868:                    }
1869:
1870:                } catch (LimitExceededException e) {
1871:                    LdapSearchEnumeration res = new LdapSearchEnumeration(this ,
1872:                            answer, fullyQualifiedName(name), args, cont);
1873:                    res.setNamingException(e);
1874:                    return res;
1875:
1876:                } catch (PartialResultException e) {
1877:                    LdapSearchEnumeration res = new LdapSearchEnumeration(this ,
1878:                            answer, fullyQualifiedName(name), args, cont);
1879:
1880:                    res.setNamingException(e);
1881:                    return res;
1882:
1883:                } catch (IOException e) {
1884:                    NamingException e2 = new CommunicationException(e
1885:                            .getMessage());
1886:                    e2.setRootCause(e);
1887:                    throw cont.fillInException(e2);
1888:
1889:                } catch (NamingException e) {
1890:                    throw cont.fillInException(e);
1891:                }
1892:            }
1893:
1894:            LdapResult getSearchReply(LdapClient eClnt, LdapResult res)
1895:                    throws NamingException {
1896:                // ensureOpen() won't work here because
1897:                // session was associated with previous connection
1898:
1899:                // %%% RL: we can actually allow the enumeration to continue
1900:                // using the old handle but other weird things might happen
1901:                // when we hit a referral
1902:                if (clnt != eClnt) {
1903:                    throw new CommunicationException(
1904:                            "Context's connection changed; unable to continue enumeration");
1905:                }
1906:
1907:                try {
1908:                    return eClnt.getSearchReply(batchSize, res, binaryAttrs);
1909:                } catch (IOException e) {
1910:                    NamingException e2 = new CommunicationException(e
1911:                            .getMessage());
1912:                    e2.setRootCause(e);
1913:                    throw e2;
1914:                }
1915:            }
1916:
1917:            // Perform a search. Expect 1 SearchResultEntry and the SearchResultDone.
1918:            private LdapResult doSearchOnce(Name name, String filter,
1919:                    SearchControls cons, boolean relative)
1920:                    throws NamingException {
1921:
1922:                int savedBatchSize = batchSize;
1923:                batchSize = 2; // 2 protocol elements
1924:
1925:                LdapResult answer = doSearch(name, filter, cons, relative, true);
1926:
1927:                batchSize = savedBatchSize;
1928:                return answer;
1929:            }
1930:
1931:            private LdapResult doSearch(Name name, String filter,
1932:                    SearchControls cons, boolean relative,
1933:                    boolean waitFirstReply) throws NamingException {
1934:                ensureOpen();
1935:                try {
1936:                    int scope;
1937:
1938:                    switch (cons.getSearchScope()) {
1939:                    case SearchControls.OBJECT_SCOPE:
1940:                        scope = LdapClient.SCOPE_BASE_OBJECT;
1941:                        break;
1942:                    default:
1943:                    case SearchControls.ONELEVEL_SCOPE:
1944:                        scope = LdapClient.SCOPE_ONE_LEVEL;
1945:                        break;
1946:                    case SearchControls.SUBTREE_SCOPE:
1947:                        scope = LdapClient.SCOPE_SUBTREE;
1948:                        break;
1949:                    }
1950:
1951:                    // If cons.getReturningObjFlag() then caller should already
1952:                    // have make sure to request the appropriate attrs
1953:
1954:                    String[] retattrs = cons.getReturningAttributes();
1955:                    if (retattrs != null && retattrs.length == 0) {
1956:                        // Ldap treats null and empty array the same
1957:                        // need to replace with single element array
1958:                        retattrs = new String[1];
1959:                        retattrs[0] = "1.1";
1960:                    }
1961:
1962:                    String nm = (relative ? fullyQualifiedName(name) : (name
1963:                            .isEmpty() ? "" : name.get(0)));
1964:
1965:                    // JNDI unit is milliseconds, LDAP unit is seconds.
1966:                    // Zero means no limit.
1967:                    int msecLimit = cons.getTimeLimit();
1968:                    int secLimit = 0;
1969:
1970:                    if (msecLimit > 0) {
1971:                        secLimit = (msecLimit / 1000) + 1;
1972:                    }
1973:
1974:                    LdapResult answer = clnt.search(nm, scope, derefAliases,
1975:                            (int) cons.getCountLimit(), secLimit, cons
1976:                                    .getReturningObjFlag() ? false : typesOnly,
1977:                            retattrs, filter, batchSize, reqCtls, binaryAttrs,
1978:                            waitFirstReply);
1979:                    respCtls = answer.resControls; // retrieve response controls
1980:                    return answer;
1981:
1982:                } catch (IOException e) {
1983:                    NamingException e2 = new CommunicationException(e
1984:                            .getMessage());
1985:                    e2.setRootCause(e);
1986:                    throw e2;
1987:                }
1988:            }
1989:
1990:            /*
1991:             * Certain simple JNDI searches are automatically converted to
1992:             * LDAP compare operations by the LDAP service provider. A search
1993:             * is converted to a compare iff:
1994:             *
1995:             *    - the scope is set to OBJECT_SCOPE
1996:             *    - the filter string contains a simple assertion: "<type>=<value>"
1997:             *    - the returning attributes list is present but empty
1998:             */
1999:
2000:            // returns true if a search can be caried out as a compare, and sets
2001:            // tokens[0] and tokens[1] to the type and value respectively.
2002:            // e.g. filter "cn=Jon Ruiz" becomes, type "cn" and value "Jon Ruiz"
2003:            // This function uses the documents JNDI Compare example as a model
2004:            // for when to turn a search into a compare.
2005:            private static boolean searchToCompare(String filter,
2006:                    SearchControls cons, String tokens[]) {
2007:
2008:                // if scope is not object-scope, it's really a search
2009:                if (cons.getSearchScope() != SearchControls.OBJECT_SCOPE) {
2010:                    return false;
2011:                }
2012:
2013:                // if attributes are to be returned, it's really a search
2014:                String[] attrs = cons.getReturningAttributes();
2015:                if (attrs == null || attrs.length != 0) {
2016:                    return false;
2017:                }
2018:
2019:                // if the filter not a simple assertion, it's really a search
2020:                if (!filterToAssertion(filter, tokens)) {
2021:                    return false;
2022:                }
2023:
2024:                // it can be converted to a compare
2025:                return true;
2026:            }
2027:
2028:            // If the supplied filter is a simple assertion i.e. "<type>=<value>"
2029:            // (enclosing parentheses are permitted) then
2030:            // filterToAssertion will return true and pass the type and value as
2031:            // the first and second elements of tokens respectively.
2032:            // precondition: tokens[] must be initialized and be at least of size 2.
2033:
2034:            private static boolean filterToAssertion(String filter,
2035:                    String tokens[]) {
2036:
2037:                // find the left and right half of the assertion
2038:                StringTokenizer assertionTokenizer = new StringTokenizer(
2039:                        filter, "=");
2040:
2041:                if (assertionTokenizer.countTokens() != 2) {
2042:                    return false;
2043:                }
2044:
2045:                tokens[0] = assertionTokenizer.nextToken();
2046:                tokens[1] = assertionTokenizer.nextToken();
2047:
2048:                // make sure the value does not contain a wildcard
2049:                if (tokens[1].indexOf('*') != -1) {
2050:                    return false;
2051:                }
2052:
2053:                // test for enclosing parenthesis
2054:                boolean hasParens = false;
2055:                int len = tokens[1].length();
2056:
2057:                if ((tokens[0].charAt(0) == '(')
2058:                        && (tokens[1].charAt(len - 1) == ')')) {
2059:                    hasParens = true;
2060:
2061:                } else if ((tokens[0].charAt(0) == '(')
2062:                        || (tokens[1].charAt(len - 1) == ')')) {
2063:                    return false; // unbalanced
2064:                }
2065:
2066:                // make sure the left and right half are not expresions themselves
2067:                StringTokenizer illegalCharsTokenizer = new StringTokenizer(
2068:                        tokens[0], "()&|!=~><*", true);
2069:
2070:                if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
2071:                    return false;
2072:                }
2073:
2074:                illegalCharsTokenizer = new StringTokenizer(tokens[1],
2075:                        "()&|!=~><*", true);
2076:
2077:                if (illegalCharsTokenizer.countTokens() != (hasParens ? 2 : 1)) {
2078:                    return false;
2079:                }
2080:
2081:                // strip off enclosing parenthesis, if present
2082:                if (hasParens) {
2083:                    tokens[0] = tokens[0].substring(1);
2084:                    tokens[1] = tokens[1].substring(0, len - 1);
2085:                }
2086:
2087:                return true;
2088:            }
2089:
2090:            private LdapResult compare(Name name, String type, String value)
2091:                    throws IOException, NamingException {
2092:
2093:                ensureOpen();
2094:                String nm = fullyQualifiedName(name);
2095:
2096:                LdapResult answer = clnt.compare(nm, type, value, reqCtls);
2097:                respCtls = answer.resControls; // retrieve response controls
2098:
2099:                return answer;
2100:            }
2101:
2102:            private static SearchControls cloneSearchControls(
2103:                    SearchControls cons) {
2104:                if (cons == null) {
2105:                    return null;
2106:                }
2107:                String[] retAttrs = cons.getReturningAttributes();
2108:                if (retAttrs != null) {
2109:                    String[] attrs = new String[retAttrs.length];
2110:                    System.arraycopy(retAttrs, 0, attrs, 0, retAttrs.length);
2111:                    retAttrs = attrs;
2112:                }
2113:                return new SearchControls(cons.getSearchScope(), cons
2114:                        .getCountLimit(), cons.getTimeLimit(), retAttrs, cons
2115:                        .getReturningObjFlag(), cons.getDerefLinkFlag());
2116:            }
2117:
2118:            // -------------- Environment Properties ------------------
2119:
2120:            /**
2121:             * Override with noncloning version.
2122:             */
2123:            protected Hashtable p_getEnvironment() {
2124:                return envprops;
2125:            }
2126:
2127:            public Hashtable getEnvironment() throws NamingException {
2128:                return (envprops == null ? new Hashtable(5, 0.75f)
2129:                        : (Hashtable) envprops.clone());
2130:            }
2131:
2132:            public Object removeFromEnvironment(String propName)
2133:                    throws NamingException {
2134:
2135:                // not there; just return
2136:                if (envprops == null || envprops.get(propName) == null) {
2137:                    return null;
2138:                }
2139:
2140:                if (propName.equals(REF_SEPARATOR)) {
2141:                    addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2142:                } else if (propName.equals(TYPES_ONLY)) {
2143:                    typesOnly = DEFAULT_TYPES_ONLY;
2144:                } else if (propName.equals(DELETE_RDN)) {
2145:                    deleteRDN = DEFAULT_DELETE_RDN;
2146:                } else if (propName.equals(DEREF_ALIASES)) {
2147:                    derefAliases = DEFAULT_DEREF_ALIASES;
2148:                } else if (propName.equals(Context.BATCHSIZE)) {
2149:                    batchSize = DEFAULT_BATCH_SIZE;
2150:                } else if (propName.equals(REFERRAL_LIMIT)) {
2151:                    referralHopLimit = DEFAULT_REFERRAL_LIMIT;
2152:                } else if (propName.equals(Context.REFERRAL)) {
2153:                    setReferralMode(null, true);
2154:                } else if (propName.equals(BINARY_ATTRIBUTES)) {
2155:                    setBinaryAttributes(null);
2156:                } else if (propName.equals(CONNECT_TIMEOUT)) {
2157:                    connectTimeout = -1;
2158:                } else if (propName.equals(READ_TIMEOUT)) {
2159:                    readTimeout = -1;
2160:
2161:                    // The following properties affect the connection
2162:
2163:                } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
2164:                    closeConnection(SOFT_CLOSE);
2165:                    // De-activate SSL and reset the context's url and port number
2166:                    if (useSsl && !hasLdapsScheme) {
2167:                        useSsl = false;
2168:                        url = null;
2169:                        if (useDefaultPortNumber) {
2170:                            port_number = DEFAULT_PORT;
2171:                        }
2172:                    }
2173:                } else if (propName.equals(VERSION)
2174:                        || propName.equals(SOCKET_FACTORY)) {
2175:                    closeConnection(SOFT_CLOSE);
2176:                } else if (propName.equals(Context.SECURITY_AUTHENTICATION)
2177:                        || propName.equals(Context.SECURITY_PRINCIPAL)
2178:                        || propName.equals(Context.SECURITY_CREDENTIALS)) {
2179:                    sharable = false;
2180:                }
2181:
2182:                // Update environment; reconnection will use new props
2183:                envprops = (Hashtable) envprops.clone();
2184:                return envprops.remove(propName);
2185:            }
2186:
2187:            public Object addToEnvironment(String propName, Object propVal)
2188:                    throws NamingException {
2189:
2190:                // If adding null, call remove
2191:                if (propVal == null) {
2192:                    return removeFromEnvironment(propName);
2193:                }
2194:
2195:                if (propName.equals(REF_SEPARATOR)) {
2196:                    setRefSeparator((String) propVal);
2197:                } else if (propName.equals(TYPES_ONLY)) {
2198:                    setTypesOnly((String) propVal);
2199:                } else if (propName.equals(DELETE_RDN)) {
2200:                    setDeleteRDN((String) propVal);
2201:                } else if (propName.equals(DEREF_ALIASES)) {
2202:                    setDerefAliases((String) propVal);
2203:                } else if (propName.equals(Context.BATCHSIZE)) {
2204:                    setBatchSize((String) propVal);
2205:                } else if (propName.equals(REFERRAL_LIMIT)) {
2206:                    setReferralLimit((String) propVal);
2207:                } else if (propName.equals(Context.REFERRAL)) {
2208:                    setReferralMode((String) propVal, true);
2209:                } else if (propName.equals(BINARY_ATTRIBUTES)) {
2210:                    setBinaryAttributes((String) propVal);
2211:                } else if (propName.equals(CONNECT_TIMEOUT)) {
2212:                    setConnectTimeout((String) propVal);
2213:                } else if (propName.equals(READ_TIMEOUT)) {
2214:                    setReadTimeout((String) propVal);
2215:                    // The following properties affect the connection
2216:
2217:                } else if (propName.equals(Context.SECURITY_PROTOCOL)) {
2218:                    closeConnection(SOFT_CLOSE);
2219:                    // Activate SSL and reset the context's url and port number
2220:                    if ("ssl".equals(propVal)) {
2221:                        useSsl = true;
2222:                        url = null;
2223:                        if (useDefaultPortNumber) {
2224:                            port_number = DEFAULT_SSL_PORT;
2225:                        }
2226:                    }
2227:                } else if (propName.equals(VERSION)
2228:                        || propName.equals(SOCKET_FACTORY)) {
2229:                    closeConnection(SOFT_CLOSE);
2230:                } else if (propName.equals(Context.SECURITY_AUTHENTICATION)
2231:                        || propName.equals(Context.SECURITY_PRINCIPAL)
2232:                        || propName.equals(Context.SECURITY_CREDENTIALS)) {
2233:                    sharable = false;
2234:                }
2235:
2236:                // Update environment; reconnection will use new props
2237:                envprops = (envprops == null ? new Hashtable(5, 0.75f)
2238:                        : (Hashtable) envprops.clone());
2239:                return envprops.put(propName, propVal);
2240:            }
2241:
2242:            /**
2243:             * Sets the URL that created the context in the java.naming.provider.url
2244:             * property.
2245:             */
2246:            void setProviderUrl(String providerUrl) { // called by LdapCtxFactory
2247:                if (envprops != null) {
2248:                    envprops.put(Context.PROVIDER_URL, providerUrl);
2249:                }
2250:            }
2251:
2252:            /**
2253:             * Sets the domain name for the context in the com.sun.jndi.ldap.domainname
2254:             * property.
2255:             * Used for hostname verification by Start TLS
2256:             */
2257:            void setDomainName(String domainName) { // called by LdapCtxFactory
2258:                if (envprops != null) {
2259:                    envprops.put(DOMAIN_NAME, domainName);
2260:                }
2261:            }
2262:
2263:            private void initEnv() throws NamingException {
2264:                if (envprops == null) {
2265:                    // Make sure that referrals are to their default
2266:                    setReferralMode(null, false);
2267:                    return;
2268:                }
2269:
2270:                // Set batch size
2271:                setBatchSize((String) envprops.get(Context.BATCHSIZE));
2272:
2273:                // Set separator used for encoding RefAddr
2274:                setRefSeparator((String) envprops.get(REF_SEPARATOR));
2275:
2276:                // Set whether RDN is removed when renaming object
2277:                setDeleteRDN((String) envprops.get(DELETE_RDN));
2278:
2279:                // Set whether types are returned only
2280:                setTypesOnly((String) envprops.get(TYPES_ONLY));
2281:
2282:                // Set how aliases are dereferenced 
2283:                setDerefAliases((String) envprops.get(DEREF_ALIASES));
2284:
2285:                // Set the limit on referral chains
2286:                setReferralLimit((String) envprops.get(REFERRAL_LIMIT));
2287:
2288:                setBinaryAttributes((String) envprops.get(BINARY_ATTRIBUTES));
2289:
2290:                bindCtls = cloneControls((Control[]) envprops
2291:                        .get(BIND_CONTROLS));
2292:
2293:                // set referral handling
2294:                setReferralMode((String) envprops.get(Context.REFERRAL), false);
2295:
2296:                // Set the connect timeout
2297:                setConnectTimeout((String) envprops.get(CONNECT_TIMEOUT));
2298:
2299:                // Set the read timeout
2300:                setReadTimeout((String) envprops.get(READ_TIMEOUT));
2301:
2302:                // When connection is created, it will use these and other
2303:                // properties from the environment
2304:            }
2305:
2306:            private void setDeleteRDN(String deleteRDNProp) {
2307:                if ((deleteRDNProp != null)
2308:                        && (deleteRDNProp.equalsIgnoreCase("false"))) {
2309:                    deleteRDN = false;
2310:                } else {
2311:                    deleteRDN = DEFAULT_DELETE_RDN;
2312:                }
2313:            }
2314:
2315:            private void setTypesOnly(String typesOnlyProp) {
2316:                if ((typesOnlyProp != null)
2317:                        && (typesOnlyProp.equalsIgnoreCase("true"))) {
2318:                    typesOnly = true;
2319:                } else {
2320:                    typesOnly = DEFAULT_TYPES_ONLY;
2321:                }
2322:            }
2323:
2324:            /**
2325:             * Sets the batch size of this context;
2326:             */
2327:            private void setBatchSize(String batchSizeProp) {
2328:                // set batchsize
2329:                if (batchSizeProp != null) {
2330:                    batchSize = Integer.parseInt(batchSizeProp);
2331:                } else {
2332:                    batchSize = DEFAULT_BATCH_SIZE;
2333:                }
2334:            }
2335:
2336:            /**
2337:             * Sets the referral mode of this context to 'follow', 'throw' or 'ignore'.
2338:             * If referral mode is 'ignore' then activate the manageReferral control.
2339:             */
2340:            private void setReferralMode(String ref, boolean update) {
2341:                // First determine the referral mode
2342:                if (ref != null) {
2343:                    if (ref.equals("follow")) {
2344:                        handleReferrals = LdapClient.LDAP_REF_FOLLOW;
2345:                    } else if (ref.equals("throw")) {
2346:                        handleReferrals = LdapClient.LDAP_REF_THROW;
2347:                    } else if (ref.equals("ignore")) {
2348:                        handleReferrals = LdapClient.LDAP_REF_IGNORE;
2349:                    } else {
2350:                        throw new IllegalArgumentException("Illegal value for "
2351:                                + Context.REFERRAL + " property.");
2352:                    }
2353:                } else {
2354:                    handleReferrals = DEFAULT_REFERRAL_MODE;
2355:                }
2356:
2357:                if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2358:                    // If ignoring referrals, add manageReferralControl
2359:                    reqCtls = addControl(reqCtls, manageReferralControl);
2360:
2361:                } else if (update) {
2362:
2363:                    // If we're update an existing context, remove the control
2364:                    reqCtls = removeControl(reqCtls, manageReferralControl);
2365:
2366:                } // else, leave alone; need not update
2367:            }
2368:
2369:            /**
2370:             * Set whether aliases are derefereced during resolution and searches.
2371:             */
2372:            private void setDerefAliases(String deref) {
2373:                if (deref != null) {
2374:                    if (deref.equals("never")) {
2375:                        derefAliases = 0; // never de-reference aliases
2376:                    } else if (deref.equals("searching")) {
2377:                        derefAliases = 1; // de-reference aliases during searching
2378:                    } else if (deref.equals("finding")) {
2379:                        derefAliases = 2; // de-reference during name resolution
2380:                    } else if (deref.equals("always")) {
2381:                        derefAliases = 3; // always de-reference aliases
2382:                    } else {
2383:                        throw new IllegalArgumentException("Illegal value for "
2384:                                + DEREF_ALIASES + " property.");
2385:                    }
2386:                } else {
2387:                    derefAliases = DEFAULT_DEREF_ALIASES;
2388:                }
2389:            }
2390:
2391:            private void setRefSeparator(String sepStr) throws NamingException {
2392:                if (sepStr != null && sepStr.length() > 0) {
2393:                    addrEncodingSeparator = sepStr.charAt(0);
2394:                } else {
2395:                    addrEncodingSeparator = DEFAULT_REF_SEPARATOR;
2396:                }
2397:            }
2398:
2399:            /**
2400:             * Sets the limit on referral chains
2401:             */
2402:            private void setReferralLimit(String referralLimitProp) {
2403:                // set referral limit
2404:                if (referralLimitProp != null) {
2405:                    referralHopLimit = Integer.parseInt(referralLimitProp);
2406:
2407:                    // a zero setting indicates no limit
2408:                    if (referralHopLimit == 0)
2409:                        referralHopLimit = Integer.MAX_VALUE;
2410:                } else {
2411:                    referralHopLimit = DEFAULT_REFERRAL_LIMIT;
2412:                }
2413:            }
2414:
2415:            // For counting referral hops
2416:            void setHopCount(int hopCount) {
2417:                this .hopCount = hopCount;
2418:            }
2419:
2420:            /**
2421:             * Sets the connect timeout value
2422:             */
2423:            private void setConnectTimeout(String connectTimeoutProp) {
2424:                if (connectTimeoutProp != null) {
2425:                    connectTimeout = Integer.parseInt(connectTimeoutProp);
2426:                } else {
2427:                    connectTimeout = -1;
2428:                }
2429:            }
2430:
2431:            /**
2432:             * Sets the read timeout value
2433:             */
2434:            private void setReadTimeout(String readTimeoutProp) {
2435:                if (readTimeoutProp != null) {
2436:                    readTimeout = Integer.parseInt(readTimeoutProp);
2437:                } else {
2438:                    readTimeout = -1;
2439:                }
2440:            }
2441:
2442:            /*
2443:             * Extract URLs from a string. The format of the string is:
2444:             *
2445:             *     <urlstring > ::= "Referral:" <ldapurls>
2446:             *     <ldapurls>   ::= <separator> <ldapurl> | <ldapurls>
2447:             *     <separator>  ::= ASCII linefeed character (0x0a)
2448:             *     <ldapurl>    ::= LDAP URL format (RFC 1959)
2449:             */
2450:            private static Vector extractURLs(String refString) {
2451:
2452:                int separator = 0;
2453:                int urlCount = 0;
2454:
2455:                // count the number of URLs
2456:                while ((separator = refString.indexOf('\n', separator)) >= 0) {
2457:                    separator++;
2458:                    urlCount++;
2459:                }
2460:
2461:                Vector referrals = new Vector(urlCount);
2462:                int iURL;
2463:                int i = 0;
2464:
2465:                separator = refString.indexOf('\n');
2466:                iURL = separator + 1;
2467:                while ((separator = refString.indexOf('\n', iURL)) >= 0) {
2468:                    referrals.addElement(refString.substring(iURL, separator));
2469:                    iURL = separator + 1;
2470:                }
2471:                referrals.addElement(refString.substring(iURL));
2472:
2473:                return referrals;
2474:            }
2475:
2476:            /*
2477:             * Argument is a space-separated list of attribute IDs
2478:             * Converts attribute IDs to lowercase before adding to built-in list.
2479:             */
2480:            private void setBinaryAttributes(String attrIds) {
2481:                if (attrIds == null) {
2482:                    binaryAttrs = null;
2483:                } else {
2484:                    binaryAttrs = new Hashtable(11, 0.75f);
2485:                    StringTokenizer tokens = new StringTokenizer(attrIds
2486:                            .toLowerCase(), " ");
2487:
2488:                    while (tokens.hasMoreTokens()) {
2489:                        binaryAttrs.put(tokens.nextToken(), Boolean.TRUE);
2490:                    }
2491:                }
2492:            }
2493:
2494:            // ----------------- Connection  ---------------------
2495:
2496:            protected void finalize() {
2497:                try {
2498:                    close();
2499:                } catch (NamingException e) {
2500:                    // ignore failures 
2501:                }
2502:            }
2503:
2504:            synchronized public void close() throws NamingException {
2505:                if (debug) {
2506:                    System.err.println("LdapCtx: close() called " + this );
2507:                    (new Throwable()).printStackTrace();
2508:                }
2509:
2510:                // Event (normal and unsolicited)
2511:                if (eventSupport != null) {
2512:                    eventSupport.cleanup(); // idempotent
2513:                    removeUnsolicited();
2514:                }
2515:
2516:                // Enumerations that are keeping the connection alive
2517:                if (enumCount > 0) {
2518:                    if (debug)
2519:                        System.err.println("LdapCtx: close deferred");
2520:                    closeRequested = true;
2521:                    return;
2522:                }
2523:                closeConnection(SOFT_CLOSE);
2524:
2525:                // %%%: RL: There is no need to set these to null, as they're just
2526:                // variables whose contents and references will automatically
2527:                // be cleaned up when they're no longer referenced.
2528:                // Also, setting these to null creates problems for the attribute
2529:                // schema-related methods, which need these to work.
2530:                /*
2531:                 schemaTrees = null;
2532:                 envprops = null;
2533:                 */
2534:            }
2535:
2536:            public void reconnect(Control[] connCtls) throws NamingException {
2537:                // Update environment
2538:                envprops = (envprops == null ? new Hashtable(5, 0.75f)
2539:                        : (Hashtable) envprops.clone());
2540:
2541:                if (connCtls == null) {
2542:                    envprops.remove(BIND_CONTROLS);
2543:                    bindCtls = null;
2544:                } else {
2545:                    envprops.put(BIND_CONTROLS,
2546:                            bindCtls = cloneControls(connCtls));
2547:                }
2548:
2549:                sharable = false; // can't share with existing contexts
2550:                ensureOpen(); // open or reauthenticated
2551:            }
2552:
2553:            private void ensureOpen() throws NamingException {
2554:                ensureOpen(false);
2555:            }
2556:
2557:            private void ensureOpen(boolean startTLS) throws NamingException {
2558:
2559:                try {
2560:                    if (clnt == null) {
2561:                        if (debug) {
2562:                            System.err.println("LdapCtx: Reconnecting " + this );
2563:                        }
2564:
2565:                        // reset the cache before a new connection is established
2566:                        schemaTrees = new Hashtable(11, 0.75f);
2567:                        connect(startTLS);
2568:
2569:                    } else if (!sharable || startTLS) {
2570:
2571:                        synchronized (clnt) {
2572:                            if (!clnt.isLdapv3 || clnt.referenceCount > 1
2573:                                    || clnt.usingSaslStreams()) {
2574:                                closeConnection(SOFT_CLOSE);
2575:                            }
2576:                        }
2577:                        // reset the cache before a new connection is established
2578:                        schemaTrees = new Hashtable(11, 0.75f);
2579:                        connect(startTLS);
2580:                    }
2581:
2582:                } finally {
2583:                    sharable = true; // connection is now either new or single-use
2584:                    // OK for others to start sharing again
2585:                }
2586:            }
2587:
2588:            private void connect(boolean startTLS) throws NamingException {
2589:                if (debug) {
2590:                    System.err.println("LdapCtx: Connecting " + this );
2591:                }
2592:
2593:                String user = null; // authenticating user
2594:                Object passwd = null; // password for authenticating user
2595:                String secProtocol = null; // security protocol (e.g. "ssl")
2596:                String socketFactory = null; // socket factory
2597:                String authMechanism = null; // authentication mechanism
2598:                String ver = null;
2599:                int ldapVersion; // LDAP protocol version
2600:                boolean usePool = false; // enable connection pooling
2601:
2602:                if (envprops != null) {
2603:                    user = (String) envprops.get(Context.SECURITY_PRINCIPAL);
2604:                    passwd = envprops.get(Context.SECURITY_CREDENTIALS);
2605:                    ver = (String) envprops.get(VERSION);
2606:                    secProtocol = useSsl ? "ssl" : (String) envprops
2607:                            .get(Context.SECURITY_PROTOCOL);
2608:                    socketFactory = (String) envprops.get(SOCKET_FACTORY);
2609:                    authMechanism = (String) envprops
2610:                            .get(Context.SECURITY_AUTHENTICATION);
2611:
2612:                    usePool = "true".equalsIgnoreCase((String) envprops
2613:                            .get(ENABLE_POOL));
2614:                }
2615:
2616:                if (socketFactory == null) {
2617:                    socketFactory = "ssl".equals(secProtocol) ? DEFAULT_SSL_FACTORY
2618:                            : null;
2619:                }
2620:
2621:                if (authMechanism == null) {
2622:                    authMechanism = (user == null) ? "none" : "simple";
2623:                }
2624:
2625:                try {
2626:                    boolean initial = (clnt == null);
2627:
2628:                    if (initial) {
2629:                        ldapVersion = (ver != null) ? Integer.parseInt(ver)
2630:                                : DEFAULT_LDAP_VERSION;
2631:
2632:                        clnt = LdapClient.getInstance(
2633:                                usePool, // Whether to use connection pooling
2634:
2635:                                // Required for LdapClient constructor
2636:                                hostname, port_number, socketFactory,
2637:                                connectTimeout, readTimeout, trace,
2638:
2639:                                // Required for basic client identity
2640:                                ldapVersion, authMechanism, bindCtls,
2641:                                secProtocol,
2642:
2643:                                // Required for simple client identity
2644:                                user, passwd,
2645:
2646:                                // Required for SASL client identity
2647:                                envprops);
2648:
2649:                        /**
2650:                         * Pooled connections are preauthenticated; 
2651:                         * newly created ones are not.
2652:                         */
2653:                        if (clnt.authenticateCalled()) {
2654:                            return;
2655:                        }
2656:
2657:                    } else if (sharable && startTLS) {
2658:                        return; // no authentication required
2659:
2660:                    } else {
2661:                        // reauthenticating over existing connection; 
2662:                        // only v3 supports this
2663:                        ldapVersion = LdapClient.LDAP_VERSION3;
2664:                    }
2665:
2666:                    LdapResult answer = clnt.authenticate(initial, user,
2667:                            passwd, ldapVersion, authMechanism, bindCtls,
2668:                            envprops);
2669:
2670:                    respCtls = answer.resControls; // retrieve (bind) response controls
2671:
2672:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
2673:                        if (initial) {
2674:                            closeConnection(HARD_CLOSE); // hard close
2675:                        }
2676:                        processReturnCode(answer);
2677:                    }
2678:
2679:                } catch (LdapReferralException e) {
2680:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
2681:                        throw e;
2682:
2683:                    String referral;
2684:                    LdapURL url;
2685:                    NamingException saved_ex = null;
2686:
2687:                    // Process the referrals sequentially (top level) and
2688:                    // recursively (per referral)
2689:                    while (true) {
2690:
2691:                        if ((referral = e.getNextReferral()) == null) {
2692:                            // No more referrals to follow
2693:
2694:                            if (saved_ex != null) {
2695:                                throw (NamingException) (saved_ex
2696:                                        .fillInStackTrace());
2697:                            } else {
2698:                                // No saved exception, something must have gone wrong
2699:                                throw new NamingException(
2700:                                        "Internal error processing referral during connection");
2701:                            }
2702:                        }
2703:
2704:                        // Use host/port number from referral
2705:                        url = new LdapURL(referral);
2706:                        hostname = url.getHost();
2707:                        if ((hostname != null) && (hostname.charAt(0) == '[')) {
2708:                            hostname = hostname.substring(1,
2709:                                    hostname.length() - 1);
2710:                        }
2711:                        port_number = url.getPort();
2712:
2713:                        // Try to connect again using new host/port number
2714:                        try {
2715:                            connect(startTLS);
2716:                            break;
2717:
2718:                        } catch (NamingException ne) {
2719:                            saved_ex = ne;
2720:                            continue; // follow another referral
2721:                        }
2722:                    }
2723:                }
2724:            }
2725:
2726:            private void closeConnection(boolean hardclose) {
2727:                removeUnsolicited(); // idempotent
2728:
2729:                if (clnt != null) {
2730:                    if (debug) {
2731:                        System.err.println("LdapCtx: calling clnt.close() "
2732:                                + this );
2733:                    }
2734:                    clnt.close(reqCtls, hardclose);
2735:                    clnt = null;
2736:                }
2737:            }
2738:
2739:            // Used by Enum classes to track whether it still needs context
2740:            private int enumCount = 0;
2741:            private boolean closeRequested = false;
2742:
2743:            synchronized void incEnumCount() {
2744:                ++enumCount;
2745:                if (debug)
2746:                    System.err.println("LdapCtx: " + this  + " enum inc: "
2747:                            + enumCount);
2748:            }
2749:
2750:            synchronized void decEnumCount() {
2751:                --enumCount;
2752:                if (debug)
2753:                    System.err.println("LdapCtx: " + this  + " enum dec: "
2754:                            + enumCount);
2755:
2756:                if (enumCount == 0 && closeRequested) {
2757:                    try {
2758:                        close();
2759:                    } catch (NamingException e) {
2760:                        // ignore failures
2761:                    }
2762:                }
2763:            }
2764:
2765:            // ------------ Return code and Error messages  -----------------------
2766:
2767:            protected void processReturnCode(LdapResult answer)
2768:                    throws NamingException {
2769:                processReturnCode(answer, null, this , null, envprops, null);
2770:            }
2771:
2772:            void processReturnCode(LdapResult answer, Name remainName)
2773:                    throws NamingException {
2774:                processReturnCode(answer, (new CompositeName()).add(currentDN),
2775:                        this , remainName, envprops,
2776:                        fullyQualifiedName(remainName));
2777:            }
2778:
2779:            protected void processReturnCode(LdapResult res, Name resolvedName,
2780:                    Object resolvedObj, Name remainName, Hashtable envprops,
2781:                    String fullDN) throws NamingException {
2782:
2783:                String msg = LdapClient.getErrorMessage(res.status,
2784:                        res.errorMessage);
2785:                NamingException e;
2786:                LdapReferralException r = null;
2787:
2788:                switch (res.status) {
2789:
2790:                case LdapClient.LDAP_SUCCESS:
2791:
2792:                    // handle Search continuation references
2793:                    if (res.referrals != null) {
2794:
2795:                        msg = "Unprocessed Continuation Reference(s)";
2796:
2797:                        if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2798:                            e = new PartialResultException(msg);
2799:                            break;
2800:                        }
2801:
2802:                        // handle multiple sets of URLs
2803:                        int contRefCount = res.referrals.size();
2804:                        LdapReferralException head = null;
2805:                        LdapReferralException ptr = null;
2806:
2807:                        msg = "Continuation Reference";
2808:
2809:                        // make a chain of LdapReferralExceptions
2810:                        for (int i = 0; i < contRefCount; i++) {
2811:
2812:                            r = new LdapReferralException(resolvedName,
2813:                                    resolvedObj, remainName, msg, envprops,
2814:                                    fullDN, handleReferrals, reqCtls);
2815:                            r.setReferralInfo((Vector) res.referrals
2816:                                    .elementAt(i), true);
2817:
2818:                            if (hopCount > 1) {
2819:                                r.setHopCount(hopCount);
2820:                            }
2821:
2822:                            if (head == null) {
2823:                                head = ptr = r;
2824:                            } else {
2825:                                ptr.nextReferralEx = r; // append ex. to end of chain
2826:                                ptr = r;
2827:                            }
2828:                        }
2829:                        res.referrals = null; // reset
2830:
2831:                        if (res.refEx == null) {
2832:                            res.refEx = head;
2833:
2834:                        } else {
2835:                            ptr = res.refEx;
2836:
2837:                            while (ptr.nextReferralEx != null) {
2838:                                ptr = ptr.nextReferralEx;
2839:                            }
2840:                            ptr.nextReferralEx = head;
2841:                        }
2842:
2843:                        // check the hop limit
2844:                        if (hopCount > referralHopLimit) {
2845:                            NamingException lee = new LimitExceededException(
2846:                                    "Referral limit exceeded");
2847:                            lee.setRootCause(r);
2848:                            throw lee;
2849:                        }
2850:                    }
2851:                    return;
2852:
2853:                case LdapClient.LDAP_REFERRAL:
2854:
2855:                    if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2856:                        e = new PartialResultException(msg);
2857:                        break;
2858:                    }
2859:
2860:                    r = new LdapReferralException(resolvedName, resolvedObj,
2861:                            remainName, msg, envprops, fullDN, handleReferrals,
2862:                            reqCtls);
2863:                    // only one set of URLs is present
2864:                    r.setReferralInfo((Vector) res.referrals.elementAt(0),
2865:                            false);
2866:
2867:                    if (hopCount > 1) {
2868:                        r.setHopCount(hopCount);
2869:                    }
2870:
2871:                    // check the hop limit
2872:                    if (hopCount > referralHopLimit) {
2873:                        NamingException lee = new LimitExceededException(
2874:                                "Referral limit exceeded");
2875:                        lee.setRootCause(r);
2876:                        e = lee;
2877:
2878:                    } else {
2879:                        e = r;
2880:                    }
2881:                    break;
2882:
2883:                /*
2884:                 * Handle SLAPD-style referrals.
2885:                 *
2886:                 * Referrals received during name resolution should be followed
2887:                 * until one succeeds - the target entry is located. An exception
2888:                 * is thrown now to handle these.
2889:                 *
2890:                 * Referrals received during a search operation point to unexplored
2891:                 * parts of the directory and each should be followed. An exception
2892:                 * is thrown later (during results enumeration) to handle these.
2893:                 */
2894:
2895:                case LdapClient.LDAP_PARTIAL_RESULTS:
2896:
2897:                    if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
2898:                        e = new PartialResultException(msg);
2899:                        break;
2900:                    }
2901:
2902:                    // extract SLAPD-style referrals from errorMessage
2903:                    if ((res.errorMessage != null)
2904:                            && (!res.errorMessage.equals(""))) {
2905:                        res.referrals = extractURLs(res.errorMessage);
2906:                    } else {
2907:                        e = new PartialResultException(msg);
2908:                        break;
2909:                    }
2910:
2911:                    // build exception
2912:                    r = new LdapReferralException(resolvedName, resolvedObj,
2913:                            remainName, msg, envprops, fullDN, handleReferrals,
2914:                            reqCtls);
2915:
2916:                    if (hopCount > 1) {
2917:                        r.setHopCount(hopCount);
2918:                    }
2919:                    /*
2920:                     * %%%
2921:                     * SLAPD-style referrals received during name resolution
2922:                     * cannot be distinguished from those received during a
2923:                     * search operation. Since both must be handled differently
2924:                     * the following rule is applied:
2925:                     *
2926:                     *     If 1 referral and 0 entries is received then
2927:                     *     assume name resolution has not yet completed.
2928:                     */
2929:                    if (((res.entries == null) || (res.entries.size() == 0))
2930:                            && (res.referrals.size() == 1)) {
2931:
2932:                        r.setReferralInfo((Vector) res.referrals, false);
2933:
2934:                        // check the hop limit
2935:                        if (hopCount > referralHopLimit) {
2936:                            NamingException lee = new LimitExceededException(
2937:                                    "Referral limit exceeded");
2938:                            lee.setRootCause(r);
2939:                            e = lee;
2940:
2941:                        } else {
2942:                            e = r;
2943:                        }
2944:
2945:                    } else {
2946:                        r.setReferralInfo(res.referrals, true);
2947:                        res.refEx = r;
2948:                        return;
2949:                    }
2950:                    break;
2951:
2952:                case LdapClient.LDAP_INVALID_DN_SYNTAX:
2953:                case LdapClient.LDAP_NAMING_VIOLATION:
2954:
2955:                    if (remainName != null) {
2956:                        e = new InvalidNameException(remainName.toString()
2957:                                + ": " + msg);
2958:                    } else {
2959:                        e = new InvalidNameException(msg);
2960:                    }
2961:                    break;
2962:
2963:                default:
2964:                    e = mapErrorCode(res.status, res.errorMessage);
2965:                    break;
2966:                }
2967:                e.setResolvedName(resolvedName);
2968:                e.setResolvedObj(resolvedObj);
2969:                e.setRemainingName(remainName);
2970:                throw e;
2971:            }
2972:
2973:            /**
2974:             * Maps an LDAP error code to an appropriate NamingException.
2975:             * %%% public; used by controls
2976:             *
2977:             * @param errorCode numeric LDAP error code
2978:             * @param errorMessage textual description of the LDAP error. May be null.
2979:             *
2980:             * @return A NamingException or null if the error code indicates success.
2981:             */
2982:            public static NamingException mapErrorCode(int errorCode,
2983:                    String errorMessage) {
2984:
2985:                if (errorCode == LdapClient.LDAP_SUCCESS)
2986:                    return null;
2987:
2988:                NamingException e = null;
2989:                String message = LdapClient.getErrorMessage(errorCode,
2990:                        errorMessage);
2991:
2992:                switch (errorCode) {
2993:
2994:                case LdapClient.LDAP_ALIAS_DEREFERENCING_PROBLEM:
2995:                    e = new NamingException(message);
2996:                    break;
2997:
2998:                case LdapClient.LDAP_ALIAS_PROBLEM:
2999:                    e = new NamingException(message);
3000:                    break;
3001:
3002:                case LdapClient.LDAP_ATTRIBUTE_OR_VALUE_EXISTS:
3003:                    e = new AttributeInUseException(message);
3004:                    break;
3005:
3006:                case LdapClient.LDAP_AUTH_METHOD_NOT_SUPPORTED:
3007:                case LdapClient.LDAP_CONFIDENTIALITY_REQUIRED:
3008:                case LdapClient.LDAP_STRONG_AUTH_REQUIRED:
3009:                case LdapClient.LDAP_INAPPROPRIATE_AUTHENTICATION:
3010:                    e = new AuthenticationNotSupportedException(message);
3011:                    break;
3012:
3013:                case LdapClient.LDAP_ENTRY_ALREADY_EXISTS:
3014:                    e = new NameAlreadyBoundException(message);
3015:                    break;
3016:
3017:                case LdapClient.LDAP_INVALID_CREDENTIALS:
3018:                case LdapClient.LDAP_SASL_BIND_IN_PROGRESS:
3019:                    e = new AuthenticationException(message);
3020:                    break;
3021:
3022:                case LdapClient.LDAP_INAPPROPRIATE_MATCHING:
3023:                    e = new InvalidSearchFilterException(message);
3024:                    break;
3025:
3026:                case LdapClient.LDAP_INSUFFICIENT_ACCESS_RIGHTS:
3027:                    e = new NoPermissionException(message);
3028:                    break;
3029:
3030:                case LdapClient.LDAP_INVALID_ATTRIBUTE_SYNTAX:
3031:                case LdapClient.LDAP_CONSTRAINT_VIOLATION:
3032:                    e = new InvalidAttributeValueException(message);
3033:                    break;
3034:
3035:                case LdapClient.LDAP_LOOP_DETECT:
3036:                    e = new NamingException(message);
3037:                    break;
3038:
3039:                case LdapClient.LDAP_NO_SUCH_ATTRIBUTE:
3040:                    e = new NoSuchAttributeException(message);
3041:                    break;
3042:
3043:                case LdapClient.LDAP_NO_SUCH_OBJECT:
3044:                    e = new NameNotFoundException(message);
3045:                    break;
3046:
3047:                case LdapClient.LDAP_OBJECT_CLASS_MODS_PROHIBITED:
3048:                case LdapClient.LDAP_OBJECT_CLASS_VIOLATION:
3049:                case LdapClient.LDAP_NOT_ALLOWED_ON_RDN:
3050:                    e = new SchemaViolationException(message);
3051:                    break;
3052:
3053:                case LdapClient.LDAP_NOT_ALLOWED_ON_NON_LEAF:
3054:                    e = new ContextNotEmptyException(message);
3055:                    break;
3056:
3057:                case LdapClient.LDAP_OPERATIONS_ERROR:
3058:                    // %%% need new exception ?
3059:                    e = new NamingException(message);
3060:                    break;
3061:
3062:                case LdapClient.LDAP_OTHER:
3063:                    e = new NamingException(message);
3064:                    break;
3065:
3066:                case LdapClient.LDAP_PROTOCOL_ERROR:
3067:                    e = new CommunicationException(message);
3068:                    break;
3069:
3070:                case LdapClient.LDAP_SIZE_LIMIT_EXCEEDED:
3071:                    e = new SizeLimitExceededException(message);
3072:                    break;
3073:
3074:                case LdapClient.LDAP_TIME_LIMIT_EXCEEDED:
3075:                    e = new TimeLimitExceededException(message);
3076:                    break;
3077:
3078:                case LdapClient.LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
3079:                    e = new OperationNotSupportedException(message);
3080:                    break;
3081:
3082:                case LdapClient.LDAP_UNAVAILABLE:
3083:                case LdapClient.LDAP_BUSY:
3084:                    e = new ServiceUnavailableException(message);
3085:                    break;
3086:
3087:                case LdapClient.LDAP_UNDEFINED_ATTRIBUTE_TYPE:
3088:                    e = new InvalidAttributeIdentifierException(message);
3089:                    break;
3090:
3091:                case LdapClient.LDAP_UNWILLING_TO_PERFORM:
3092:                    e = new OperationNotSupportedException(message);
3093:                    break;
3094:
3095:                case LdapClient.LDAP_COMPARE_FALSE:
3096:                case LdapClient.LDAP_COMPARE_TRUE:
3097:                case LdapClient.LDAP_IS_LEAF:
3098:                    // these are really not exceptions and this code probably
3099:                    // never gets executed
3100:                    e = new NamingException(message);
3101:                    break;
3102:
3103:                case LdapClient.LDAP_ADMIN_LIMIT_EXCEEDED:
3104:                    e = new LimitExceededException(message);
3105:                    break;
3106:
3107:                case LdapClient.LDAP_REFERRAL:
3108:                    e = new NamingException(message);
3109:                    break;
3110:
3111:                case LdapClient.LDAP_PARTIAL_RESULTS:
3112:                    e = new NamingException(message);
3113:                    break;
3114:
3115:                case LdapClient.LDAP_INVALID_DN_SYNTAX:
3116:                case LdapClient.LDAP_NAMING_VIOLATION:
3117:                    e = new InvalidNameException(message);
3118:                    break;
3119:
3120:                default:
3121:                    e = new NamingException(message);
3122:                    break;
3123:                }
3124:
3125:                return e;
3126:            }
3127:
3128:            // ----------------- Extensions and Controls -------------------
3129:
3130:            public ExtendedResponse extendedOperation(ExtendedRequest request)
3131:                    throws NamingException {
3132:
3133:                boolean startTLS = (request.getID().equals(STARTTLS_REQ_OID));
3134:                ensureOpen(startTLS);
3135:
3136:                try {
3137:
3138:                    LdapResult answer = clnt.extendedOp(request.getID(),
3139:                            request.getEncodedValue(), reqCtls, startTLS);
3140:                    respCtls = answer.resControls; // retrieve response controls
3141:
3142:                    if (answer.status != LdapClient.LDAP_SUCCESS) {
3143:                        processReturnCode(answer, new CompositeName());
3144:                    }
3145:                    // %%% verify request.getID() == answer.extensionId
3146:
3147:                    int len = (answer.extensionValue == null) ? 0
3148:                            : answer.extensionValue.length;
3149:
3150:                    ExtendedResponse er = request.createExtendedResponse(
3151:                            answer.extensionId, answer.extensionValue, 0, len);
3152:
3153:                    if (er instanceof  StartTlsResponseImpl) {
3154:                        // Pass the connection handle to StartTlsResponseImpl
3155:                        String domainName = (String) (envprops != null ? envprops
3156:                                .get(DOMAIN_NAME)
3157:                                : null);
3158:                        ((StartTlsResponseImpl) er).setConnection(clnt.conn,
3159:                                domainName);
3160:                    }
3161:                    return er;
3162:
3163:                } catch (LdapReferralException e) {
3164:
3165:                    if (handleReferrals == LdapClient.LDAP_REF_THROW)
3166:                        throw e;
3167:
3168:                    // process the referrals sequentially
3169:                    while (true) {
3170:
3171:                        LdapReferralContext refCtx = (LdapReferralContext) e
3172:                                .getReferralContext(envprops, bindCtls);
3173:
3174:                        // repeat the original operation at the new context
3175:                        try {
3176:
3177:                            return refCtx.extendedOperation(request);
3178:
3179:                        } catch (LdapReferralException re) {
3180:                            e = re;
3181:                            continue;
3182:
3183:                        } finally {
3184:                            // Make sure we close referral context
3185:                            refCtx.close();
3186:                        }
3187:                    }
3188:
3189:                } catch (IOException e) {
3190:                    NamingException e2 = new CommunicationException(e
3191:                            .getMessage());
3192:                    e2.setRootCause(e);
3193:                    throw e2;
3194:                }
3195:            }
3196:
3197:            public void setRequestControls(Control[] reqCtls)
3198:                    throws NamingException {
3199:                if (handleReferrals == LdapClient.LDAP_REF_IGNORE) {
3200:                    this .reqCtls = addControl(reqCtls, manageReferralControl);
3201:                } else {
3202:                    this .reqCtls = cloneControls(reqCtls);
3203:                }
3204:            }
3205:
3206:            public Control[] getRequestControls() throws NamingException {
3207:                return cloneControls(reqCtls);
3208:            }
3209:
3210:            public Control[] getConnectControls() throws NamingException {
3211:                return cloneControls(bindCtls);
3212:            }
3213:
3214:            public Control[] getResponseControls() throws NamingException {
3215:                return (respCtls != null) ? convertControls(respCtls) : null;
3216:            }
3217:
3218:            /**
3219:             * Narrow controls using own default factory and ControlFactory.
3220:             * @param ctls A non-null Vector
3221:             */
3222:            Control[] convertControls(Vector ctls) throws NamingException {
3223:                int count = ctls.size();
3224:
3225:                if (count == 0) {
3226:                    return null;
3227:                }
3228:
3229:                Control[] controls = new Control[count];
3230:
3231:                for (int i = 0; i < count; i++) {
3232:                    // Try own factory first
3233:                    controls[i] = myResponseControlFactory
3234:                            .getControlInstance((Control) ctls.elementAt(i));
3235:
3236:                    // Try assigned factories if own produced null
3237:                    if (controls[i] == null) {
3238:                        controls[i] = ControlFactory.getControlInstance(
3239:                                (Control) ctls.elementAt(i), this , envprops);
3240:                    }
3241:                }
3242:                return controls;
3243:            }
3244:
3245:            private static Control[] addControl(Control[] prevCtls,
3246:                    Control addition) {
3247:                if (prevCtls == null) {
3248:                    return new Control[] { addition };
3249:                }
3250:
3251:                // Find it 
3252:                int found = findControl(prevCtls, addition);
3253:                if (found != -1) {
3254:                    return prevCtls; // no need to do it again
3255:                }
3256:
3257:                Control[] newCtls = new Control[prevCtls.length + 1];
3258:                System.arraycopy(prevCtls, 0, newCtls, 0, prevCtls.length);
3259:                newCtls[prevCtls.length] = addition;
3260:                return newCtls;
3261:            }
3262:
3263:            private static int findControl(Control[] ctls, Control target) {
3264:                for (int i = 0; i < ctls.length; i++) {
3265:                    if (ctls[i] == target) {
3266:                        return i;
3267:                    }
3268:                }
3269:                return -1;
3270:            }
3271:
3272:            private static Control[] removeControl(Control[] prevCtls,
3273:                    Control target) {
3274:                if (prevCtls == null) {
3275:                    return null;
3276:                }
3277:
3278:                // Find it 
3279:                int found = findControl(prevCtls, target);
3280:                if (found == -1) {
3281:                    return prevCtls; // not there
3282:                }
3283:
3284:                // Remove it
3285:                Control[] newCtls = new Control[prevCtls.length - 1];
3286:                System.arraycopy(prevCtls, 0, newCtls, 0, found);
3287:                System.arraycopy(prevCtls, found + 1, newCtls, found,
3288:                        prevCtls.length - found - 1);
3289:                return newCtls;
3290:            }
3291:
3292:            private static Control[] cloneControls(Control[] ctls) {
3293:                if (ctls == null) {
3294:                    return null;
3295:                }
3296:                Control[] copiedCtls = new Control[ctls.length];
3297:                System.arraycopy(ctls, 0, copiedCtls, 0, ctls.length);
3298:                return copiedCtls;
3299:            }
3300:
3301:            // -------------------- Events ------------------------
3302:            /*
3303:             * Access to eventSupport need not be synchronized even though the
3304:             * Connection thread can access it asynchronously. It is
3305:             * impossible for a race condition to occur because 
3306:             * eventSupport.addNamingListener() must have been called before
3307:             * the Connection thread can call back to this ctx.
3308:             */
3309:            public void addNamingListener(Name nm, int scope, NamingListener l)
3310:                    throws NamingException {
3311:                addNamingListener(getTargetName(nm), scope, l);
3312:            }
3313:
3314:            public void addNamingListener(String nm, int scope, NamingListener l)
3315:                    throws NamingException {
3316:                if (eventSupport == null)
3317:                    eventSupport = new EventSupport(this );
3318:                eventSupport.addNamingListener(getTargetName(new CompositeName(
3319:                        nm)), scope, l);
3320:
3321:                // If first time asking for unsol
3322:                if (l instanceof  UnsolicitedNotificationListener
3323:                        && !unsolicited) {
3324:                    addUnsolicited();
3325:                }
3326:            }
3327:
3328:            public void removeNamingListener(NamingListener l)
3329:                    throws NamingException {
3330:                if (eventSupport == null)
3331:                    return; // no activity before, so just return
3332:
3333:                eventSupport.removeNamingListener(l);
3334:
3335:                // If removing an Unsol listener and it is the last one, let clnt know
3336:                if (l instanceof  UnsolicitedNotificationListener
3337:                        && !eventSupport.hasUnsolicited()) {
3338:                    removeUnsolicited();
3339:                }
3340:            }
3341:
3342:            public void addNamingListener(String nm, String filter,
3343:                    SearchControls ctls, NamingListener l)
3344:                    throws NamingException {
3345:                if (eventSupport == null)
3346:                    eventSupport = new EventSupport(this );
3347:                eventSupport.addNamingListener(getTargetName(new CompositeName(
3348:                        nm)), filter, cloneSearchControls(ctls), l);
3349:
3350:                // If first time asking for unsol
3351:                if (l instanceof  UnsolicitedNotificationListener
3352:                        && !unsolicited) {
3353:                    addUnsolicited();
3354:                }
3355:            }
3356:
3357:            public void addNamingListener(Name nm, String filter,
3358:                    SearchControls ctls, NamingListener l)
3359:                    throws NamingException {
3360:                addNamingListener(getTargetName(nm), filter, ctls, l);
3361:            }
3362:
3363:            public void addNamingListener(Name nm, String filter,
3364:                    Object[] filterArgs, SearchControls ctls, NamingListener l)
3365:                    throws NamingException {
3366:                addNamingListener(getTargetName(nm), filter, filterArgs, ctls,
3367:                        l);
3368:            }
3369:
3370:            public void addNamingListener(String nm, String filterExpr,
3371:                    Object[] filterArgs, SearchControls ctls, NamingListener l)
3372:                    throws NamingException {
3373:                String strfilter = SearchFilter.format(filterExpr, filterArgs);
3374:                addNamingListener(getTargetName(new CompositeName(nm)),
3375:                        strfilter, ctls, l);
3376:            }
3377:
3378:            public boolean targetMustExist() {
3379:                return true;
3380:            }
3381:
3382:            /**
3383:             * Retrieves the target name for which the listener is registering.
3384:             * If nm is a CompositeName, use its first and only component. It
3385:             * cannot have more than one components because a target be outside of
3386:             * this namespace. If nm is not a CompositeName, then treat it as a
3387:             * compound name.
3388:             * @param nm The non-null target name.
3389:             */
3390:            private static String getTargetName(Name nm) throws NamingException {
3391:                if (nm instanceof  CompositeName) {
3392:                    if (nm.size() > 1) {
3393:                        throw new InvalidNameException(
3394:                                "Target cannot span multiple namespaces: " + nm);
3395:                    } else if (nm.size() == 0) {
3396:                        return "";
3397:                    } else {
3398:                        return nm.get(0);
3399:                    }
3400:                } else {
3401:                    // treat as compound name
3402:                    return nm.toString();
3403:                }
3404:            }
3405:
3406:            // ------------------ Unsolicited Notification ---------------
3407:            // package private methods for handling unsolicited notification
3408:
3409:            /**
3410:             * Registers this context with the underlying LdapClient.
3411:             * When the underlying LdapClient receives an unsolicited notification,
3412:             * it will invoke LdapCtx.fireUnsolicited() so that this context
3413:             * can (using EventSupport) notified any registered listeners.
3414:             * This method is called by EventSupport when an unsolicited listener
3415:             * first registers with this context (should be called just once).
3416:             * @see #removeUnsolicited
3417:             * @see #fireUnsolicited
3418:             */
3419:            private void addUnsolicited() throws NamingException {
3420:                if (debug) {
3421:                    System.out.println("LdapCtx.addUnsolicited: " + this );
3422:                }
3423:
3424:                // addNamingListener must have created EventSupport already
3425:                ensureOpen();
3426:                synchronized (eventSupport) {
3427:                    clnt.addUnsolicited(this );
3428:                    unsolicited = true;
3429:                }
3430:            }
3431:
3432:            /**
3433:             * Removes this context from registering interest in unsolicited
3434:             * notifications from the underlying LdapClient. This method is called 
3435:             * under any one of the following conditions:
3436:             * <ul>
3437:             * <li>All unsolicited listeners have been removed. (see removingNamingListener)
3438:             * <li>This context is closed.
3439:             * <li>This context's underlying LdapClient changes.
3440:             *</ul>
3441:             * After this method has been called, this context will not pass
3442:             * on any events related to unsolicited notifications to EventSupport and
3443:             * and its listeners.
3444:             */
3445:
3446:            private void removeUnsolicited() {
3447:                if (debug) {
3448:                    System.out.println("LdapCtx.removeUnsolicited: "
3449:                            + unsolicited);
3450:                }
3451:                if (eventSupport == null) {
3452:                    return;
3453:                }
3454:
3455:                // addNamingListener must have created EventSupport already
3456:                synchronized (eventSupport) {
3457:                    if (unsolicited && clnt != null) {
3458:                        clnt.removeUnsolicited(this );
3459:                    }
3460:                    unsolicited = false;
3461:                }
3462:            }
3463:
3464:            /**
3465:             * Uses EventSupport to fire an event related to an unsolicited notification.
3466:             * Called by LdapClient when LdapClient receives an unsolicited notification.
3467:             */
3468:            void fireUnsolicited(Object obj) {
3469:                if (debug) {
3470:                    System.out.println("LdapCtx.fireUnsolicited: " + obj);
3471:                }
3472:                // addNamingListener must have created EventSupport already
3473:                synchronized (eventSupport) {
3474:                    if (unsolicited) {
3475:                        eventSupport.fireUnsolicited(obj);
3476:
3477:                        if (obj instanceof  NamingException) {
3478:                            unsolicited = false;
3479:                            // No need to notify clnt because clnt is the
3480:                            // only one that can fire a NamingException to 
3481:                            // unsol listeners and it will handle its own cleanup
3482:                        }
3483:                    }
3484:                }
3485:            }
3486:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.