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