001    /*
002     * Copyright 2011-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2011-2015 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.listener.interceptor.InMemoryOperationInterceptor;
038    import com.unboundid.ldap.sdk.DN;
039    import com.unboundid.ldap.sdk.LDAPException;
040    import com.unboundid.ldap.sdk.OperationType;
041    import com.unboundid.ldap.sdk.ResultCode;
042    import com.unboundid.ldap.sdk.Version;
043    import com.unboundid.ldap.sdk.schema.Schema;
044    import com.unboundid.util.Mutable;
045    import com.unboundid.util.NotExtensible;
046    import com.unboundid.util.StaticUtils;
047    import com.unboundid.util.ThreadSafety;
048    import com.unboundid.util.ThreadSafetyLevel;
049    
050    import 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)
096    public 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    }