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