001    /*
002     * Copyright 2011-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2011-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.listener;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Collection;
028    import java.util.EnumSet;
029    import java.util.HashSet;
030    import java.util.Iterator;
031    import java.util.LinkedHashMap;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    import java.util.logging.Handler;
036    
037    import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
038    import com.unboundid.ldap.sdk.DN;
039    import com.unboundid.ldap.sdk.Entry;
040    import com.unboundid.ldap.sdk.LDAPException;
041    import com.unboundid.ldap.sdk.OperationType;
042    import com.unboundid.ldap.sdk.ReadOnlyEntry;
043    import com.unboundid.ldap.sdk.ResultCode;
044    import com.unboundid.ldap.sdk.Version;
045    import com.unboundid.ldap.sdk.schema.Schema;
046    import com.unboundid.util.Mutable;
047    import com.unboundid.util.NotExtensible;
048    import com.unboundid.util.StaticUtils;
049    import com.unboundid.util.ThreadSafety;
050    import com.unboundid.util.ThreadSafetyLevel;
051    
052    import static com.unboundid.ldap.listener.ListenerMessages.*;
053    
054    
055    
056    /**
057     * This class provides a simple data structure with information that may be
058     * used to control the behavior of an {@link InMemoryDirectoryServer} instance.
059     * At least one base DN must be specified.  For all other properties, the
060     * following default values will be used unless an alternate configuration is
061     * provided:
062     * <UL>
063     *   <LI>Listeners:  The server will provide a single listener that will use an
064     *       automatically-selected port on all interfaces, which will not use SSL
065     *       or StartTLS.</LI>
066     *   <LI>Allowed Operation Types:  All types of operations will be allowed.</LI>
067     *   <LI>Authentication Required Operation Types:  Authentication will not be
068     *       required for any types of operations.</LI>
069     *   <LI>Schema:  The server will use a schema with a number of standard
070     *       attribute types and object classes.</LI>
071     *   <LI>Additional Bind Credentials:  The server will not have any additional
072     *       bind credentials.</LI>
073     *   <LI>Referential Integrity Attributes:  Referential integrity will not be
074     *       maintained.</LI>
075     *   <LI>Generate Operational Attributes:  The server will automatically
076     *       generate a number of operational attributes.</LI>
077     *   <LI>Extended Operation Handlers:  The server will support the password
078     *       modify extended operation as defined in RFC 3062, the start and end
079     *       transaction extended operations as defined in RFC 5805, and the
080     *       "Who Am I?" extended operation as defined in RFC 4532.</LI>
081     *   <LI>SASL Bind Handlers:  The server will support the SASL PLAIN mechanism
082     *       as defined in RFC 4616.</LI>
083     *   <LI>Max ChangeLog Entries:  The server will not provide an LDAP
084     *       changelog.</LI>
085     *   <LI>Access Log Handler:  The server will not perform any access
086     *       logging.</LI>
087     *   <LI>Code Log Handler:  The server will not perform any code logging.</LI>
088     *   <LI>LDAP Debug Log Handler:  The server will not perform any LDAP debug
089     *       logging.</LI>
090     *   <LI>Listener Exception Handler:  The server will not use a listener
091     *       exception handler.</LI>
092     *   <LI>Maximum Size Limit:  The server will not enforce a maximum search size
093     *       limit.</LI>
094     * </UL>
095     */
096    @NotExtensible()
097    @Mutable()
098    @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
099    public class InMemoryDirectoryServerConfig
100    {
101      // Indicates whether to enforce the requirement that attribute values comply
102      // with the associated attribute syntax.
103      private boolean enforceAttributeSyntaxCompliance;
104    
105      // Indicates whether to enforce the requirement that entries contain exactly
106      // one structural object class.
107      private boolean enforceSingleStructuralObjectClass;
108    
109      // Indicates whether to automatically generate operational attributes.
110      private boolean generateOperationalAttributes;
111    
112      // Indicates whether the code log should include sample code for processing
113      // the requests.
114      private boolean includeRequestProcessingInCodeLog;
115    
116      // The base DNs to use for the LDAP listener.
117      private DN[] baseDNs;
118    
119      // The log handler that should be used to record access log messages about
120      // operations processed by the server.
121      private Handler accessLogHandler;
122    
123      // The log handler that should be used to record detailed protocol-level
124      // messages about LDAP operations processed by the server.
125      private Handler ldapDebugLogHandler;
126    
127      // The maximum number of entries to retain in a generated changelog.
128      private int maxChangeLogEntries;
129    
130      // The maximum number of concurrent connections that will be allowed.
131      private int maxConnections;
132    
133      // The maximum number of entries that may be returned in any single search
134      // operation.
135      private int maxSizeLimit;
136    
137      // The exception handler that should be used for the listener.
138      private LDAPListenerExceptionHandler exceptionHandler;
139    
140      // The extended operation handlers that may be used to process extended
141      // operations in the server.
142      private final List<InMemoryExtendedOperationHandler>
143           extendedOperationHandlers;
144    
145      // The listener configurations that should be used for accepting connections
146      // to the server.
147      private final List<InMemoryListenerConfig> listenerConfigs;
148    
149      // The operation interceptors that should be used with the in-memory directory
150      // server.
151      private final List<InMemoryOperationInterceptor> operationInterceptors;
152    
153      // The SASL bind handlers that may be used to process SASL bind requests in
154      // the server.
155      private final List<InMemorySASLBindHandler> saslBindHandlers;
156    
157      // The names or OIDs of the attributes for which to maintain equality indexes.
158      private final List<String> equalityIndexAttributes;
159    
160      // A set of additional credentials that can be used for binding without
161      // requiring a corresponding entry in the data set.
162      private final Map<DN,byte[]> additionalBindCredentials;
163    
164      // The entry to use for the server root DSE.
165      private ReadOnlyEntry rootDSEEntry;
166    
167      // The schema to use for the server.
168      private Schema schema;
169    
170      // The set of operation types that will be supported by the server.
171      private final Set<OperationType> allowedOperationTypes;
172    
173      // The set of operation types for which authentication will be required.
174      private final Set<OperationType> authenticationRequiredOperationTypes;
175    
176      // The set of attributes for which referential integrity should be maintained.
177      private final Set<String> referentialIntegrityAttributes;
178    
179      // The path to a file that should be written with code that may be used to
180      // issue the requests received by the server.
181      private String codeLogPath;
182    
183      // The vendor name to report in the server root DSE.
184      private String vendorName;
185    
186      // The vendor version to report in the server root DSE.
187      private String vendorVersion;
188    
189    
190    
191      /**
192       * Creates a new in-memory directory server config object with the provided
193       * set of base DNs.
194       *
195       * @param  baseDNs  The set of base DNs to use for the server.  It must not
196       *                  be {@code null} or empty.
197       *
198       * @throws  LDAPException  If the provided set of base DN strings is null or
199       *                         empty, or if any of the provided base DN strings
200       *                         cannot be parsed as a valid DN.
201       */
202      public InMemoryDirectoryServerConfig(final String... baseDNs)
203             throws LDAPException
204      {
205        this(parseDNs(Schema.getDefaultStandardSchema(), baseDNs));
206      }
207    
208    
209    
210      /**
211       * Creates a new in-memory directory server config object with the default
212       * settings.
213       *
214       * @param  baseDNs  The set of base DNs to use for the server.  It must not
215       *                  be {@code null} or empty.
216       *
217       * @throws  LDAPException  If the provided set of base DNs is null or empty.
218       */
219      public InMemoryDirectoryServerConfig(final DN... baseDNs)
220             throws LDAPException
221      {
222        if ((baseDNs == null) || (baseDNs.length == 0))
223        {
224          throw new LDAPException(ResultCode.PARAM_ERROR,
225               ERR_MEM_DS_CFG_NO_BASE_DNS.get());
226        }
227    
228        this.baseDNs = baseDNs;
229    
230        listenerConfigs = new ArrayList<InMemoryListenerConfig>(1);
231        listenerConfigs.add(InMemoryListenerConfig.createLDAPConfig("default"));
232    
233        additionalBindCredentials            = new LinkedHashMap<DN,byte[]>(1);
234        accessLogHandler                     = null;
235        ldapDebugLogHandler                  = null;
236        enforceAttributeSyntaxCompliance     = true;
237        enforceSingleStructuralObjectClass   = true;
238        generateOperationalAttributes        = true;
239        maxChangeLogEntries                  = 0;
240        maxConnections                       = 0;
241        maxSizeLimit                         = 0;
242        exceptionHandler                     = null;
243        equalityIndexAttributes              = new ArrayList<String>(10);
244        rootDSEEntry                         = null;
245        schema                               = Schema.getDefaultStandardSchema();
246        allowedOperationTypes                = EnumSet.allOf(OperationType.class);
247        authenticationRequiredOperationTypes = EnumSet.noneOf(OperationType.class);
248        referentialIntegrityAttributes       = new HashSet<String>(0);
249        vendorName                           = "UnboundID Corp.";
250        vendorVersion                        = Version.FULL_VERSION_STRING;
251        codeLogPath                          = null;
252        includeRequestProcessingInCodeLog    = false;
253    
254        operationInterceptors = new ArrayList<InMemoryOperationInterceptor>(5);
255    
256        extendedOperationHandlers =
257             new ArrayList<InMemoryExtendedOperationHandler>(3);
258        extendedOperationHandlers.add(new PasswordModifyExtendedOperationHandler());
259        extendedOperationHandlers.add(new TransactionExtendedOperationHandler());
260        extendedOperationHandlers.add(new WhoAmIExtendedOperationHandler());
261    
262        saslBindHandlers = new ArrayList<InMemorySASLBindHandler>(1);
263        saslBindHandlers.add(new PLAINBindHandler());
264      }
265    
266    
267    
268      /**
269       * Creates a new in-memory directory server config object that is a duplicate
270       * of the provided config and may be altered without impacting the state of
271       * the given config object.
272       *
273       * @param  cfg  The in-memory directory server config object for to be
274       *              duplicated.
275       */
276      public InMemoryDirectoryServerConfig(final InMemoryDirectoryServerConfig cfg)
277      {
278        baseDNs = new DN[cfg.baseDNs.length];
279        System.arraycopy(cfg.baseDNs, 0, baseDNs, 0, baseDNs.length);
280    
281        listenerConfigs = new ArrayList<InMemoryListenerConfig>(
282             cfg.listenerConfigs);
283    
284        operationInterceptors = new ArrayList<InMemoryOperationInterceptor>(
285             cfg.operationInterceptors);
286    
287        extendedOperationHandlers = new ArrayList<InMemoryExtendedOperationHandler>(
288             cfg.extendedOperationHandlers);
289    
290        saslBindHandlers =
291             new ArrayList<InMemorySASLBindHandler>(cfg.saslBindHandlers);
292    
293        additionalBindCredentials =
294             new LinkedHashMap<DN,byte[]>(cfg.additionalBindCredentials);
295    
296        referentialIntegrityAttributes =
297             new HashSet<String>(cfg.referentialIntegrityAttributes);
298    
299        allowedOperationTypes = EnumSet.noneOf(OperationType.class);
300        allowedOperationTypes.addAll(cfg.allowedOperationTypes);
301    
302        authenticationRequiredOperationTypes = EnumSet.noneOf(OperationType.class);
303        authenticationRequiredOperationTypes.addAll(
304             cfg.authenticationRequiredOperationTypes);
305    
306        equalityIndexAttributes =
307             new ArrayList<String>(cfg.equalityIndexAttributes);
308    
309        enforceAttributeSyntaxCompliance   = cfg.enforceAttributeSyntaxCompliance;
310        enforceSingleStructuralObjectClass = cfg.enforceSingleStructuralObjectClass;
311        generateOperationalAttributes      = cfg.generateOperationalAttributes;
312        accessLogHandler                   = cfg.accessLogHandler;
313        ldapDebugLogHandler                = cfg.ldapDebugLogHandler;
314        maxChangeLogEntries                = cfg.maxChangeLogEntries;
315        maxConnections                     = cfg.maxConnections;
316        maxSizeLimit                       = cfg.maxSizeLimit;
317        exceptionHandler                   = cfg.exceptionHandler;
318        rootDSEEntry                       = cfg.rootDSEEntry;
319        schema                             = cfg.schema;
320        vendorName                         = cfg.vendorName;
321        vendorVersion                      = cfg.vendorVersion;
322        codeLogPath                        = cfg.codeLogPath;
323        includeRequestProcessingInCodeLog  = cfg.includeRequestProcessingInCodeLog;
324      }
325    
326    
327    
328      /**
329       * Retrieves the set of base DNs that should be used for the directory server.
330       *
331       * @return  The set of base DNs that should be used for the directory server.
332       */
333      public DN[] getBaseDNs()
334      {
335        return baseDNs;
336      }
337    
338    
339    
340      /**
341       * Specifies the set of base DNs that should be used for the directory server.
342       *
343       * @param  baseDNs  The set of base DNs that should be used for the directory
344       *                  server.  It must not be {@code null} or empty.
345       *
346       * @throws  LDAPException  If the provided set of base DN strings is null or
347       *                         empty, or if any of the provided base DN strings
348       *                         cannot be parsed as a valid DN.
349       */
350      public void setBaseDNs(final String... baseDNs)
351             throws LDAPException
352      {
353        setBaseDNs(parseDNs(schema, baseDNs));
354      }
355    
356    
357    
358      /**
359       * Specifies the set of base DNs that should be used for the directory server.
360       *
361       * @param  baseDNs  The set of base DNs that should be used for the directory
362       *                  server.  It must not be {@code null} or empty.
363       *
364       * @throws  LDAPException  If the provided set of base DNs is null or empty.
365       */
366      public void setBaseDNs(final DN... baseDNs)
367             throws LDAPException
368      {
369        if ((baseDNs == null) || (baseDNs.length == 0))
370        {
371          throw new LDAPException(ResultCode.PARAM_ERROR,
372               ERR_MEM_DS_CFG_NO_BASE_DNS.get());
373        }
374    
375        this.baseDNs = baseDNs;
376      }
377    
378    
379    
380      /**
381       * Retrieves the list of listener configurations that should be used for the
382       * directory server.
383       *
384       * @return  The list of listener configurations that should be used for the
385       *          directory server.
386       */
387      public List<InMemoryListenerConfig> getListenerConfigs()
388      {
389        return listenerConfigs;
390      }
391    
392    
393    
394      /**
395       * Specifies the configurations for all listeners that should be used for the
396       * directory server.
397       *
398       * @param  listenerConfigs  The configurations for all listeners that should
399       *                          be used for the directory server.  It must not be
400       *                          {@code null} or empty, and it must not contain
401       *                          multiple configurations with the same name.
402       *
403       * @throws  LDAPException  If there is a problem with the provided set of
404       *                         listener configurations.
405       */
406      public void setListenerConfigs(
407                       final InMemoryListenerConfig... listenerConfigs)
408             throws LDAPException
409      {
410        setListenerConfigs(StaticUtils.toList(listenerConfigs));
411      }
412    
413    
414    
415      /**
416       * Specifies the configurations for all listeners that should be used for the
417       * directory server.
418       *
419       * @param  listenerConfigs  The configurations for all listeners that should
420       *                          be used for the directory server.  It must not be
421       *                          {@code null} or empty, and it must not contain
422       *                          multiple configurations with the same name.
423       *
424       * @throws  LDAPException  If there is a problem with the provided set of
425       *                         listener configurations.
426       */
427      public void setListenerConfigs(
428                       final Collection<InMemoryListenerConfig> listenerConfigs)
429             throws LDAPException
430      {
431        if ((listenerConfigs == null) || listenerConfigs.isEmpty())
432        {
433          throw new LDAPException(ResultCode.PARAM_ERROR,
434               ERR_MEM_DS_CFG_NO_LISTENERS.get());
435        }
436    
437        final HashSet<String> listenerNames =
438             new HashSet<String>(listenerConfigs.size());
439        for (final InMemoryListenerConfig c : listenerConfigs)
440        {
441          final String name = StaticUtils.toLowerCase(c.getListenerName());
442          if (listenerNames.contains(name))
443          {
444            throw new LDAPException(ResultCode.PARAM_ERROR,
445                 ERR_MEM_DS_CFG_CONFLICTING_LISTENER_NAMES.get(name));
446          }
447          else
448          {
449            listenerNames.add(name);
450          }
451        }
452    
453        this.listenerConfigs.clear();
454        this.listenerConfigs.addAll(listenerConfigs);
455      }
456    
457    
458    
459      /**
460       * Retrieves the set of operation types that will be allowed by the server.
461       * Note that if the server is configured to support StartTLS, then it will be
462       * allowed even if other types of extended operations are not allowed.
463       *
464       * @return  The set of operation types that will be allowed by the server.
465       */
466      public Set<OperationType> getAllowedOperationTypes()
467      {
468        return allowedOperationTypes;
469      }
470    
471    
472    
473      /**
474       * Specifies the set of operation types that will be allowed by the server.
475       * Note that if the server is configured to support StartTLS, then it will be
476       * allowed even if other types of extended operations are not allowed.
477       *
478       * @param  operationTypes  The set of operation types that will be allowed by
479       *                         the server.
480       */
481      public void setAllowedOperationTypes(final OperationType... operationTypes)
482      {
483        allowedOperationTypes.clear();
484        if (operationTypes != null)
485        {
486          allowedOperationTypes.addAll(Arrays.asList(operationTypes));
487        }
488      }
489    
490    
491    
492      /**
493       * Specifies the set of operation types that will be allowed by the server.
494       * Note that if the server is configured to support StartTLS, then it will be
495       * allowed even if other types of extended operations are not allowed.
496       *
497       * @param  operationTypes  The set of operation types that will be allowed by
498       *                         the server.
499       */
500      public void setAllowedOperationTypes(
501                       final Collection<OperationType> operationTypes)
502      {
503        allowedOperationTypes.clear();
504        if (operationTypes != null)
505        {
506          allowedOperationTypes.addAll(operationTypes);
507        }
508      }
509    
510    
511    
512      /**
513       * Retrieves the set of operation types that will only be allowed for
514       * authenticated clients.  Note that authentication will never be required for
515       * bind operations, and if the server is configured to support StartTLS, then
516       * authentication will never be required for StartTLS operations even if it
517       * is required for other types of extended operations.
518       *
519       * @return  The set of operation types that will only be allowed for
520       *          authenticated clients.
521       */
522      public Set<OperationType> getAuthenticationRequiredOperationTypes()
523      {
524        return authenticationRequiredOperationTypes;
525      }
526    
527    
528    
529      /**
530       * Specifies the set of operation types that will only be allowed for
531       * authenticated clients.  Note that authentication will never be required for
532       * bind operations, and if the server is configured to support StartTLS, then
533       * authentication will never be required for StartTLS operations even if it
534       * is required for other types of extended operations.
535       *
536       * @param  operationTypes  The set of operation types that will be allowed for
537       *                         authenticated clients.
538       */
539      public void setAuthenticationRequiredOperationTypes(
540                       final OperationType... operationTypes)
541      {
542        authenticationRequiredOperationTypes.clear();
543        if (operationTypes != null)
544        {
545          authenticationRequiredOperationTypes.addAll(
546               Arrays.asList(operationTypes));
547        }
548      }
549    
550    
551    
552      /**
553       * Specifies the set of operation types that will only be allowed for
554       * authenticated clients.  Note that authentication will never be required for
555       * bind operations, and if the server is configured to support StartTLS, then
556       * authentication will never be required for StartTLS operations even if it
557       * is required for other types of extended operations.
558       *
559       * @param  operationTypes  The set of operation types that will be allowed for
560       *                         authenticated clients.
561       */
562      public void setAuthenticationRequiredOperationTypes(
563                       final Collection<OperationType> operationTypes)
564      {
565        authenticationRequiredOperationTypes.clear();
566        if (operationTypes != null)
567        {
568          authenticationRequiredOperationTypes.addAll(operationTypes);
569        }
570      }
571    
572    
573    
574      /**
575       * Retrieves a map containing DNs and passwords of additional users that will
576       * be allowed to bind to the server, even if their entries do not exist in the
577       * data set.  This can be used to mimic the functionality of special
578       * administrative accounts (e.g., "cn=Directory Manager" in many directories).
579       * The map that is returned may be altered if desired.
580       *
581       * @return  A map containing DNs and passwords of additional users that will
582       *          be allowed to bind to the server, even if their entries do not
583       *          exist in the data set.
584       */
585      public Map<DN,byte[]> getAdditionalBindCredentials()
586      {
587        return additionalBindCredentials;
588      }
589    
590    
591    
592      /**
593       * Adds an additional bind DN and password combination that can be used to
594       * bind to the server, even if the corresponding entry does not exist in the
595       * data set.  This can be used to mimic the functionality of special
596       * administrative accounts (e.g., "cn=Directory Manager" in many directories).
597       * If a password has already been defined for the given DN, then it will be
598       * replaced with the newly-supplied password.
599       *
600       * @param  dn        The bind DN to allow.  It must not be {@code null} or
601       *                   represent the null DN.
602       * @param  password  The password for the provided bind DN.  It must not be
603       *                   {@code null} or empty.
604       *
605       * @throws  LDAPException  If there is a problem with the provided bind DN or
606       *                         password.
607       */
608      public void addAdditionalBindCredentials(final String dn,
609                                               final String password)
610             throws LDAPException
611      {
612        addAdditionalBindCredentials(dn, StaticUtils.getBytes(password));
613      }
614    
615    
616    
617      /**
618       * Adds an additional bind DN and password combination that can be used to
619       * bind to the server, even if the corresponding entry does not exist in the
620       * data set.  This can be used to mimic the functionality of special
621       * administrative accounts (e.g., "cn=Directory Manager" in many directories).
622       * If a password has already been defined for the given DN, then it will be
623       * replaced with the newly-supplied password.
624       *
625       * @param  dn        The bind DN to allow.  It must not be {@code null} or
626       *                   represent the null DN.
627       * @param  password  The password for the provided bind DN.  It must not be
628       *                   {@code null} or empty.
629       *
630       * @throws  LDAPException  If there is a problem with the provided bind DN or
631       *                         password.
632       */
633      public void addAdditionalBindCredentials(final String dn,
634                                               final byte[] password)
635             throws LDAPException
636      {
637        if (dn == null)
638        {
639          throw new LDAPException(ResultCode.PARAM_ERROR,
640               ERR_MEM_DS_CFG_NULL_ADDITIONAL_BIND_DN.get());
641        }
642    
643        final DN parsedDN = new DN(dn, schema);
644        if (parsedDN.isNullDN())
645        {
646          throw new LDAPException(ResultCode.PARAM_ERROR,
647               ERR_MEM_DS_CFG_NULL_ADDITIONAL_BIND_DN.get());
648        }
649    
650        if ((password == null) || (password.length == 0))
651        {
652          throw new LDAPException(ResultCode.PARAM_ERROR,
653               ERR_MEM_DS_CFG_NULL_ADDITIONAL_BIND_PW.get());
654        }
655    
656        additionalBindCredentials.put(parsedDN, password);
657      }
658    
659    
660    
661      /**
662       * Retrieves the object that should be used to handle any errors encountered
663       * while attempting to interact with a client, if defined.
664       *
665       * @return  The object that should be used to handle any errors encountered
666       *          while attempting to interact with a client, or {@code null} if no
667       *          exception handler should be used.
668       */
669      public LDAPListenerExceptionHandler getListenerExceptionHandler()
670      {
671        return exceptionHandler;
672      }
673    
674    
675    
676      /**
677       * Specifies the LDAP listener exception handler that the server should use to
678       * handle any errors encountered while attempting to interact with a client.
679       *
680       * @param  exceptionHandler  The LDAP listener exception handler that the
681       *                           server should use to handle any errors
682       *                           encountered while attempting to interact with a
683       *                           client.  It may be {@code null} if no exception
684       *                           handler should be used.
685       */
686      public void setListenerExceptionHandler(
687                       final LDAPListenerExceptionHandler exceptionHandler)
688      {
689        this.exceptionHandler = exceptionHandler;
690      }
691    
692    
693    
694      /**
695       * Retrieves the schema that should be used by the server, if defined.  If a
696       * schema is defined, then it will be used to validate entries and determine
697       * which matching rules should be used for various types of matching
698       * operations.
699       *
700       * @return  The schema that should be used by the server, or {@code null} if
701       *          no schema should be used.
702       */
703      public Schema getSchema()
704      {
705        return schema;
706      }
707    
708    
709    
710      /**
711       * Specifies the schema that should be used by the server.  If a schema is
712       * defined, then it will be used to validate entries and determine which
713       * matching rules should be used for various types of matching operations.
714       *
715       * @param  schema  The schema that should be used by the server.  It may be
716       *                 {@code null} if no schema should be used.
717       */
718      public void setSchema(final Schema schema)
719      {
720        this.schema = schema;
721      }
722    
723    
724    
725      /**
726       * Indicates whether the server should reject attribute values which violate
727       * the constraints of the associated syntax.  This setting will be ignored if
728       * a {@code null} schema is in place.
729       *
730       * @return  {@code true} if the server should reject attribute values which
731       *          violate the constraints of the associated syntax, or {@code false}
732       *          if not.
733       */
734      public boolean enforceAttributeSyntaxCompliance()
735      {
736        return enforceAttributeSyntaxCompliance;
737      }
738    
739    
740    
741      /**
742       * Specifies whether the server should reject attribute values which violate
743       * the constraints of the associated syntax.  This setting will be ignored if
744       * a {@code null} schema is in place.
745       *
746       * @param  enforceAttributeSyntaxCompliance  Indicates whether the server
747       *                                           should reject attribute values
748       *                                           which violate the constraints of
749       *                                           the associated syntax.
750       */
751      public void setEnforceAttributeSyntaxCompliance(
752                       final boolean enforceAttributeSyntaxCompliance)
753      {
754        this.enforceAttributeSyntaxCompliance = enforceAttributeSyntaxCompliance;
755      }
756    
757    
758    
759      /**
760       * Indicates whether the server should reject entries which do not contain
761       * exactly one structural object class.  This setting will be ignored if a
762       * {@code null} schema is in place.
763       *
764       * @return  {@code true} if the server should reject entries which do not
765       *          contain exactly one structural object class, or {@code false} if
766       *          it should allow entries which do not have any structural class or
767       *          that have multiple structural classes.
768       */
769      public boolean enforceSingleStructuralObjectClass()
770      {
771        return enforceSingleStructuralObjectClass;
772      }
773    
774    
775    
776      /**
777       * Specifies whether the server should reject entries which do not contain
778       * exactly one structural object class.  This setting will be ignored if a
779       * {@code null} schema is in place.
780       *
781       * @param  enforceSingleStructuralObjectClass  Indicates whether the server
782       *                                             should reject entries which do
783       *                                             not contain exactly one
784       *                                             structural object class.
785       */
786      public void setEnforceSingleStructuralObjectClass(
787                       final boolean enforceSingleStructuralObjectClass)
788      {
789        this.enforceSingleStructuralObjectClass =
790             enforceSingleStructuralObjectClass;
791      }
792    
793    
794    
795      /**
796       * Retrieves the log handler that should be used to record access log messages
797       * about operations processed by the server, if any.
798       *
799       * @return  The log handler that should be used to record access log messages
800       *          about operations processed by the server, or {@code null} if no
801       *          access logging should be performed.
802       */
803      public Handler getAccessLogHandler()
804      {
805        return accessLogHandler;
806      }
807    
808    
809    
810      /**
811       * Specifies the log handler that should be used to record access log messages
812       * about operations processed by the server.
813       *
814       * @param  accessLogHandler  The log handler that should be used to record
815       *                           access log messages about operations processed by
816       *                           the server.  It may be {@code null} if no access
817       *                           logging should be performed.
818       */
819      public void setAccessLogHandler(final Handler accessLogHandler)
820      {
821        this.accessLogHandler = accessLogHandler;
822      }
823    
824    
825    
826      /**
827       * Retrieves the log handler that should be used to record detailed messages
828       * about LDAP communication to and from the server, which may be useful for
829       * debugging purposes.
830       *
831       * @return  The log handler that should be used to record detailed
832       *          protocol-level debug messages about LDAP communication to and from
833       *          the server, or {@code null} if no debug logging should be
834       *          performed.
835       */
836      public Handler getLDAPDebugLogHandler()
837      {
838        return ldapDebugLogHandler;
839      }
840    
841    
842    
843      /**
844       * Specifies the log handler that should be used to record detailed messages
845       * about LDAP communication to and from the server, which may be useful for
846       * debugging purposes.
847       *
848       * @param  ldapDebugLogHandler  The log handler that should be used to record
849       *                              detailed messages about LDAP communication to
850       *                              and from the server.  It may be {@code null}
851       *                              if no LDAP debug logging should be performed.
852       */
853      public void setLDAPDebugLogHandler(final Handler ldapDebugLogHandler)
854      {
855        this.ldapDebugLogHandler = ldapDebugLogHandler;
856      }
857    
858    
859    
860      /**
861       * Retrieves the path to a file to be written with generated code that may
862       * be used to construct the requests processed by the server.
863       *
864       * @return  The path to a file to be written with generated code that may be
865       *          used to construct the requests processed by the server, or
866       *          {@code null} if no code log should be written.
867       */
868      public String getCodeLogPath()
869      {
870        return codeLogPath;
871      }
872    
873    
874    
875      /**
876       * Indicates whether the code log should include sample code for processing
877       * the generated requests.  This will only be used if {@link #getCodeLogPath}
878       * returns a non-{@code null} value.
879       *
880       * @return  {@code false} if the code log should only include code that
881       *          corresponds to requests received from clients, or {@code true} if
882       *          the code log should also include sample code for processing the
883       *          generated requests and interpreting the results.
884       */
885      public boolean includeRequestProcessingInCodeLog()
886      {
887        return includeRequestProcessingInCodeLog;
888      }
889    
890    
891    
892      /**
893       * Specifies information about code logging that should be performed by the
894       * server, if any.
895       *
896       * @param  codeLogPath        The path to the file to which a code log should
897       *                            be written.  It may be {@code null} if no code
898       *                            log should be written.
899       * @param  includeProcessing  Indicates whether to include sample code that
900       *                            demonstrates how to process the requests and
901       *                            interpret the results.  This will only be
902       *                            used if the {@code codeLogPath} argument is
903       *                            non-{@code null}.
904       */
905      public void setCodeLogDetails(final String codeLogPath,
906                                    final boolean includeProcessing)
907      {
908        this.codeLogPath = codeLogPath;
909        includeRequestProcessingInCodeLog = includeProcessing;
910      }
911    
912    
913    
914      /**
915       * Retrieves a list of the operation interceptors that may be used to
916       * intercept and transform requests before they are processed by the in-memory
917       * directory server, and/or to intercept and transform responses before they
918       * are returned to the client.  The contents of the list may be altered by the
919       * caller.
920       *
921       * @return  An updatable list of the operation interceptors that may be used
922       *          to intercept and transform requests and/or responses.
923       */
924      public List<InMemoryOperationInterceptor> getOperationInterceptors()
925      {
926        return operationInterceptors;
927      }
928    
929    
930    
931      /**
932       * Adds the provided operation interceptor to the list of operation
933       * interceptors that may be used to transform requests before they are
934       * processed by the in-memory directory server, and/or to transform responses
935       * before they are returned to the client.
936       *
937       * @param  interceptor  The operation interceptor that should be invoked in
938       *                      the course of processing requests and responses.
939       */
940      public void addInMemoryOperationInterceptor(
941                       final InMemoryOperationInterceptor interceptor)
942      {
943        operationInterceptors.add(interceptor);
944      }
945    
946    
947    
948      /**
949       * Retrieves a list of the extended operation handlers that may be used to
950       * process extended operations in the server.  The contents of the list may
951       * be altered by the caller.
952       *
953       * @return  An updatable list of the extended operation handlers that may be
954       *          used to process extended operations in the server.
955       */
956      public List<InMemoryExtendedOperationHandler> getExtendedOperationHandlers()
957      {
958        return extendedOperationHandlers;
959      }
960    
961    
962    
963      /**
964       * Adds the provided extended operation handler for use by the server for
965       * processing certain types of extended operations.
966       *
967       * @param  handler  The extended operation handler that should be used by the
968       *                  server for processing certain types of extended
969       *                  operations.
970       */
971      public void addExtendedOperationHandler(
972                       final InMemoryExtendedOperationHandler handler)
973      {
974        extendedOperationHandlers.add(handler);
975      }
976    
977    
978    
979      /**
980       * Retrieves a list of the SASL bind handlers that may be used to process
981       * SASL bind requests in the server.  The contents of the list may be altered
982       * by the caller.
983       *
984       * @return  An updatable list of the SASL bind handlers that may be used to
985       *          process SASL bind requests in the server.
986       */
987      public List<InMemorySASLBindHandler> getSASLBindHandlers()
988      {
989        return saslBindHandlers;
990      }
991    
992    
993    
994      /**
995       * Adds the provided SASL bind handler for use by the server for processing
996       * certain types of SASL bind requests.
997       *
998       * @param  handler  The SASL bind handler that should be used by the server
999       *                  for processing certain types of SASL bind requests.
1000       */
1001      public void addSASLBindHandler(final InMemorySASLBindHandler handler)
1002      {
1003        saslBindHandlers.add(handler);
1004      }
1005    
1006    
1007    
1008      /**
1009       * Indicates whether the server should automatically generate operational
1010       * attributes (including entryDN, entryUUID, creatorsName, createTimestamp,
1011       * modifiersName, modifyTimestamp, and subschemaSubentry) for entries in the
1012       * server.
1013       *
1014       * @return  {@code true} if the server should automatically generate
1015       *          operational attributes for entries in the server, or {@code false}
1016       *          if not.
1017       */
1018      public boolean generateOperationalAttributes()
1019      {
1020        return generateOperationalAttributes;
1021      }
1022    
1023    
1024    
1025      /**
1026       * Specifies whether the server should automatically generate operational
1027       * attributes (including entryDN, entryUUID, creatorsName, createTimestamp,
1028       * modifiersName, modifyTimestamp, and subschemaSubentry) for entries in the
1029       * server.
1030       *
1031       * @param  generateOperationalAttributes  Indicates whether the server should
1032       *                                        automatically generate operational
1033       *                                        attributes for entries in the
1034       *                                        server.
1035       */
1036      public void setGenerateOperationalAttributes(
1037                       final boolean generateOperationalAttributes)
1038      {
1039        this.generateOperationalAttributes = generateOperationalAttributes;
1040      }
1041    
1042    
1043    
1044      /**
1045       * Retrieves the maximum number of changelog entries that the server should
1046       * maintain.
1047       *
1048       * @return  The maximum number of changelog entries that the server should
1049       *          maintain, or 0 if the server should not maintain a changelog.
1050       */
1051      public int getMaxChangeLogEntries()
1052      {
1053        return maxChangeLogEntries;
1054      }
1055    
1056    
1057    
1058      /**
1059       * Specifies the maximum number of changelog entries that the server should
1060       * maintain.  A value less than or equal to zero indicates that the server
1061       * should not attempt to maintain a changelog.
1062       *
1063       * @param  maxChangeLogEntries  The maximum number of changelog entries that
1064       *                              the server should maintain.
1065       */
1066      public void setMaxChangeLogEntries(final int maxChangeLogEntries)
1067      {
1068        if (maxChangeLogEntries < 0)
1069        {
1070          this.maxChangeLogEntries = 0;
1071        }
1072        else
1073        {
1074          this.maxChangeLogEntries = maxChangeLogEntries;
1075        }
1076      }
1077    
1078    
1079    
1080      /**
1081       * Retrieves the maximum number of concurrent connections that the server will
1082       * allow.  If a client tries to establish a new connection while the server
1083       * already has the maximum number of concurrent connections, then the new
1084       * connection will be rejected.  Note that if the server is configured with
1085       * multiple listeners, then each listener will be allowed to have up to this
1086       * number of connections.
1087       *
1088       * @return  The maximum number of concurrent connections that the server will
1089       *          allow, or zero if no limit should be enforced.
1090       */
1091      public int getMaxConnections()
1092      {
1093        return maxConnections;
1094      }
1095    
1096    
1097    
1098      /**
1099       * Specifies the maximum number of concurrent connections that the server will
1100       * allow.  If a client tries to establish a new connection while the server
1101       * already has the maximum number of concurrent connections, then the new
1102       * connection will be rejected.  Note that if the server is configured with
1103       * multiple listeners, then each listener will be allowed to have up to this
1104       * number of connections.
1105       *
1106       * @param  maxConnections  The maximum number of concurrent connections that
1107       *                         the server will allow.  A value that is less than
1108       *                         or equal to zero indicates no limit.
1109       */
1110      public void setMaxConnections(final int maxConnections)
1111      {
1112        if (maxConnections > 0)
1113        {
1114          this.maxConnections = maxConnections;
1115        }
1116        else
1117        {
1118          this.maxConnections = 0;
1119        }
1120      }
1121    
1122    
1123    
1124      /**
1125       * Retrieves the maximum number of entries that the server should return in
1126       * any search operation.
1127       *
1128       * @return  The maximum number of entries that the server should return in any
1129       *          search operation, or zero if no limit should be enforced.
1130       */
1131      public int getMaxSizeLimit()
1132      {
1133        return maxSizeLimit;
1134      }
1135    
1136    
1137    
1138      /**
1139       * Specifies the maximum number of entries that the server should return in
1140       * any search operation.  A value less than or equal to zero indicates that no
1141       * maximum limit should be enforced.
1142       *
1143       * @param  maxSizeLimit  The maximum number of entries that the server should
1144       *                       return in any search operation.
1145       */
1146      public void setMaxSizeLimit(final int maxSizeLimit)
1147      {
1148        if (maxSizeLimit > 0)
1149        {
1150          this.maxSizeLimit = maxSizeLimit;
1151        }
1152        else
1153        {
1154          this.maxSizeLimit = 0;
1155        }
1156      }
1157    
1158    
1159    
1160      /**
1161       * Retrieves a list containing the names or OIDs of the attribute types for
1162       * which to maintain an equality index to improve the performance of certain
1163       * kinds of searches.
1164       *
1165       * @return  A list containing the names or OIDs of the attribute types for
1166       *          which to maintain an equality index to improve the performance of
1167       *          certain kinds of searches, or an empty list if no equality indexes
1168       *          should be created.
1169       */
1170      public List<String> getEqualityIndexAttributes()
1171      {
1172        return equalityIndexAttributes;
1173      }
1174    
1175    
1176    
1177      /**
1178       * Specifies the names or OIDs of the attribute types for which to maintain an
1179       * equality index to improve the performance of certain kinds of searches.
1180       *
1181       * @param  equalityIndexAttributes  The names or OIDs of the attributes for
1182       *                                  which to maintain an equality index to
1183       *                                  improve the performance of certain kinds
1184       *                                  of searches.  It may be {@code null} or
1185       *                                  empty to indicate that no equality indexes
1186       *                                  should be maintained.
1187       */
1188      public void setEqualityIndexAttributes(
1189                       final String... equalityIndexAttributes)
1190      {
1191        setEqualityIndexAttributes(StaticUtils.toList(equalityIndexAttributes));
1192      }
1193    
1194    
1195    
1196      /**
1197       * Specifies the names or OIDs of the attribute types for which to maintain an
1198       * equality index to improve the performance of certain kinds of searches.
1199       *
1200       * @param  equalityIndexAttributes  The names or OIDs of the attributes for
1201       *                                  which to maintain an equality index to
1202       *                                  improve the performance of certain kinds
1203       *                                  of searches.  It may be {@code null} or
1204       *                                  empty to indicate that no equality indexes
1205       *                                  should be maintained.
1206       */
1207      public void setEqualityIndexAttributes(
1208                       final Collection<String> equalityIndexAttributes)
1209      {
1210        this.equalityIndexAttributes.clear();
1211        if (equalityIndexAttributes != null)
1212        {
1213          this.equalityIndexAttributes.addAll(equalityIndexAttributes);
1214        }
1215      }
1216    
1217    
1218    
1219      /**
1220       * Retrieves the names of the attributes for which referential integrity
1221       * should be maintained.  If referential integrity is to be provided and an
1222       * entry is removed, then any other entries containing one of the specified
1223       * attributes with a value equal to the DN of the entry that was removed, then
1224       * that value will also be removed.  Similarly, if an entry is moved or
1225       * renamed, then any references to that entry in one of the specified
1226       * attributes will be updated to reflect the new DN.
1227       *
1228       * @return  The names of the attributes for which referential integrity should
1229       *          be maintained, or an empty set if referential integrity should not
1230       *          be maintained for any attributes.
1231       */
1232      public Set<String> getReferentialIntegrityAttributes()
1233      {
1234        return referentialIntegrityAttributes;
1235      }
1236    
1237    
1238    
1239      /**
1240       * Specifies the names of the attributes for which referential integrity
1241       * should be maintained.  If referential integrity is to be provided and an
1242       * entry is removed, then any other entries containing one of the specified
1243       * attributes with a value equal to the DN of the entry that was removed, then
1244       * that value will also be removed.  Similarly, if an entry is moved or
1245       * renamed, then any references to that entry in one of the specified
1246       * attributes will be updated to reflect the new DN.
1247       *
1248       * @param  referentialIntegrityAttributes  The names of the attributes for
1249       *                                          which referential integrity should
1250       *                                          be maintained.  The values of
1251       *                                          these attributes should be DNs.
1252       *                                          It may be {@code null} or empty if
1253       *                                          referential integrity should not
1254       *                                          be maintained.
1255       */
1256      public void setReferentialIntegrityAttributes(
1257                       final String... referentialIntegrityAttributes)
1258      {
1259        setReferentialIntegrityAttributes(
1260             StaticUtils.toList(referentialIntegrityAttributes));
1261      }
1262    
1263    
1264    
1265      /**
1266       * Specifies the names of the attributes for which referential integrity
1267       * should be maintained.  If referential integrity is to be provided and an
1268       * entry is removed, then any other entries containing one of the specified
1269       * attributes with a value equal to the DN of the entry that was removed, then
1270       * that value will also be removed.  Similarly, if an entry is moved or
1271       * renamed, then any references to that entry in one of the specified
1272       * attributes will be updated to reflect the new DN.
1273       *
1274       * @param  referentialIntegrityAttributes  The names of the attributes for
1275       *                                          which referential integrity should
1276       *                                          be maintained.  The values of
1277       *                                          these attributes should be DNs.
1278       *                                          It may be {@code null} or empty if
1279       *                                          referential integrity should not
1280       *                                          be maintained.
1281       */
1282      public void setReferentialIntegrityAttributes(
1283                       final Collection<String> referentialIntegrityAttributes)
1284      {
1285        this.referentialIntegrityAttributes.clear();
1286        if (referentialIntegrityAttributes != null)
1287        {
1288          this.referentialIntegrityAttributes.addAll(
1289               referentialIntegrityAttributes);
1290        }
1291      }
1292    
1293    
1294    
1295      /**
1296       * Retrieves the vendor name value to report in the server root DSE.
1297       *
1298       * @return  The vendor name value to report in the server root DSE, or
1299       *          {@code null} if no vendor name should appear.
1300       */
1301      public String getVendorName()
1302      {
1303        return vendorName;
1304      }
1305    
1306    
1307    
1308      /**
1309       * Specifies the vendor name value to report in the server root DSE.
1310       *
1311       * @param  vendorName  The vendor name value to report in the server root DSE.
1312       *                     It may be {@code null} if no vendor name should appear.
1313       */
1314      public void setVendorName(final String vendorName)
1315      {
1316        this.vendorName = vendorName;
1317      }
1318    
1319    
1320    
1321      /**
1322       * Retrieves the vendor version value to report in the server root DSE.
1323       *
1324       * @return  The vendor version value to report in the server root DSE, or
1325       *          {@code null} if no vendor version should appear.
1326       */
1327      public String getVendorVersion()
1328      {
1329        return vendorVersion;
1330      }
1331    
1332    
1333    
1334      /**
1335       * Specifies the vendor version value to report in the server root DSE.
1336       *
1337       * @param  vendorVersion  The vendor version value to report in the server
1338       *                        root DSE.  It may be {@code null} if no vendor
1339       *                        version should appear.
1340       */
1341      public void setVendorVersion(final String vendorVersion)
1342      {
1343        this.vendorVersion = vendorVersion;
1344      }
1345    
1346    
1347    
1348      /**
1349       * Retrieves a predefined entry that should always be returned as the
1350       * in-memory directory server's root DSE, if defined.
1351       *
1352       * @return  A predefined entry that should always be returned as the in-memory
1353       *          directory server's root DSE, or {@code null} if the root DSE
1354       *          should be dynamically generated.
1355       */
1356      public ReadOnlyEntry getRootDSEEntry()
1357      {
1358        return rootDSEEntry;
1359      }
1360    
1361    
1362    
1363      /**
1364       * Specifies an entry that should always be returned as the in-memory
1365       * directory server's root DSE.  Note that if a specific root DSE entry is
1366       * provided, then
1367       *
1368       * @param  rootDSEEntry  An entry that should always be returned as the
1369       *                       in-memory directory server's root DSE, or
1370       *                       {@code null} to indicate that the root DSE should be
1371       *                       dynamically generated.
1372       */
1373      public void setRootDSEEntry(final Entry rootDSEEntry)
1374      {
1375        if (rootDSEEntry == null)
1376        {
1377          this.rootDSEEntry = null;
1378          return;
1379        }
1380    
1381        final Entry e = rootDSEEntry.duplicate();
1382        e.setDN("");
1383        this.rootDSEEntry = new ReadOnlyEntry(e);
1384      }
1385    
1386    
1387    
1388      /**
1389       * Parses the provided set of strings as DNs.
1390       *
1391       * @param  dnStrings  The array of strings to be parsed as DNs.
1392       * @param  schema     The schema to use to generate the normalized
1393       *                    representations of the DNs, if available.
1394       *
1395       * @return  The array of parsed DNs.
1396       *
1397       * @throws  LDAPException  If any of the provided strings cannot be parsed as
1398       *                         DNs.
1399       */
1400      private static DN[] parseDNs(final Schema schema, final String... dnStrings)
1401              throws LDAPException
1402      {
1403        if (dnStrings == null)
1404        {
1405          return null;
1406        }
1407    
1408        final DN[] dns = new DN[dnStrings.length];
1409        for (int i=0; i < dns.length; i++)
1410        {
1411          dns[i] = new DN(dnStrings[i], schema);
1412        }
1413        return dns;
1414      }
1415    
1416    
1417    
1418      /**
1419       * Retrieves a string representation of this in-memory directory server
1420       * configuration.
1421       *
1422       * @return  A string representation of this in-memory directory server
1423       *          configuration.
1424       */
1425      @Override()
1426      public String toString()
1427      {
1428        final StringBuilder buffer = new StringBuilder();
1429        toString(buffer);
1430        return buffer.toString();
1431      }
1432    
1433    
1434    
1435      /**
1436       * Appends a string representation of this in-memory directory server
1437       * configuration to the provided buffer.
1438       *
1439       * @param  buffer  The buffer to which the string representation should be
1440       *                 appended.
1441       */
1442      public void toString(final StringBuilder buffer)
1443      {
1444        buffer.append("InMemoryDirectoryServerConfig(baseDNs={");
1445    
1446        for (int i=0; i < baseDNs.length; i++)
1447        {
1448          if (i > 0)
1449          {
1450            buffer.append(", ");
1451          }
1452    
1453          buffer.append('\'');
1454          baseDNs[i].toString(buffer);
1455          buffer.append('\'');
1456        }
1457        buffer.append('}');
1458    
1459        buffer.append(", listenerConfigs={");
1460    
1461        final Iterator<InMemoryListenerConfig> listenerCfgIterator =
1462             listenerConfigs.iterator();
1463        while(listenerCfgIterator.hasNext())
1464        {
1465          listenerCfgIterator.next().toString(buffer);
1466          if (listenerCfgIterator.hasNext())
1467          {
1468            buffer.append(", ");
1469          }
1470        }
1471        buffer.append('}');
1472    
1473        buffer.append(", schemaProvided=");
1474        buffer.append((schema != null));
1475        buffer.append(", enforceAttributeSyntaxCompliance=");
1476        buffer.append(enforceAttributeSyntaxCompliance);
1477        buffer.append(", enforceSingleStructuralObjectClass=");
1478        buffer.append(enforceSingleStructuralObjectClass);
1479    
1480        if (! additionalBindCredentials.isEmpty())
1481        {
1482          buffer.append(", additionalBindDNs={");
1483    
1484          final Iterator<DN> bindDNIterator =
1485               additionalBindCredentials.keySet().iterator();
1486          while (bindDNIterator.hasNext())
1487          {
1488            buffer.append('\'');
1489            bindDNIterator.next().toString(buffer);
1490            buffer.append('\'');
1491            if (bindDNIterator.hasNext())
1492            {
1493              buffer.append(", ");
1494            }
1495          }
1496          buffer.append('}');
1497        }
1498    
1499        if (! equalityIndexAttributes.isEmpty())
1500        {
1501          buffer.append(", equalityIndexAttributes={");
1502    
1503          final Iterator<String> attrIterator = equalityIndexAttributes.iterator();
1504          while (attrIterator.hasNext())
1505          {
1506            buffer.append('\'');
1507            buffer.append(attrIterator.next());
1508            buffer.append('\'');
1509            if (attrIterator.hasNext())
1510            {
1511              buffer.append(", ");
1512            }
1513          }
1514          buffer.append('}');
1515        }
1516    
1517        if (! referentialIntegrityAttributes.isEmpty())
1518        {
1519          buffer.append(", referentialIntegrityAttributes={");
1520    
1521          final Iterator<String> attrIterator =
1522               referentialIntegrityAttributes.iterator();
1523          while (attrIterator.hasNext())
1524          {
1525            buffer.append('\'');
1526            buffer.append(attrIterator.next());
1527            buffer.append('\'');
1528            if (attrIterator.hasNext())
1529            {
1530              buffer.append(", ");
1531            }
1532          }
1533          buffer.append('}');
1534        }
1535    
1536        buffer.append(", generateOperationalAttributes=");
1537        buffer.append(generateOperationalAttributes);
1538    
1539        if (maxChangeLogEntries > 0)
1540        {
1541          buffer.append(", maxChangelogEntries=");
1542          buffer.append(maxChangeLogEntries);
1543        }
1544    
1545        buffer.append(", maxConnections=");
1546        buffer.append(maxConnections);
1547        buffer.append(", maxSizeLimit=");
1548        buffer.append(maxSizeLimit);
1549    
1550        if (! extendedOperationHandlers.isEmpty())
1551        {
1552          buffer.append(", extendedOperationHandlers={");
1553    
1554          final Iterator<InMemoryExtendedOperationHandler>
1555               handlerIterator = extendedOperationHandlers.iterator();
1556          while (handlerIterator.hasNext())
1557          {
1558            buffer.append(handlerIterator.next().toString());
1559            if (handlerIterator.hasNext())
1560            {
1561              buffer.append(", ");
1562            }
1563          }
1564          buffer.append('}');
1565        }
1566    
1567        if (! saslBindHandlers.isEmpty())
1568        {
1569          buffer.append(", saslBindHandlers={");
1570    
1571          final Iterator<InMemorySASLBindHandler>
1572               handlerIterator = saslBindHandlers.iterator();
1573          while (handlerIterator.hasNext())
1574          {
1575            buffer.append(handlerIterator.next().toString());
1576            if (handlerIterator.hasNext())
1577            {
1578              buffer.append(", ");
1579            }
1580          }
1581          buffer.append('}');
1582        }
1583    
1584        if (accessLogHandler != null)
1585        {
1586          buffer.append(", accessLogHandlerClass='");
1587          buffer.append(accessLogHandler.getClass().getName());
1588          buffer.append('\'');
1589        }
1590    
1591        if (ldapDebugLogHandler != null)
1592        {
1593          buffer.append(", ldapDebugLogHandlerClass='");
1594          buffer.append(ldapDebugLogHandler.getClass().getName());
1595          buffer.append('\'');
1596        }
1597    
1598        if (codeLogPath != null)
1599        {
1600          buffer.append(", codeLogPath='");
1601          buffer.append(codeLogPath);
1602          buffer.append("', includeRequestProcessingInCodeLog=");
1603          buffer.append(includeRequestProcessingInCodeLog);
1604        }
1605    
1606        if (exceptionHandler != null)
1607        {
1608          buffer.append(", listenerExceptionHandlerClass='");
1609          buffer.append(exceptionHandler.getClass().getName());
1610          buffer.append('\'');
1611        }
1612    
1613        if (vendorName != null)
1614        {
1615          buffer.append(", vendorName='");
1616          buffer.append(vendorName);
1617          buffer.append('\'');
1618        }
1619    
1620        if (vendorVersion != null)
1621        {
1622          buffer.append(", vendorVersion='");
1623          buffer.append(vendorVersion);
1624          buffer.append('\'');
1625        }
1626    
1627        buffer.append(')');
1628      }
1629    }