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