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 }