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