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