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