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