001 /*
002 * Copyright 2008-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 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.sdk.schema;
022
023
024
025 import java.io.Serializable;
026 import java.util.ArrayList;
027 import java.util.Collection;
028 import java.util.Collections;
029 import java.util.HashSet;
030 import java.util.Iterator;
031 import java.util.List;
032 import java.util.Map;
033 import java.util.Set;
034 import java.util.TreeMap;
035 import java.util.concurrent.ConcurrentHashMap;
036 import java.util.concurrent.atomic.AtomicLong;
037 import java.util.concurrent.atomic.AtomicReference;
038 import java.util.regex.Pattern;
039
040 import com.unboundid.asn1.ASN1OctetString;
041 import com.unboundid.ldap.matchingrules.MatchingRule;
042 import com.unboundid.ldap.sdk.Attribute;
043 import com.unboundid.ldap.sdk.Entry;
044 import com.unboundid.ldap.sdk.LDAPException;
045 import com.unboundid.ldap.sdk.RDN;
046 import com.unboundid.util.ThreadSafety;
047 import com.unboundid.util.ThreadSafetyLevel;
048
049 import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
050 import static com.unboundid.util.Debug.*;
051 import static com.unboundid.util.StaticUtils.*;
052 import static com.unboundid.util.Validator.*;
053
054
055
056 /**
057 * This class provides a mechanism for validating entries against a schema. It
058 * provides the ability to customize the types of validation to perform, and can
059 * collect information about the entries that fail validation to provide a
060 * summary of the problems encountered.
061 * <BR><BR>
062 * The types of validation that may be performed for each entry include:
063 * <UL>
064 * <LI>Ensure that the entry has a valid DN.</LI>
065 * <LI>Ensure that all attribute values used in the entry's RDN are also
066 * present in the entry.</LI>
067 * <LI>Ensure that the entry has exactly one structural object class.</LI>
068 * <LI>Ensure that all of the object classes for the entry are defined in the
069 * schema.</LI>
070 * <LI>Ensure that all of the auxiliary classes for the entry are allowed by
071 * the DIT content rule for the entry's structural object class (if such a
072 * DIT content rule is defined).</LI>
073 * <LI>Ensure that all attributes contained in the entry are defined in the
074 * schema.</LI>
075 * <LI>Ensure that all attributes required by the entry's object classes or
076 * DIT content rule (if defined) are present in the entry.</LI>
077 * <LI>Ensure that all of the user attributes contained in the entry are
078 * allowed by the entry's object classes or DIT content rule (if
079 * defined).</LI>
080 * <LI>Ensure that all attribute values conform to the requirements of the
081 * associated attribute syntax.</LI>
082 * <LI>Ensure that all attributes with multiple values are defined as
083 * multi-valued in the associated schema.</LI>
084 * <LI>If there is a name form associated with the entry's structural object
085 * class, then ensure that the entry's RDN satisfies its constraints.</LI>
086 * </UL>
087 * All of these forms of validation will be performed by default, but individual
088 * types of validation may be enabled or disabled.
089 * <BR><BR>
090 * This class will not make any attempt to validate compliance with DIT
091 * structure rules, nor will it check the OBSOLETE field for any of the schema
092 * elements. In addition, attempts to validate whether attribute values
093 * conform to the syntax for the associated attribute type may only be
094 * completely accurate for syntaxes supported by the LDAP SDK.
095 * <BR><BR>
096 * This class is largely threadsafe, and the {@link EntryValidator#entryIsValid}
097 * is designed so that it can be invoked concurrently by multiple threads.
098 * Note, however, that it is not recommended that the any of the other methods
099 * in this class be used while any threads are running the {@code entryIsValid}
100 * method because changing the configuration or attempting to retrieve retrieve
101 * information may yield inaccurate or inconsistent results.
102 */
103 @ThreadSafety(level=ThreadSafetyLevel.MOSTLY_THREADSAFE)
104 public final class EntryValidator
105 implements Serializable
106 {
107 /**
108 * The serial version UID for this serializable class.
109 */
110 private static final long serialVersionUID = -8945609557086398241L;
111
112
113
114 // A count of the total number of entries examined.
115 private final AtomicLong entriesExamined;
116
117 // A count of the number of entries missing an attribute value contained in
118 // the RDN.
119 private final AtomicLong entriesMissingRDNValues;
120
121 // A count of the total number of invalid entries encountered.
122 private final AtomicLong invalidEntries;
123
124 // A count of the number of entries with DNs that could not be parsed.
125 private final AtomicLong malformedDNs;
126
127 // A count of the number of entries missing a superior object class.
128 private final AtomicLong missingSuperiorClasses;
129
130 // A count of the number of entries containing multiple structural object
131 // classes.
132 private final AtomicLong multipleStructuralClasses;
133
134 // A count of the number of entries with RDNs that violate the associated
135 // name form.
136 private final AtomicLong nameFormViolations;
137
138 // A count of the number of entries without any object class.
139 private final AtomicLong noObjectClasses;
140
141 // A count of the number of entries without a structural object class.
142 private final AtomicLong noStructuralClass;
143
144 // Indicates whether an entry should be considered invalid if it contains an
145 // attribute value which violates the associated attribute syntax.
146 private boolean checkAttributeSyntax;
147
148 // Indicates whether an entry should be considered invalid if it contains one
149 // or more attribute values in its RDN that are not present in the set of
150 // entry attributes.
151 private boolean checkEntryMissingRDNValues;
152
153 // Indicates whether an entry should be considered invalid if its DN cannot be
154 // parsed.
155 private boolean checkMalformedDNs;
156
157 // Indicates whether an entry should be considered invalid if it is missing
158 // attributes required by its object classes or DIT content rule.
159 private boolean checkMissingAttributes;
160
161 // Indicates whether an entry should be considered invalid if it is missing
162 // one or more superior object classes.
163 private boolean checkMissingSuperiorObjectClasses;
164
165 // Indicates whether an entry should be considered invalid if its RDN does not
166 // conform to name form requirements.
167 private boolean checkNameForms;
168
169 // Indicates whether an entry should be considered invalid if it contains any
170 // attributes which are not allowed by its object classes or DIT content rule.
171 private boolean checkProhibitedAttributes;
172
173 // Indicates whether an entry should be considered invalid if it contains an
174 // auxiliary class that is not allowed by its DIT content rule or an abstract
175 // class that is not associated with a non-abstract class.
176 private boolean checkProhibitedObjectClasses;
177
178 // Indicates whether an entry should be considered invalid if it contains any
179 // attribute defined as single-valued with more than one values.
180 private boolean checkSingleValuedAttributes;
181
182 // Indicates whether an entry should be considered invalid if it does not
183 // contain exactly one structural object class.
184 private boolean checkStructuralObjectClasses;
185
186 // Indicates whether an entry should be considered invalid if it contains an
187 // attribute which is not defined in the schema.
188 private boolean checkUndefinedAttributes;
189
190 // Indicates whether an entry should be considered invalid if it contains an
191 // object class which is not defined in the schema.
192 private boolean checkUndefinedObjectClasses;
193
194 // A map of the attributes with values violating the associated syntax to the
195 // number of values found violating the syntax.
196 private final ConcurrentHashMap<String,AtomicLong> attributesViolatingSyntax;
197
198 // A map of the required attribute types that were missing from entries to
199 // the number of entries missing them.
200 private final ConcurrentHashMap<String,AtomicLong> missingAttributes;
201
202 // A map of the prohibited attribute types that were included in entries to
203 // the number of entries referencing them.
204 private final ConcurrentHashMap<String,AtomicLong> prohibitedAttributes;
205
206 // A map of the prohibited auxiliary object classes that were included in
207 // entries to the number of entries referencing them.
208 private final ConcurrentHashMap<String,AtomicLong> prohibitedObjectClasses;
209
210 // A map of the single-valued attributes with multiple values to the number
211 // of entries with multiple values for those attributes.
212 private final ConcurrentHashMap<String,AtomicLong> singleValueViolations;
213
214 // A map of undefined attribute types to the number of entries referencing
215 // them.
216 private final ConcurrentHashMap<String,AtomicLong> undefinedAttributes;
217
218 // A map of undefined object classes to the number of entries referencing
219 // them.
220 private final ConcurrentHashMap<String,AtomicLong> undefinedObjectClasses;
221
222 // The schema against which entries will be validated.
223 private final Schema schema;
224
225 // The attribute types for which to ignore syntax violations.
226 private Set<AttributeTypeDefinition> ignoreSyntaxViolationTypes;
227
228
229
230 /**
231 * Creates a new entry validator that will validate entries according to the
232 * provided schema.
233 *
234 * @param schema The schema against which entries will be validated.
235 */
236 public EntryValidator(final Schema schema)
237 {
238 this.schema = schema;
239
240 checkAttributeSyntax = true;
241 checkEntryMissingRDNValues = true;
242 checkMalformedDNs = true;
243 checkMissingAttributes = true;
244 checkMissingSuperiorObjectClasses = true;
245 checkNameForms = true;
246 checkProhibitedAttributes = true;
247 checkProhibitedObjectClasses = true;
248 checkSingleValuedAttributes = true;
249 checkStructuralObjectClasses = true;
250 checkUndefinedAttributes = true;
251 checkUndefinedObjectClasses = true;
252
253 ignoreSyntaxViolationTypes = Collections.emptySet();
254
255 entriesExamined = new AtomicLong(0L);
256 entriesMissingRDNValues = new AtomicLong(0L);
257 invalidEntries = new AtomicLong(0L);
258 malformedDNs = new AtomicLong(0L);
259 missingSuperiorClasses = new AtomicLong(0L);
260 multipleStructuralClasses = new AtomicLong(0L);
261 nameFormViolations = new AtomicLong(0L);
262 noObjectClasses = new AtomicLong(0L);
263 noStructuralClass = new AtomicLong(0L);
264
265 attributesViolatingSyntax = new ConcurrentHashMap<String,AtomicLong>();
266 missingAttributes = new ConcurrentHashMap<String,AtomicLong>();
267 prohibitedAttributes = new ConcurrentHashMap<String,AtomicLong>();
268 prohibitedObjectClasses = new ConcurrentHashMap<String,AtomicLong>();
269 singleValueViolations = new ConcurrentHashMap<String,AtomicLong>();
270 undefinedAttributes = new ConcurrentHashMap<String,AtomicLong>();
271 undefinedObjectClasses = new ConcurrentHashMap<String,AtomicLong>();
272 }
273
274
275
276 /**
277 * Indicates whether the entry validator should consider entries invalid if
278 * they are missing attributes which are required by the object classes or
279 * DIT content rule (if applicable) for the entry.
280 *
281 * @return {@code true} if entries that are missing attributes required by
282 * its object classes or DIT content rule should be considered
283 * invalid, or {@code false} if not.
284 */
285 public boolean checkMissingAttributes()
286 {
287 return checkMissingAttributes;
288 }
289
290
291
292 /**
293 * Specifies whether the entry validator should consider entries invalid if
294 * they are missing attributes which are required by the object classes or DIT
295 * content rule (if applicable) for the entry.
296 *
297 * @param checkMissingAttributes Indicates whether the entry validator
298 * should consider entries invalid if they are
299 * missing required attributes.
300 */
301 public void setCheckMissingAttributes(final boolean checkMissingAttributes)
302 {
303 this.checkMissingAttributes = checkMissingAttributes;
304 }
305
306
307
308 /**
309 * Indicates whether the entry validator should consider entries invalid if
310 * they are missing any superior classes for the included set of object
311 * classes.
312 *
313 * @return {@code true} if entries that are missing superior classes should
314 * be considered invalid, or {@code false} if not.
315 */
316 public boolean checkMissingSuperiorObjectClasses()
317 {
318 return checkMissingSuperiorObjectClasses;
319 }
320
321
322
323 /**
324 * Specifies whether the entry validator should consider entries invalid if
325 * they are missing any superior classes for the included set of object
326 * classes.
327 *
328 * @param checkMissingSuperiorObjectClasses Indicates whether the entry
329 * validator should consider
330 * entries invalid if they are
331 * missing any superior classes for
332 * the included set of object
333 * classes.
334 */
335 public void setCheckMissingSuperiorObjectClasses(
336 final boolean checkMissingSuperiorObjectClasses)
337 {
338 this.checkMissingSuperiorObjectClasses = checkMissingSuperiorObjectClasses;
339 }
340
341
342
343 /**
344 * Indicates whether the entry validator should consider entries invalid if
345 * their DNs cannot be parsed.
346 *
347 * @return {@code true} if entries with malformed DNs should be considered
348 * invalid, or {@code false} if not.
349 */
350 public boolean checkMalformedDNs()
351 {
352 return checkMalformedDNs;
353 }
354
355
356
357 /**
358 * Specifies whether the entry validator should consider entries invalid if
359 * their DNs cannot be parsed.
360 *
361 * @param checkMalformedDNs Specifies whether entries with malformed DNs
362 * should be considered invalid.
363 */
364 public void setCheckMalformedDNs(final boolean checkMalformedDNs)
365 {
366 this.checkMalformedDNs = checkMalformedDNs;
367 }
368
369
370
371 /**
372 * Indicates whether the entry validator should consider entries invalid if
373 * they contain one or more attribute values in their RDN that are not present
374 * in the set of entry attributes.
375 *
376 * @return {@code true} if entries missing one or more attribute values
377 * included in their RDNs should be considered invalid, or
378 * {@code false} if not.
379 */
380 public boolean checkEntryMissingRDNValues()
381 {
382 return checkEntryMissingRDNValues;
383 }
384
385
386
387 /**
388 * Specifies whether the entry validator should consider entries invalid if
389 * they contain one or more attribute values in their RDN that are not present
390 * in the set of entry attributes.
391 *
392 * @param checkEntryMissingRDNValues Indicates whether the entry validator
393 * should consider entries invalid if they
394 * contain one or more attribute values in
395 * their RDN that are not present in the
396 * set of entry attributes.
397 */
398 public void setCheckEntryMissingRDNValues(
399 final boolean checkEntryMissingRDNValues)
400 {
401 this.checkEntryMissingRDNValues = checkEntryMissingRDNValues;
402 }
403
404
405
406 /**
407 * Indicates whether the entry validator should consider entries invalid if
408 * the attributes contained in the RDN violate the constraints of the
409 * associated name form.
410 *
411 * @return {@code true} if entries with RDNs that do not conform to the
412 * associated name form should be considered invalid, or
413 * {@code false} if not.
414 */
415 public boolean checkNameForms()
416 {
417 return checkNameForms;
418 }
419
420
421
422 /**
423 * Specifies whether the entry validator should consider entries invalid if
424 * the attributes contained in the RDN violate the constraints of the
425 * associated name form.
426 *
427 * @param checkNameForms Indicates whether the entry validator should
428 * consider entries invalid if their RDNs violate name
429 * form constraints.
430 */
431 public void setCheckNameForms(final boolean checkNameForms)
432 {
433 this.checkNameForms = checkNameForms;
434 }
435
436
437
438 /**
439 * Indicates whether the entry validator should consider entries invalid if
440 * they contain attributes which are not allowed by (or are prohibited by) the
441 * object classes and DIT content rule (if applicable) for the entry.
442 *
443 * @return {@code true} if entries should be considered invalid if they
444 * contain attributes which are not allowed, or {@code false} if not.
445 */
446 public boolean checkProhibitedAttributes()
447 {
448 return checkProhibitedAttributes;
449 }
450
451
452
453 /**
454 * Specifies whether the entry validator should consider entries invalid if
455 * they contain attributes which are not allowed by (or are prohibited by) the
456 * object classes and DIT content rule (if applicable) for the entry.
457 *
458 * @param checkProhibitedAttributes Indicates whether entries should be
459 * considered invalid if they contain
460 * attributes which are not allowed.
461 */
462 public void setCheckProhibitedAttributes(
463 final boolean checkProhibitedAttributes)
464 {
465 this.checkProhibitedAttributes = checkProhibitedAttributes;
466 }
467
468
469
470 /**
471 * Indicates whether the entry validator should consider entries invalid if
472 * they contain auxiliary object classes which are not allowed by the DIT
473 * content rule (if applicable) for the entry, or if they contain any abstract
474 * object classes which are not subclassed by any non-abstract classes
475 * included in the entry.
476 *
477 * @return {@code true} if entries should be considered invalid if they
478 * contain prohibited object classes, or {@code false} if not.
479 */
480 public boolean checkProhibitedObjectClasses()
481 {
482 return checkProhibitedObjectClasses;
483 }
484
485
486
487 /**
488 * Specifies whether the entry validator should consider entries invalid if
489 * they contain auxiliary object classes which are not allowed by the DIT
490 * content rule (if applicable) for the entry, or if they contain any abstract
491 * object classes which are not subclassed by any non-abstract classes
492 * included in the entry.
493 *
494 * @param checkProhibitedObjectClasses Indicates whether entries should be
495 * considered invalid if they contain
496 * prohibited object classes.
497 */
498 public void setCheckProhibitedObjectClasses(
499 final boolean checkProhibitedObjectClasses)
500 {
501 this.checkProhibitedObjectClasses = checkProhibitedObjectClasses;
502 }
503
504
505
506 /**
507 * Indicates whether the entry validator should consider entries invalid if
508 * they they contain attributes with more than one value which are declared as
509 * single-valued in the schema.
510 *
511 * @return {@code true} if entries should be considered invalid if they
512 * contain single-valued attributes with more than one value, or
513 * {@code false} if not.
514 */
515 public boolean checkSingleValuedAttributes()
516 {
517 return checkSingleValuedAttributes;
518 }
519
520
521
522 /**
523 * Specifies whether the entry validator should consider entries invalid if
524 * they contain attributes with more than one value which are declared as
525 * single-valued in the schema.
526 *
527 * @param checkSingleValuedAttributes Indicates whether entries should be
528 * considered invalid if they contain
529 * single-valued attributes with more
530 * than one value.
531 */
532 public void setCheckSingleValuedAttributes(
533 final boolean checkSingleValuedAttributes)
534 {
535 this.checkSingleValuedAttributes = checkSingleValuedAttributes;
536 }
537
538
539
540 /**
541 * Indicates whether the entry validator should consider entries invalid if
542 * they do not contain exactly one structural object class (i.e., either do
543 * not have any structural object class, or have more than one).
544 *
545 * @return {@code true} if entries should be considered invalid if they do
546 * not have exactly one structural object class, or {@code false} if
547 * not.
548 */
549 public boolean checkStructuralObjectClasses()
550 {
551 return checkStructuralObjectClasses;
552 }
553
554
555
556 /**
557 * Specifies whether the entry validator should consider entries invalid if
558 * they do not contain exactly one structural object class (i.e., either do
559 * not have any structural object class, or have more than one).
560 *
561 * @param checkStructuralObjectClasses Indicates whether entries should be
562 * considered invalid if they do not
563 * have exactly one structural object
564 * class.
565 */
566 public void setCheckStructuralObjectClasses(
567 final boolean checkStructuralObjectClasses)
568 {
569 this.checkStructuralObjectClasses = checkStructuralObjectClasses;
570 }
571
572
573
574 /**
575 * Indicates whether the entry validator should consider entries invalid if
576 * they contain attributes which violate the associated attribute syntax.
577 *
578 * @return {@code true} if entries should be considered invalid if they
579 * contain attribute values which violate the associated attribute
580 * syntax, or {@code false} if not.
581 */
582 public boolean checkAttributeSyntax()
583 {
584 return checkAttributeSyntax;
585 }
586
587
588
589 /**
590 * Specifies whether the entry validator should consider entries invalid if
591 * they contain attributes which violate the associated attribute syntax.
592 *
593 * @param checkAttributeSyntax Indicates whether entries should be
594 * considered invalid if they violate the
595 * associated attribute syntax.
596 */
597 public void setCheckAttributeSyntax(final boolean checkAttributeSyntax)
598 {
599 this.checkAttributeSyntax = checkAttributeSyntax;
600 }
601
602
603
604 /**
605 * Retrieves the set of attribute types for which syntax violations should be
606 * ignored. If {@link #checkAttributeSyntax()} returns {@code true}, then
607 * any attribute syntax violations will be flagged for all attributes except
608 * those attributes in this set. If {@code checkAttributeSyntax()} returns
609 * {@code false}, then all syntax violations will be ignored.
610 *
611 * @return The set of attribute types for which syntax violations should be
612 * ignored.
613 */
614 public Set<AttributeTypeDefinition> getIgnoreSyntaxViolationsAttributeTypes()
615 {
616 return ignoreSyntaxViolationTypes;
617 }
618
619
620
621 /**
622 * Specifies the set of attribute types for which syntax violations should be
623 * ignored. This method will only have any effect if
624 * {@link #checkAttributeSyntax()} returns {@code true}.
625 *
626 * @param attributeTypes The definitions for the attribute types for which
627 * to ignore syntax violations. It may be
628 * {@code null} or empty if no violations should be
629 * ignored.
630 */
631 public void setIgnoreSyntaxViolationAttributeTypes(
632 final AttributeTypeDefinition... attributeTypes)
633 {
634 if (attributeTypes == null)
635 {
636 ignoreSyntaxViolationTypes = Collections.emptySet();
637 }
638 else
639 {
640 ignoreSyntaxViolationTypes = Collections.unmodifiableSet(
641 new HashSet<AttributeTypeDefinition>(toList(attributeTypes)));
642 }
643 }
644
645
646
647 /**
648 * Specifies the names or OIDs of the attribute types for which syntax
649 * violations should be ignored. This method will only have any effect if
650 * {@link #checkAttributeSyntax()} returns {@code true}.
651 *
652 * @param attributeTypes The names or OIDs of the attribute types for which
653 * to ignore syntax violations. It may be
654 * {@code null} or empty if no violations should be
655 * ignored.
656 */
657 public void setIgnoreSyntaxViolationAttributeTypes(
658 final String... attributeTypes)
659 {
660 setIgnoreSyntaxViolationAttributeTypes(toList(attributeTypes));
661 }
662
663
664
665 /**
666 * Specifies the names or OIDs of the attribute types for which syntax
667 * violations should be ignored. This method will only have any effect if
668 * {@link #checkAttributeSyntax()} returns {@code true}.
669 *
670 * @param attributeTypes The names or OIDs of the attribute types for which
671 * to ignore syntax violations. It may be
672 * {@code null} or empty if no violations should be
673 * ignored. Any attribute types not defined in the
674 * schema will be ignored.
675 */
676 public void setIgnoreSyntaxViolationAttributeTypes(
677 final Collection<String> attributeTypes)
678 {
679 if (attributeTypes == null)
680 {
681 ignoreSyntaxViolationTypes = Collections.emptySet();
682 return;
683 }
684
685 final HashSet<AttributeTypeDefinition> atSet =
686 new HashSet<AttributeTypeDefinition>(attributeTypes.size());
687 for (final String s : attributeTypes)
688 {
689 final AttributeTypeDefinition d = schema.getAttributeType(s);
690 if (d != null)
691 {
692 atSet.add(d);
693 }
694 }
695
696 ignoreSyntaxViolationTypes = Collections.unmodifiableSet(atSet);
697 }
698
699
700
701 /**
702 * Indicates whether the entry validator should consider entries invalid if
703 * they contain attributes which are not defined in the schema.
704 *
705 * @return {@code true} if entries should be considered invalid if they
706 * contain attributes which are not defined in the schema, or
707 * {@code false} if not.
708 */
709 public boolean checkUndefinedAttributes()
710 {
711 return checkUndefinedAttributes;
712 }
713
714
715
716 /**
717 * Specifies whether the entry validator should consider entries invalid if
718 * they contain attributes which are not defined in the schema.
719 *
720 * @param checkUndefinedAttributes Indicates whether entries should be
721 * considered invalid if they contain
722 * attributes which are not defined in the
723 * schema, or {@code false} if not.
724 */
725 public void setCheckUndefinedAttributes(
726 final boolean checkUndefinedAttributes)
727 {
728 this.checkUndefinedAttributes = checkUndefinedAttributes;
729 }
730
731
732
733 /**
734 * Indicates whether the entry validator should consider entries invalid if
735 * they contain object classes which are not defined in the schema.
736 *
737 * @return {@code true} if entries should be considered invalid if they
738 * contain object classes which are not defined in the schema, or
739 * {@code false} if not.
740 */
741 public boolean checkUndefinedObjectClasses()
742 {
743 return checkUndefinedObjectClasses;
744 }
745
746
747
748 /**
749 * Specifies whether the entry validator should consider entries invalid if
750 * they contain object classes which are not defined in the schema.
751 *
752 * @param checkUndefinedObjectClasses Indicates whether entries should be
753 * considered invalid if they contain
754 * object classes which are not defined
755 * in the schema.
756 */
757 public void setCheckUndefinedObjectClasses(
758 final boolean checkUndefinedObjectClasses)
759 {
760 this.checkUndefinedObjectClasses = checkUndefinedObjectClasses;
761 }
762
763
764
765 /**
766 * Indicates whether the provided entry passes all of the enabled types of
767 * validation.
768 *
769 * @param entry The entry to be examined. It must not be
770 * {@code null}.
771 * @param invalidReasons A list to which messages may be added which provide
772 * information about why the entry is invalid. It may
773 * be {@code null} if this information is not needed.
774 *
775 * @return {@code true} if the entry conforms to all of the enabled forms of
776 * validation, or {@code false} if the entry fails at least one of
777 * the tests.
778 */
779 public boolean entryIsValid(final Entry entry,
780 final List<String> invalidReasons)
781 {
782 ensureNotNull(entry);
783
784 boolean entryValid = true;
785 entriesExamined.incrementAndGet();
786
787 // Get the parsed DN for the entry.
788 RDN rdn = null;
789 try
790 {
791 rdn = entry.getParsedDN().getRDN();
792 }
793 catch (final LDAPException le)
794 {
795 debugException(le);
796 if (checkMalformedDNs)
797 {
798 entryValid = false;
799 malformedDNs.incrementAndGet();
800 if (invalidReasons != null)
801 {
802 invalidReasons.add(ERR_ENTRY_MALFORMED_DN.get(
803 getExceptionMessage(le)));
804 }
805 }
806 }
807
808 // Get the object class descriptions for the object classes in the entry.
809 final HashSet<ObjectClassDefinition> ocSet =
810 new HashSet<ObjectClassDefinition>();
811 final boolean missingOC =
812 (! getObjectClasses(entry, ocSet, invalidReasons));
813 if (missingOC)
814 {
815 entryValid = false;
816 }
817
818 // If the entry was not missing any object classes, then get the structural
819 // class for the entry and use it to get the associated DIT content rule and
820 // name form.
821 DITContentRuleDefinition ditContentRule = null;
822 NameFormDefinition nameForm = null;
823 if (! missingOC)
824 {
825 final AtomicReference<ObjectClassDefinition> ref =
826 new AtomicReference<ObjectClassDefinition>(null);
827 entryValid &= getStructuralClass(ocSet, ref, invalidReasons);
828 final ObjectClassDefinition structuralClass = ref.get();
829 if (structuralClass != null)
830 {
831 ditContentRule = schema.getDITContentRule(structuralClass.getOID());
832 nameForm =
833 schema.getNameFormByObjectClass(structuralClass.getNameOrOID());
834 }
835 }
836
837 // If we should check for missing required attributes, then do so.
838 HashSet<AttributeTypeDefinition> requiredAttrs = null;
839 if (checkMissingAttributes || checkProhibitedAttributes)
840 {
841 requiredAttrs = getRequiredAttributes(ocSet, ditContentRule);
842 if (checkMissingAttributes)
843 {
844 entryValid &= checkForMissingAttributes(entry, rdn, requiredAttrs,
845 invalidReasons);
846 }
847 }
848
849 // Iterate through all of the attributes in the entry. Make sure that they
850 // are all defined in the schema, that they are allowed to be present in the
851 // entry, that their values conform to the associated syntax, and that any
852 // single-valued attributes have only one value.
853 HashSet<AttributeTypeDefinition> optionalAttrs = null;
854 if (checkProhibitedAttributes)
855 {
856 optionalAttrs =
857 getOptionalAttributes(ocSet, ditContentRule, requiredAttrs);
858 }
859 for (final Attribute a : entry.getAttributes())
860 {
861 entryValid &=
862 checkAttribute(a, requiredAttrs, optionalAttrs, invalidReasons);
863 }
864
865 // If there is a DIT content rule, then check to ensure that all of the
866 // auxiliary object classes are allowed.
867 if (checkProhibitedObjectClasses && (ditContentRule != null))
868 {
869 entryValid &=
870 checkAuxiliaryClasses(ocSet, ditContentRule, invalidReasons);
871 }
872
873 // Check the entry's RDN to ensure that all attributes are defined in the
874 // schema, allowed to be present, and comply with the name form.
875 if (rdn != null)
876 {
877 entryValid &= checkRDN(rdn, entry, requiredAttrs, optionalAttrs, nameForm,
878 invalidReasons);
879 }
880
881 if (! entryValid)
882 {
883 invalidEntries.incrementAndGet();
884 }
885
886 return entryValid;
887 }
888
889
890
891 /**
892 * Gets the object classes for the entry, including any that weren't
893 * explicitly included but should be because they were superior to classes
894 * that were included.
895 *
896 * @param entry The entry to examine.
897 * @param ocSet The set into which the object class definitions
898 * should be placed.
899 * @param invalidReasons A list to which messages may be added which provide
900 * information about why the entry is invalid. It may
901 * be {@code null} if this information is not needed.
902 *
903 * @return {@code true} if the entry passed all validation processing
904 * performed by this method, or {@code false} if there were any
905 * failures.
906 */
907 private boolean getObjectClasses(final Entry entry,
908 final HashSet<ObjectClassDefinition> ocSet,
909 final List<String> invalidReasons)
910 {
911 final String[] ocValues = entry.getObjectClassValues();
912 if ((ocValues == null) || (ocValues.length == 0))
913 {
914 noObjectClasses.incrementAndGet();
915 if (invalidReasons != null)
916 {
917 invalidReasons.add(ERR_ENTRY_NO_OCS.get());
918 }
919 return false;
920 }
921
922 boolean entryValid = true;
923 final HashSet<String> missingOCs = new HashSet<String>(ocValues.length);
924 for (final String ocName : entry.getObjectClassValues())
925 {
926 final ObjectClassDefinition d = schema.getObjectClass(ocName);
927 if (d == null)
928 {
929 if (checkUndefinedObjectClasses)
930 {
931 entryValid = false;
932 missingOCs.add(toLowerCase(ocName));
933 updateCount(ocName, undefinedObjectClasses);
934 if (invalidReasons != null)
935 {
936 invalidReasons.add(ERR_ENTRY_UNDEFINED_OC.get(ocName));
937 }
938 }
939 }
940 else
941 {
942 ocSet.add(d);
943 }
944 }
945
946 for (final ObjectClassDefinition d :
947 new HashSet<ObjectClassDefinition>(ocSet))
948 {
949 entryValid &= addSuperiorClasses(d, ocSet, missingOCs, invalidReasons);
950 }
951
952 return entryValid;
953 }
954
955
956
957 /**
958 * Recursively adds the definition superior class for the provided object
959 * class definition to the provided set, if it is not already present.
960 *
961 * @param d The object class definition to process.
962 * @param ocSet The set into which the object class definitions
963 * should be placed.
964 * @param missingOCNames The names of the object classes we already know are
965 * missing and therefore shouldn't be flagged again.
966 * @param invalidReasons A list to which messages may be added which provide
967 * information about why the entry is invalid. It may
968 * be {@code null} if this information is not needed.
969 *
970 * @return {@code true} if the entry passed all validation processing
971 * performed by this method, or {@code false} if there were any
972 * failures.
973 */
974 private boolean addSuperiorClasses(final ObjectClassDefinition d,
975 final HashSet<ObjectClassDefinition> ocSet,
976 final HashSet<String> missingOCNames,
977 final List<String> invalidReasons)
978 {
979 boolean entryValid = true;
980
981 for (final String ocName : d.getSuperiorClasses())
982 {
983 final ObjectClassDefinition supOC = schema.getObjectClass(ocName);
984 if (supOC == null)
985 {
986 if (checkUndefinedObjectClasses)
987 {
988 entryValid = false;
989 final String lowerName = toLowerCase(ocName);
990 if (! missingOCNames.contains(lowerName))
991 {
992 missingOCNames.add(lowerName);
993 updateCount(ocName, undefinedObjectClasses);
994 if (invalidReasons != null)
995 {
996 invalidReasons.add(ERR_ENTRY_UNDEFINED_SUP_OC.get(
997 d.getNameOrOID(), ocName));
998 }
999 }
1000 }
1001 }
1002 else
1003 {
1004 if (! ocSet.contains(supOC))
1005 {
1006 ocSet.add(supOC);
1007 if (checkMissingSuperiorObjectClasses)
1008 {
1009 entryValid = false;
1010 missingSuperiorClasses.incrementAndGet();
1011 if (invalidReasons != null)
1012 {
1013 invalidReasons.add(ERR_ENTRY_MISSING_SUP_OC.get(
1014 supOC.getNameOrOID(), d.getNameOrOID()));
1015 }
1016 }
1017 }
1018
1019 entryValid &=
1020 addSuperiorClasses(supOC, ocSet, missingOCNames, invalidReasons);
1021 }
1022 }
1023
1024 return entryValid;
1025 }
1026
1027
1028
1029 /**
1030 * Retrieves the structural object class from the set of provided object
1031 * classes.
1032 *
1033 * @param ocSet The set of object class definitions for the entry.
1034 * @param structuralClass The reference that will be updated with the
1035 * entry's structural object class.
1036 * @param invalidReasons A list to which messages may be added which
1037 * provide provide information about why the entry is
1038 * invalid. It may be {@code null} if this
1039 * information is not needed.
1040 *
1041 * @return {@code true} if the entry passes all validation checks performed
1042 * by this method, or {@code false} if not.
1043 */
1044 private boolean getStructuralClass(final HashSet<ObjectClassDefinition> ocSet,
1045 final AtomicReference<ObjectClassDefinition> structuralClass,
1046 final List<String> invalidReasons)
1047 {
1048 final HashSet<ObjectClassDefinition> ocCopy =
1049 new HashSet<ObjectClassDefinition>(ocSet);
1050 for (final ObjectClassDefinition d : ocSet)
1051 {
1052 final ObjectClassType t = d.getObjectClassType(schema);
1053 if (t == ObjectClassType.STRUCTURAL)
1054 {
1055 ocCopy.removeAll(d.getSuperiorClasses(schema, true));
1056 }
1057 else if (t == ObjectClassType.AUXILIARY)
1058 {
1059 ocCopy.remove(d);
1060 ocCopy.removeAll(d.getSuperiorClasses(schema, true));
1061 }
1062 }
1063
1064 // Iterate through the set of remaining classes and strip out any
1065 // abstract classes.
1066 boolean entryValid = true;
1067 Iterator<ObjectClassDefinition> iterator = ocCopy.iterator();
1068 while (iterator.hasNext())
1069 {
1070 final ObjectClassDefinition d = iterator.next();
1071 if (d.getObjectClassType(schema) == ObjectClassType.ABSTRACT)
1072 {
1073 if (checkProhibitedObjectClasses)
1074 {
1075 entryValid = false;
1076 updateCount(d.getNameOrOID(), prohibitedObjectClasses);
1077 if (invalidReasons != null)
1078 {
1079 invalidReasons.add(ERR_ENTRY_INVALID_ABSTRACT_CLASS.get(
1080 d.getNameOrOID()));
1081 }
1082 }
1083 iterator.remove();
1084 }
1085 }
1086
1087 switch (ocCopy.size())
1088 {
1089 case 0:
1090 if (checkStructuralObjectClasses)
1091 {
1092 entryValid = false;
1093 noStructuralClass.incrementAndGet();
1094 if (invalidReasons != null)
1095 {
1096 invalidReasons.add(ERR_ENTRY_NO_STRUCTURAL_CLASS.get());
1097 }
1098 }
1099 break;
1100
1101 case 1:
1102 structuralClass.set(ocCopy.iterator().next());
1103 break;
1104
1105 default:
1106 if (checkStructuralObjectClasses)
1107 {
1108 entryValid = false;
1109 multipleStructuralClasses.incrementAndGet();
1110 if (invalidReasons != null)
1111 {
1112 final StringBuilder ocList = new StringBuilder();
1113 iterator = ocCopy.iterator();
1114 while (iterator.hasNext())
1115 {
1116 ocList.append(iterator.next().getNameOrOID());
1117 if (iterator.hasNext())
1118 {
1119 ocList.append(", ");
1120 }
1121 }
1122 invalidReasons.add(
1123 ERR_ENTRY_MULTIPLE_STRUCTURAL_CLASSES.get(ocList));
1124 }
1125 }
1126 break;
1127 }
1128
1129 return entryValid;
1130 }
1131
1132
1133
1134 /**
1135 * Retrieves the set of attributes which must be present in entries with the
1136 * provided set of object classes and DIT content rule.
1137 *
1138 * @param ocSet The set of object classes for the entry.
1139 * @param ditContentRule The DIT content rule for the entry, if defined.
1140 *
1141 * @return The set of attributes which must be present in entries with the
1142 * provided set of object classes and DIT content rule.
1143 */
1144 private HashSet<AttributeTypeDefinition> getRequiredAttributes(
1145 final HashSet<ObjectClassDefinition> ocSet,
1146 final DITContentRuleDefinition ditContentRule)
1147 {
1148 final HashSet<AttributeTypeDefinition> attrSet =
1149 new HashSet<AttributeTypeDefinition>();
1150 for (final ObjectClassDefinition oc : ocSet)
1151 {
1152 attrSet.addAll(oc.getRequiredAttributes(schema, false));
1153 }
1154
1155 if (ditContentRule != null)
1156 {
1157 for (final String s : ditContentRule.getRequiredAttributes())
1158 {
1159 final AttributeTypeDefinition d = schema.getAttributeType(s);
1160 if (d != null)
1161 {
1162 attrSet.add(d);
1163 }
1164 }
1165 }
1166
1167 return attrSet;
1168 }
1169
1170
1171
1172 /**
1173 * Retrieves the set of attributes which may optionally be present in entries
1174 * with the provided set of object classes and DIT content rule.
1175 *
1176 * @param ocSet The set of object classes for the entry.
1177 * @param ditContentRule The DIT content rule for the entry, if defined.
1178 * @param requiredAttrSet The set of required attributes for the entry.
1179 *
1180 * @return The set of attributes which may optionally be present in entries
1181 * with the provided set of object classes and DIT content rule.
1182 */
1183 private HashSet<AttributeTypeDefinition> getOptionalAttributes(
1184 final HashSet<ObjectClassDefinition> ocSet,
1185 final DITContentRuleDefinition ditContentRule,
1186 final HashSet<AttributeTypeDefinition> requiredAttrSet)
1187 {
1188 final HashSet<AttributeTypeDefinition> attrSet =
1189 new HashSet<AttributeTypeDefinition>();
1190 for (final ObjectClassDefinition oc : ocSet)
1191 {
1192 if (oc.hasNameOrOID("extensibleObject") ||
1193 oc.hasNameOrOID("1.3.6.1.4.1.1466.101.120.111"))
1194 {
1195 attrSet.addAll(schema.getUserAttributeTypes());
1196 break;
1197 }
1198
1199 for (final AttributeTypeDefinition d :
1200 oc.getOptionalAttributes(schema, false))
1201 {
1202 if (! requiredAttrSet.contains(d))
1203 {
1204 attrSet.add(d);
1205 }
1206 }
1207 }
1208
1209 if (ditContentRule != null)
1210 {
1211 for (final String s : ditContentRule.getOptionalAttributes())
1212 {
1213 final AttributeTypeDefinition d = schema.getAttributeType(s);
1214 if ((d != null) && (! requiredAttrSet.contains(d)))
1215 {
1216 attrSet.add(d);
1217 }
1218 }
1219
1220 for (final String s : ditContentRule.getProhibitedAttributes())
1221 {
1222 final AttributeTypeDefinition d = schema.getAttributeType(s);
1223 if (d != null)
1224 {
1225 attrSet.remove(d);
1226 }
1227 }
1228 }
1229
1230 return attrSet;
1231 }
1232
1233
1234
1235 /**
1236 * Checks the provided entry to determine whether it is missing any required
1237 * attributes.
1238 *
1239 * @param entry The entry to examine.
1240 * @param rdn The RDN for the entry, if available.
1241 * @param requiredAttrs The set of attribute types which are required to be
1242 * included in the entry.
1243 * @param invalidReasons A list to which messages may be added which provide
1244 * information about why the entry is invalid. It may
1245 * be {@code null} if this information is not needed.
1246 *
1247 * @return {@code true} if the entry has all required attributes, or
1248 * {@code false} if not.
1249 */
1250 private boolean checkForMissingAttributes(final Entry entry, final RDN rdn,
1251 final HashSet<AttributeTypeDefinition> requiredAttrs,
1252 final List<String> invalidReasons)
1253 {
1254 boolean entryValid = true;
1255
1256 for (final AttributeTypeDefinition d : requiredAttrs)
1257 {
1258 boolean found = false;
1259 for (final String s : d.getNames())
1260 {
1261 if (entry.hasAttribute(s) || ((rdn != null) && rdn.hasAttribute(s)))
1262 {
1263 found = true;
1264 break;
1265 }
1266 }
1267
1268 if (! found)
1269 {
1270 if (! (entry.hasAttribute(d.getOID()) ||
1271 ((rdn != null) && (rdn.hasAttribute(d.getOID())))))
1272 {
1273 entryValid = false;
1274 updateCount(d.getNameOrOID(), missingAttributes);
1275 if (invalidReasons != null)
1276 {
1277 invalidReasons.add(ERR_ENTRY_MISSING_REQUIRED_ATTR.get(
1278 d.getNameOrOID()));
1279 }
1280 }
1281 }
1282 }
1283
1284 return entryValid;
1285 }
1286
1287
1288
1289 /**
1290 * Checks the provided attribute to determine whether it appears to be valid.
1291 *
1292 * @param attr The attribute to examine.
1293 * @param requiredAttrs The set of attribute types which are required to be
1294 * included in the entry.
1295 * @param optionalAttrs The set of attribute types which may optionally be
1296 * included in the entry.
1297 * @param invalidReasons A list to which messages may be added which provide
1298 * information about why the entry is invalid. It may
1299 * be {@code null} if this information is not needed.
1300 *
1301 * @return {@code true} if the attribute passed all of the checks and appears
1302 * to be valid, or {@code false} if it failed any of the checks.
1303 */
1304 private boolean checkAttribute(final Attribute attr,
1305 final HashSet<AttributeTypeDefinition> requiredAttrs,
1306 final HashSet<AttributeTypeDefinition> optionalAttrs,
1307 final List<String> invalidReasons)
1308 {
1309 boolean entryValid = true;
1310
1311 final AttributeTypeDefinition d =
1312 schema.getAttributeType(attr.getBaseName());
1313 if (d == null)
1314 {
1315 if (checkUndefinedAttributes)
1316 {
1317 entryValid = false;
1318 updateCount(attr.getBaseName(), undefinedAttributes);
1319 if (invalidReasons != null)
1320 {
1321 invalidReasons.add(ERR_ENTRY_UNDEFINED_ATTR.get(attr.getBaseName()));
1322 }
1323 }
1324
1325 return entryValid;
1326 }
1327
1328 if (checkProhibitedAttributes && (! d.isOperational()))
1329 {
1330 if (! (requiredAttrs.contains(d) || optionalAttrs.contains(d)))
1331 {
1332 entryValid = false;
1333 updateCount(d.getNameOrOID(), prohibitedAttributes);
1334 if (invalidReasons != null)
1335 {
1336 invalidReasons.add(ERR_ENTRY_ATTR_NOT_ALLOWED.get(d.getNameOrOID()));
1337 }
1338 }
1339 }
1340
1341 final ASN1OctetString[] rawValues = attr.getRawValues();
1342 if (checkSingleValuedAttributes && d.isSingleValued() &&
1343 (rawValues.length > 1))
1344 {
1345 entryValid = false;
1346 updateCount(d.getNameOrOID(), singleValueViolations);
1347 if (invalidReasons != null)
1348 {
1349 invalidReasons.add(
1350 ERR_ENTRY_ATTR_HAS_MULTIPLE_VALUES.get(d.getNameOrOID()));
1351 }
1352 }
1353
1354 if (checkAttributeSyntax)
1355 {
1356 if (! ignoreSyntaxViolationTypes.contains(d))
1357 {
1358 final MatchingRule r =
1359 MatchingRule.selectEqualityMatchingRule(d.getNameOrOID(), schema);
1360 final Map<String, String[]> extensions = d.getExtensions();
1361 for (final ASN1OctetString v : rawValues)
1362 {
1363 try
1364 {
1365 r.normalize(v);
1366 }
1367 catch (final LDAPException le)
1368 {
1369 debugException(le);
1370 entryValid = false;
1371 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1372 if (invalidReasons != null)
1373 {
1374 invalidReasons.add(ERR_ENTRY_ATTR_INVALID_SYNTAX.get(
1375 v.stringValue(), d.getNameOrOID(), getExceptionMessage(le)));
1376 }
1377 }
1378
1379
1380 // If the attribute type definition includes an X-ALLOWED-VALUE
1381 // extension, then make sure the value is in that set.
1382 final String[] allowedValues = extensions.get("X-ALLOWED-VALUE");
1383 if (allowedValues != null)
1384 {
1385 boolean isAllowed = false;
1386 for (final String allowedValue : allowedValues)
1387 {
1388 try
1389 {
1390 if (r.valuesMatch(v, new ASN1OctetString(allowedValue)))
1391 {
1392 isAllowed = true;
1393 break;
1394 }
1395 }
1396 catch (final Exception e)
1397 {
1398 debugException(e);
1399 }
1400 }
1401
1402 if (! isAllowed)
1403 {
1404 entryValid = false;
1405 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1406 if (invalidReasons != null)
1407 {
1408 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_ALLOWED.get(
1409 v.stringValue(), d.getNameOrOID()));
1410 }
1411 }
1412 }
1413
1414
1415 // If the attribute type definition includes an X-VALUE-REGEX
1416 // extension, then make sure the value matches one of those regexes.
1417 final String[] valueRegexes = extensions.get("X-VALUE-REGEX");
1418 if (valueRegexes != null)
1419 {
1420 boolean matchesRegex = false;
1421 for (final String regex : valueRegexes)
1422 {
1423 try
1424 {
1425 final Pattern pattern = Pattern.compile(regex);
1426 if (pattern.matcher(v.stringValue()).matches())
1427 {
1428 matchesRegex = true;
1429 break;
1430 }
1431 }
1432 catch (final Exception e)
1433 {
1434 debugException(e);
1435 }
1436 }
1437
1438 if (! matchesRegex)
1439 {
1440 entryValid = false;
1441 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1442 if (invalidReasons != null)
1443 {
1444 invalidReasons.add(
1445 ERR_ENTRY_ATTR_VALUE_NOT_ALLOWED_BY_REGEX.get(
1446 v.stringValue(), d.getNameOrOID()));
1447 }
1448 }
1449 }
1450
1451
1452 // If the attribute type definition includes an X-MIN-VALUE-LENGTH
1453 // extension, then make sure the value is long enough.
1454 final String[] minValueLengths = extensions.get("X-MIN-VALUE-LENGTH");
1455 if (minValueLengths != null)
1456 {
1457 int minLength = 0;
1458 for (final String s : minValueLengths)
1459 {
1460 try
1461 {
1462 minLength = Math.max(minLength, Integer.parseInt(s));
1463 }
1464 catch (final Exception e)
1465 {
1466 debugException(e);
1467 }
1468 }
1469
1470 if (v.stringValue().length() < minLength)
1471 {
1472 entryValid = false;
1473 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1474 if (invalidReasons != null)
1475 {
1476 invalidReasons.add(
1477 ERR_ENTRY_ATTR_VALUE_SHORTER_THAN_MIN_LENGTH.get(
1478 v.stringValue(), d.getNameOrOID(), minLength));
1479 }
1480 }
1481 }
1482
1483
1484 // If the attribute type definition includes an X-MAX-VALUE-LENGTH
1485 // extension, then make sure the value is short enough.
1486 final String[] maxValueLengths = extensions.get("X-MAX-VALUE-LENGTH");
1487 if (maxValueLengths != null)
1488 {
1489 int maxLength = Integer.MAX_VALUE;
1490 for (final String s : maxValueLengths)
1491 {
1492 try
1493 {
1494 maxLength = Math.min(maxLength, Integer.parseInt(s));
1495 }
1496 catch (final Exception e)
1497 {
1498 debugException(e);
1499 }
1500 }
1501
1502 if (v.stringValue().length() > maxLength)
1503 {
1504 entryValid = false;
1505 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1506 if (invalidReasons != null)
1507 {
1508 invalidReasons.add(
1509 ERR_ENTRY_ATTR_VALUE_LONGER_THAN_MAX_LENGTH.get(
1510 v.stringValue(), d.getNameOrOID(), maxLength));
1511 }
1512 }
1513 }
1514
1515
1516 // If the attribute type definition includes an X-MIN-INT-VALUE
1517 // extension, then make sure the value is large enough.
1518 final String[] minIntValues = extensions.get("X-MIN-INT-VALUE");
1519 if (minIntValues != null)
1520 {
1521 try
1522 {
1523 final long longValue = Long.parseLong(v.stringValue());
1524
1525 long minAllowedValue = 0L;
1526 for (final String s : minIntValues)
1527 {
1528 try
1529 {
1530 minAllowedValue =
1531 Math.max(minAllowedValue, Long.parseLong(s));
1532 }
1533 catch (final Exception e)
1534 {
1535 debugException(e);
1536 }
1537 }
1538
1539 if (longValue < minAllowedValue)
1540 {
1541 entryValid = false;
1542 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1543 if (invalidReasons != null)
1544 {
1545 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_INT_TOO_SMALL.get(
1546 longValue, d.getNameOrOID(), minAllowedValue));
1547 }
1548 }
1549 }
1550 catch (final Exception e)
1551 {
1552 debugException(e);
1553 entryValid = false;
1554 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1555 if (invalidReasons != null)
1556 {
1557 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_INT.get(
1558 v.stringValue(), d.getNameOrOID(), "X-MIN-INT-VALUE"));
1559 }
1560 }
1561 }
1562
1563
1564 // If the attribute type definition includes an X-MAX-INT-VALUE
1565 // extension, then make sure the value is large enough.
1566 final String[] maxIntValues = extensions.get("X-MAX-INT-VALUE");
1567 if (maxIntValues != null)
1568 {
1569 try
1570 {
1571 final long longValue = Long.parseLong(v.stringValue());
1572
1573 long maxAllowedValue = Long.MAX_VALUE;
1574 for (final String s : maxIntValues)
1575 {
1576 try
1577 {
1578 maxAllowedValue =
1579 Math.min(maxAllowedValue, Long.parseLong(s));
1580 }
1581 catch (final Exception e)
1582 {
1583 debugException(e);
1584 }
1585 }
1586
1587 if (longValue > maxAllowedValue)
1588 {
1589 entryValid = false;
1590 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1591 if (invalidReasons != null)
1592 {
1593 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_INT_TOO_LARGE.get(
1594 longValue, d.getNameOrOID(), maxAllowedValue));
1595 }
1596 }
1597 }
1598 catch (final Exception e)
1599 {
1600 debugException(e);
1601 entryValid = false;
1602 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1603 if (invalidReasons != null)
1604 {
1605 invalidReasons.add(ERR_ENTRY_ATTR_VALUE_NOT_INT.get(
1606 v.stringValue(), d.getNameOrOID(), "X-MAX-INT-VALUE"));
1607 }
1608 }
1609 }
1610 }
1611
1612
1613 // If the attribute type definition includes an X-MIN-VALUE-COUNT
1614 // extension, then make sure the value has enough values.
1615 final String[] minValueCounts = extensions.get("X-MIN-VALUE-COUNT");
1616 if (minValueCounts != null)
1617 {
1618 int minValueCount = 0;
1619 for (final String s : minValueCounts)
1620 {
1621 try
1622 {
1623 minValueCount = Math.max(minValueCount, Integer.parseInt(s));
1624 }
1625 catch (final Exception e)
1626 {
1627 debugException(e);
1628 }
1629 }
1630
1631 if (rawValues.length < minValueCount)
1632 {
1633 entryValid = false;
1634 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1635 if (invalidReasons != null)
1636 {
1637 invalidReasons.add(ERR_ENTRY_TOO_FEW_VALUES.get(rawValues.length,
1638 d.getNameOrOID(), minValueCount));
1639 }
1640 }
1641 }
1642
1643
1644 // If the attribute type definition includes an X-MAX-VALUE-COUNT
1645 // extension, then make sure the value has enough values.
1646 final String[] maxValueCounts = extensions.get("X-MAX-VALUE-COUNT");
1647 if (maxValueCounts != null)
1648 {
1649 int maxValueCount = Integer.MAX_VALUE;
1650 for (final String s : maxValueCounts)
1651 {
1652 try
1653 {
1654 maxValueCount = Math.min(maxValueCount, Integer.parseInt(s));
1655 }
1656 catch (final Exception e)
1657 {
1658 debugException(e);
1659 }
1660 }
1661
1662 if (rawValues.length > maxValueCount)
1663 {
1664 entryValid = false;
1665 updateCount(d.getNameOrOID(), attributesViolatingSyntax);
1666 if (invalidReasons != null)
1667 {
1668 invalidReasons.add(ERR_ENTRY_TOO_MANY_VALUES.get(rawValues.length,
1669 d.getNameOrOID(), maxValueCount));
1670 }
1671 }
1672 }
1673 }
1674 }
1675
1676 return entryValid;
1677 }
1678
1679
1680
1681 /**
1682 * Ensures that all of the auxiliary object classes contained in the object
1683 * class set are allowed by the provided DIT content rule.
1684 *
1685 * @param ocSet The set of object classes contained in the entry.
1686 * @param ditContentRule The DIT content rule to use to make the
1687 * determination.
1688 * @param invalidReasons A list to which messages may be added which provide
1689 * information about why the entry is invalid. It may
1690 * be {@code null} if this information is not needed.
1691 *
1692 * @return {@code true} if the entry passes all checks performed by this
1693 * method, or {@code false} if not.
1694 */
1695 private boolean checkAuxiliaryClasses(
1696 final HashSet<ObjectClassDefinition> ocSet,
1697 final DITContentRuleDefinition ditContentRule,
1698 final List<String> invalidReasons)
1699 {
1700 final HashSet<ObjectClassDefinition> auxSet =
1701 new HashSet<ObjectClassDefinition>();
1702 for (final String s : ditContentRule.getAuxiliaryClasses())
1703 {
1704 final ObjectClassDefinition d = schema.getObjectClass(s);
1705 if (d != null)
1706 {
1707 auxSet.add(d);
1708 }
1709 }
1710
1711 boolean entryValid = true;
1712 for (final ObjectClassDefinition d : ocSet)
1713 {
1714 final ObjectClassType t = d.getObjectClassType(schema);
1715 if ((t == ObjectClassType.AUXILIARY) && (! auxSet.contains(d)))
1716 {
1717 entryValid = false;
1718 updateCount(d.getNameOrOID(), prohibitedObjectClasses);
1719 if (invalidReasons != null)
1720 {
1721 invalidReasons.add(
1722 ERR_ENTRY_AUX_CLASS_NOT_ALLOWED.get(d.getNameOrOID()));
1723 }
1724 }
1725 }
1726
1727 return entryValid;
1728 }
1729
1730
1731
1732 /**
1733 * Ensures that the provided RDN is acceptable. It will ensure that all
1734 * attributes are defined in the schema and allowed for the entry, and that
1735 * the entry optionally conforms to the associated name form.
1736 *
1737 * @param rdn The RDN to examine.
1738 * @param entry The entry to examine.
1739 * @param requiredAttrs The set of attribute types which are required to be
1740 * included in the entry.
1741 * @param optionalAttrs The set of attribute types which may optionally be
1742 * included in the entry.
1743 * @param nameForm The name for to use to make the determination, if
1744 * defined.
1745 * @param invalidReasons A list to which messages may be added which provide
1746 * information about why the entry is invalid. It may
1747 * be {@code null} if this information is not needed.
1748 *
1749 * @return {@code true} if the entry passes all checks performed by this
1750 * method, or {@code false} if not.
1751 */
1752 private boolean checkRDN(final RDN rdn, final Entry entry,
1753 final HashSet<AttributeTypeDefinition> requiredAttrs,
1754 final HashSet<AttributeTypeDefinition> optionalAttrs,
1755 final NameFormDefinition nameForm,
1756 final List<String> invalidReasons)
1757 {
1758 final HashSet<AttributeTypeDefinition> nfReqAttrs =
1759 new HashSet<AttributeTypeDefinition>();
1760 final HashSet<AttributeTypeDefinition> nfAllowedAttrs =
1761 new HashSet<AttributeTypeDefinition>();
1762 if (nameForm != null)
1763 {
1764 for (final String s : nameForm.getRequiredAttributes())
1765 {
1766 final AttributeTypeDefinition d = schema.getAttributeType(s);
1767 if (d != null)
1768 {
1769 nfReqAttrs.add(d);
1770 }
1771 }
1772
1773 nfAllowedAttrs.addAll(nfReqAttrs);
1774 for (final String s : nameForm.getOptionalAttributes())
1775 {
1776 final AttributeTypeDefinition d = schema.getAttributeType(s);
1777 if (d != null)
1778 {
1779 nfAllowedAttrs.add(d);
1780 }
1781 }
1782 }
1783
1784 boolean entryValid = true;
1785 final String[] attributeNames = rdn.getAttributeNames();
1786 final byte[][] attributeValues = rdn.getByteArrayAttributeValues();
1787 for (int i=0; i < attributeNames.length; i++)
1788 {
1789 final String name = attributeNames[i];
1790 if (checkEntryMissingRDNValues)
1791 {
1792 final byte[] value = attributeValues[i];
1793 final MatchingRule matchingRule =
1794 MatchingRule.selectEqualityMatchingRule(name, schema);
1795 if (! entry.hasAttributeValue(name, value, matchingRule))
1796 {
1797 entryValid = false;
1798 entriesMissingRDNValues.incrementAndGet();
1799 if (invalidReasons != null)
1800 {
1801 invalidReasons.add(ERR_ENTRY_MISSING_RDN_VALUE.get(
1802 rdn.getAttributeValues()[i], name));
1803 }
1804 }
1805 }
1806
1807 final AttributeTypeDefinition d = schema.getAttributeType(name);
1808 if (d == null)
1809 {
1810 if (checkUndefinedAttributes)
1811 {
1812 entryValid = false;
1813 updateCount(name, undefinedAttributes);
1814 if (invalidReasons != null)
1815 {
1816 invalidReasons.add(ERR_ENTRY_RDN_ATTR_NOT_DEFINED.get(name));
1817 }
1818 }
1819 }
1820 else
1821 {
1822 if (checkProhibitedAttributes &&
1823 (! (requiredAttrs.contains(d) || optionalAttrs.contains(d) ||
1824 d.isOperational())))
1825 {
1826 entryValid = false;
1827 updateCount(d.getNameOrOID(), prohibitedAttributes);
1828 if (invalidReasons != null)
1829 {
1830 invalidReasons.add(ERR_ENTRY_RDN_ATTR_NOT_ALLOWED_IN_ENTRY.get(
1831 d.getNameOrOID()));
1832 }
1833 }
1834
1835 if (checkNameForms && (nameForm != null))
1836 {
1837 if (! nfReqAttrs.remove(d))
1838 {
1839 if (! nfAllowedAttrs.contains(d))
1840 {
1841 if (entryValid)
1842 {
1843 entryValid = false;
1844 nameFormViolations.incrementAndGet();
1845 }
1846 if (invalidReasons != null)
1847 {
1848 invalidReasons.add(
1849 ERR_ENTRY_RDN_ATTR_NOT_ALLOWED_BY_NF.get(name));
1850 }
1851 }
1852 }
1853 }
1854 }
1855 }
1856
1857 if (checkNameForms && (! nfReqAttrs.isEmpty()))
1858 {
1859 if (entryValid)
1860 {
1861 entryValid = false;
1862 nameFormViolations.incrementAndGet();
1863 }
1864 if (invalidReasons != null)
1865 {
1866 for (final AttributeTypeDefinition d : nfReqAttrs)
1867 {
1868 invalidReasons.add(ERR_ENTRY_RDN_MISSING_REQUIRED_ATTR.get(
1869 d.getNameOrOID()));
1870 }
1871 }
1872 }
1873
1874 return entryValid;
1875 }
1876
1877
1878
1879 /**
1880 * Updates the count for the given key in the provided map, adding a new key
1881 * with a count of one if necessary.
1882 *
1883 * @param key The key for which the count is to be updated.
1884 * @param map The map in which the update is to be made.
1885 */
1886 private static void updateCount(final String key,
1887 final ConcurrentHashMap<String,AtomicLong> map)
1888 {
1889 final String lowerKey = toLowerCase(key);
1890 AtomicLong l = map.get(lowerKey);
1891 if (l == null)
1892 {
1893 l = map.putIfAbsent(lowerKey, new AtomicLong(1L));
1894 if (l == null)
1895 {
1896 return;
1897 }
1898 }
1899
1900 l.incrementAndGet();
1901 }
1902
1903
1904
1905 /**
1906 * Resets all counts maintained by this entry validator.
1907 */
1908 public void resetCounts()
1909 {
1910 entriesExamined.set(0L);
1911 entriesMissingRDNValues.set(0L);
1912 invalidEntries.set(0L);
1913 malformedDNs.set(0L);
1914 missingSuperiorClasses.set(0L);
1915 multipleStructuralClasses.set(0L);
1916 nameFormViolations.set(0L);
1917 noObjectClasses.set(0L);
1918 noStructuralClass.set(0L);
1919
1920 attributesViolatingSyntax.clear();
1921 missingAttributes.clear();
1922 prohibitedAttributes.clear();
1923 prohibitedObjectClasses.clear();
1924 singleValueViolations.clear();
1925 undefinedAttributes.clear();
1926 undefinedObjectClasses.clear();
1927 }
1928
1929
1930
1931 /**
1932 * Retrieves the total number of entries examined during processing.
1933 *
1934 * @return The total number of entries examined during processing.
1935 */
1936 public long getEntriesExamined()
1937 {
1938 return entriesExamined.get();
1939 }
1940
1941
1942
1943 /**
1944 * Retrieves the total number of invalid entries encountered during
1945 * processing.
1946 *
1947 * @return The total number of invalid entries encountered during processing.
1948 */
1949 public long getInvalidEntries()
1950 {
1951 return invalidEntries.get();
1952 }
1953
1954
1955
1956 /**
1957 * Retrieves the total number of entries examined that had malformed DNs which
1958 * could not be parsed.
1959 *
1960 * @return The total number of entries examined that had malformed DNs.
1961 */
1962 public long getMalformedDNs()
1963 {
1964 return malformedDNs.get();
1965 }
1966
1967
1968
1969 /**
1970 * Retrieves the total number of entries examined that included an attribute
1971 * value in the RDN that was not present in the entry attributes.
1972 *
1973 * @return The total number of entries examined that included an attribute
1974 * value in the RDN that was not present in the entry attributes.
1975 */
1976 public long getEntriesMissingRDNValues()
1977 {
1978 return entriesMissingRDNValues.get();
1979 }
1980
1981
1982
1983 /**
1984 * Retrieves the total number of entries examined which did not contain any
1985 * object classes.
1986 *
1987 * @return The total number of entries examined which did not contain any
1988 * object classes.
1989 */
1990 public long getEntriesWithoutAnyObjectClasses()
1991 {
1992 return noObjectClasses.get();
1993 }
1994
1995
1996
1997 /**
1998 * Retrieves the total number of entries examined which did not contain any
1999 * structural object class.
2000 *
2001 * @return The total number of entries examined which did not contain any
2002 * structural object class.
2003 */
2004 public long getEntriesMissingStructuralObjectClass()
2005 {
2006 return noStructuralClass.get();
2007 }
2008
2009
2010
2011 /**
2012 * Retrieves the total number of entries examined which contained more than
2013 * one structural object class.
2014 *
2015 * @return The total number of entries examined which contained more than one
2016 * structural object class.
2017 */
2018 public long getEntriesWithMultipleStructuralObjectClasses()
2019 {
2020 return multipleStructuralClasses.get();
2021 }
2022
2023
2024
2025 /**
2026 * Retrieves the total number of entries examined which were missing one or
2027 * more superior object classes.
2028 *
2029 * @return The total number of entries examined which were missing one or
2030 * more superior object classes.
2031 */
2032 public long getEntriesWithMissingSuperiorObjectClasses()
2033 {
2034 return missingSuperiorClasses.get();
2035 }
2036
2037
2038
2039 /**
2040 * Retrieves the total number of entries examined which contained an RDN that
2041 * violated the constraints of the associated name form.
2042 *
2043 * @return The total number of entries examined which contained an RDN that
2044 * violated the constraints of the associated name form.
2045 */
2046 public long getNameFormViolations()
2047 {
2048 return nameFormViolations.get();
2049 }
2050
2051
2052
2053 /**
2054 * Retrieves the total number of undefined object classes encountered while
2055 * examining entries. Note that this number may be greater than the total
2056 * number of entries examined if entries contain multiple undefined object
2057 * classes.
2058 *
2059 * @return The total number of undefined object classes encountered while
2060 * examining entries.
2061 */
2062 public long getTotalUndefinedObjectClasses()
2063 {
2064 return getMapTotal(undefinedObjectClasses);
2065 }
2066
2067
2068
2069 /**
2070 * Retrieves the undefined object classes encountered while processing
2071 * entries, mapped from the name of the undefined object class to the number
2072 * of entries in which that object class was referenced.
2073 *
2074 * @return The undefined object classes encountered while processing entries.
2075 */
2076 public Map<String,Long> getUndefinedObjectClasses()
2077 {
2078 return convertMap(undefinedObjectClasses);
2079 }
2080
2081
2082
2083 /**
2084 * Retrieves the total number of undefined attribute types encountered while
2085 * examining entries. Note that this number may be greater than the total
2086 * number of entries examined if entries contain multiple undefined attribute
2087 * types.
2088 *
2089 * @return The total number of undefined attribute types encountered while
2090 * examining entries.
2091 */
2092 public long getTotalUndefinedAttributes()
2093 {
2094 return getMapTotal(undefinedAttributes);
2095 }
2096
2097
2098
2099 /**
2100 * Retrieves the undefined attribute types encountered while processing
2101 * entries, mapped from the name of the undefined attribute to the number
2102 * of entries in which that attribute type was referenced.
2103 *
2104 * @return The undefined attribute types encountered while processing
2105 * entries.
2106 */
2107 public Map<String,Long> getUndefinedAttributes()
2108 {
2109 return convertMap(undefinedAttributes);
2110 }
2111
2112
2113
2114 /**
2115 * Retrieves the total number of prohibited object classes encountered while
2116 * examining entries. Note that this number may be greater than the total
2117 * number of entries examined if entries contain multiple prohibited object
2118 * classes.
2119 *
2120 * @return The total number of prohibited object classes encountered while
2121 * examining entries.
2122 */
2123 public long getTotalProhibitedObjectClasses()
2124 {
2125 return getMapTotal(prohibitedObjectClasses);
2126 }
2127
2128
2129
2130 /**
2131 * Retrieves the prohibited object classes encountered while processing
2132 * entries, mapped from the name of the object class to the number of entries
2133 * in which that object class was referenced.
2134 *
2135 * @return The prohibited object classes encountered while processing
2136 * entries.
2137 */
2138 public Map<String,Long> getProhibitedObjectClasses()
2139 {
2140 return convertMap(prohibitedObjectClasses);
2141 }
2142
2143
2144
2145 /**
2146 * Retrieves the total number of prohibited attributes encountered while
2147 * examining entries. Note that this number may be greater than the total
2148 * number of entries examined if entries contain multiple prohibited
2149 * attributes.
2150 *
2151 * @return The total number of prohibited attributes encountered while
2152 * examining entries.
2153 */
2154 public long getTotalProhibitedAttributes()
2155 {
2156 return getMapTotal(prohibitedAttributes);
2157 }
2158
2159
2160
2161 /**
2162 * Retrieves the prohibited attributes encountered while processing entries,
2163 * mapped from the name of the attribute to the number of entries in which
2164 * that attribute was referenced.
2165 *
2166 * @return The prohibited attributes encountered while processing entries.
2167 */
2168 public Map<String,Long> getProhibitedAttributes()
2169 {
2170 return convertMap(prohibitedAttributes);
2171 }
2172
2173
2174
2175 /**
2176 * Retrieves the total number of missing required attributes encountered while
2177 * examining entries. Note that this number may be greater than the total
2178 * number of entries examined if entries are missing multiple attributes.
2179 *
2180 * @return The total number of missing required attributes encountered while
2181 * examining entries.
2182 */
2183 public long getTotalMissingAttributes()
2184 {
2185 return getMapTotal(missingAttributes);
2186 }
2187
2188
2189
2190 /**
2191 * Retrieves the missing required encountered while processing entries, mapped
2192 * from the name of the attribute to the number of entries in which that
2193 * attribute was required but not found.
2194 *
2195 * @return The prohibited attributes encountered while processing entries.
2196 */
2197 public Map<String,Long> getMissingAttributes()
2198 {
2199 return convertMap(missingAttributes);
2200 }
2201
2202
2203
2204 /**
2205 * Retrieves the total number of attribute values which violate their
2206 * associated syntax that were encountered while examining entries. Note that
2207 * this number may be greater than the total number of entries examined if
2208 * entries contain multiple malformed attribute values.
2209 *
2210 * @return The total number of attribute values which violate their
2211 * associated syntax that were encountered while examining entries.
2212 */
2213 public long getTotalAttributesViolatingSyntax()
2214 {
2215 return getMapTotal(attributesViolatingSyntax);
2216 }
2217
2218
2219
2220 /**
2221 * Retrieves the attributes with values violating their associated syntax that
2222 * were encountered while processing entries, mapped from the name of the
2223 * attribute to the number of malformed values found for that attribute.
2224 *
2225 * @return The attributes with malformed values encountered while processing
2226 * entries.
2227 */
2228 public Map<String,Long> getAttributesViolatingSyntax()
2229 {
2230 return convertMap(attributesViolatingSyntax);
2231 }
2232
2233
2234
2235 /**
2236 * Retrieves the total number of attributes defined as single-valued that
2237 * contained multiple values which were encountered while processing entries.
2238 * Note that this number may be greater than the total number of entries
2239 * examined if entries contain multiple such attributes.
2240 *
2241 * @return The total number of attribute defined as single-valued that
2242 * contained multiple values which were encountered while processing
2243 * entries.
2244 */
2245 public long getTotalSingleValueViolations()
2246 {
2247 return getMapTotal(singleValueViolations);
2248 }
2249
2250
2251
2252 /**
2253 * Retrieves the attributes defined as single-valued that contained multiple
2254 * values which were encountered while processing entries, mapped from the
2255 * name of the attribute to the number of entries in which that attribute had
2256 * multiple values.
2257 *
2258 * @return The attributes defined as single-valued that contained multiple
2259 * values which were encountered while processing entries.
2260 */
2261 public Map<String,Long> getSingleValueViolations()
2262 {
2263 return convertMap(singleValueViolations);
2264 }
2265
2266
2267
2268 /**
2269 * Retrieves the total number of occurrences for all items in the provided
2270 * map.
2271 *
2272 * @param map The map to be processed.
2273 *
2274 * @return The total number of occurrences for all items in the provided map.
2275 */
2276 private static long getMapTotal(final Map<String,AtomicLong> map)
2277 {
2278 long total = 0L;
2279
2280 for (final AtomicLong l : map.values())
2281 {
2282 total += l.longValue();
2283 }
2284
2285 return total;
2286 }
2287
2288
2289
2290 /**
2291 * Converts the provided map from strings to atomic longs to a map from
2292 * strings to longs.
2293 *
2294 * @param map The map to be processed.
2295 *
2296 * @return The new map.
2297 */
2298 private static Map<String,Long> convertMap(final Map<String,AtomicLong> map)
2299 {
2300 final TreeMap<String,Long> m = new TreeMap<String,Long>();
2301 for (final Map.Entry<String,AtomicLong> e : map.entrySet())
2302 {
2303 m.put(e.getKey(), e.getValue().longValue());
2304 }
2305
2306 return Collections.unmodifiableMap(m);
2307 }
2308
2309
2310
2311 /**
2312 * Retrieves a list of messages providing a summary of the invalid entries
2313 * processed by this class.
2314 *
2315 * @param detailedResults Indicates whether to include detailed information
2316 * about the attributes and object classes
2317 * responsible for the violations.
2318 *
2319 * @return A list of messages providing a summary of the invalid entries
2320 * processed by this class, or an empty list if all entries examined
2321 * were valid.
2322 */
2323 public List<String> getInvalidEntrySummary(final boolean detailedResults)
2324 {
2325 final long numInvalid = invalidEntries.get();
2326 if (numInvalid == 0)
2327 {
2328 return Collections.emptyList();
2329 }
2330
2331 final ArrayList<String> messages = new ArrayList<String>(5);
2332 final long numEntries = entriesExamined.get();
2333 long pct = 100 * numInvalid / numEntries;
2334 messages.add(INFO_ENTRY_INVALID_ENTRY_COUNT.get(
2335 numInvalid, numEntries, pct));
2336
2337 final long numBadDNs = malformedDNs.get();
2338 if (numBadDNs > 0)
2339 {
2340 pct = 100 * numBadDNs / numEntries;
2341 messages.add(INFO_ENTRY_MALFORMED_DN_COUNT.get(
2342 numBadDNs, numEntries, pct));
2343 }
2344
2345 final long numEntriesMissingRDNValues = entriesMissingRDNValues.get();
2346 if (numEntriesMissingRDNValues > 0)
2347 {
2348 pct = 100* numEntriesMissingRDNValues / numEntries;
2349 messages.add(INFO_ENTRY_MISSING_RDN_VALUE_COUNT.get(
2350 numEntriesMissingRDNValues, numEntries, pct));
2351 }
2352
2353 final long numNoOCs = noObjectClasses.get();
2354 if (numNoOCs > 0)
2355 {
2356 pct = 100 * numNoOCs / numEntries;
2357 messages.add(INFO_ENTRY_NO_OC_COUNT.get(numNoOCs, numEntries, pct));
2358 }
2359
2360 final long numMissingStructural = noStructuralClass.get();
2361 if (numMissingStructural > 0)
2362 {
2363 pct = 100 * numMissingStructural / numEntries;
2364 messages.add(INFO_ENTRY_NO_STRUCTURAL_OC_COUNT.get(
2365 numMissingStructural, numEntries, pct));
2366 }
2367
2368 final long numMultipleStructural = multipleStructuralClasses.get();
2369 if (numMultipleStructural > 0)
2370 {
2371 pct = 100 * numMultipleStructural / numEntries;
2372 messages.add(INFO_ENTRY_MULTIPLE_STRUCTURAL_OCS_COUNT.get(
2373 numMultipleStructural, numEntries, pct));
2374 }
2375
2376 final long numNFViolations = nameFormViolations.get();
2377 if (numNFViolations > 0)
2378 {
2379 pct = 100 * numNFViolations / numEntries;
2380 messages.add(INFO_ENTRY_NF_VIOLATION_COUNT.get(
2381 numNFViolations, numEntries, pct));
2382 }
2383
2384 final long numUndefinedOCs = getTotalUndefinedObjectClasses();
2385 if (numUndefinedOCs > 0)
2386 {
2387 messages.add(INFO_ENTRY_UNDEFINED_OC_COUNT.get(numUndefinedOCs));
2388 if (detailedResults)
2389 {
2390 for (final Map.Entry<String,AtomicLong> e :
2391 undefinedObjectClasses.entrySet())
2392 {
2393 messages.add(INFO_ENTRY_UNDEFINED_OC_NAME_COUNT.get(
2394 e.getKey(), e.getValue().longValue()));
2395 }
2396 }
2397 }
2398
2399 final long numProhibitedOCs = getTotalProhibitedObjectClasses();
2400 if (numProhibitedOCs > 0)
2401 {
2402 messages.add(INFO_ENTRY_PROHIBITED_OC_COUNT.get(numProhibitedOCs));
2403 if (detailedResults)
2404 {
2405 for (final Map.Entry<String,AtomicLong> e :
2406 prohibitedObjectClasses.entrySet())
2407 {
2408 messages.add(INFO_ENTRY_PROHIBITED_OC_NAME_COUNT.get(
2409 e.getKey(), e.getValue().longValue()));
2410 }
2411 }
2412 }
2413
2414 final long numMissingSuperior =
2415 getEntriesWithMissingSuperiorObjectClasses();
2416 if (numMissingSuperior > 0)
2417 {
2418 messages.add(
2419 INFO_ENTRY_MISSING_SUPERIOR_OC_COUNT.get(numMissingSuperior));
2420 }
2421
2422 final long numUndefinedAttrs = getTotalUndefinedAttributes();
2423 if (numUndefinedAttrs > 0)
2424 {
2425 messages.add(INFO_ENTRY_UNDEFINED_ATTR_COUNT.get(numUndefinedAttrs));
2426 if (detailedResults)
2427 {
2428 for (final Map.Entry<String,AtomicLong> e :
2429 undefinedAttributes.entrySet())
2430 {
2431 messages.add(INFO_ENTRY_UNDEFINED_ATTR_NAME_COUNT.get(
2432 e.getKey(), e.getValue().longValue()));
2433 }
2434 }
2435 }
2436
2437 final long numMissingAttrs = getTotalMissingAttributes();
2438 if (numMissingAttrs > 0)
2439 {
2440 messages.add(INFO_ENTRY_MISSING_ATTR_COUNT.get(numMissingAttrs));
2441 if (detailedResults)
2442 {
2443 for (final Map.Entry<String,AtomicLong> e :
2444 missingAttributes.entrySet())
2445 {
2446 messages.add(INFO_ENTRY_MISSING_ATTR_NAME_COUNT.get(
2447 e.getKey(), e.getValue().longValue()));
2448 }
2449 }
2450 }
2451
2452 final long numProhibitedAttrs = getTotalProhibitedAttributes();
2453 if (numProhibitedAttrs > 0)
2454 {
2455 messages.add(INFO_ENTRY_PROHIBITED_ATTR_COUNT.get(numProhibitedAttrs));
2456 if (detailedResults)
2457 {
2458 for (final Map.Entry<String,AtomicLong> e :
2459 prohibitedAttributes.entrySet())
2460 {
2461 messages.add(INFO_ENTRY_PROHIBITED_ATTR_NAME_COUNT.get(
2462 e.getKey(), e.getValue().longValue()));
2463 }
2464 }
2465 }
2466
2467 final long numSingleValuedViolations = getTotalSingleValueViolations();
2468 if (numSingleValuedViolations > 0)
2469 {
2470 messages.add(INFO_ENTRY_SINGLE_VALUE_VIOLATION_COUNT.get(
2471 numSingleValuedViolations));
2472 if (detailedResults)
2473 {
2474 for (final Map.Entry<String,AtomicLong> e :
2475 singleValueViolations.entrySet())
2476 {
2477 messages.add(INFO_ENTRY_SINGLE_VALUE_VIOLATION_NAME_COUNT.get(
2478 e.getKey(), e.getValue().longValue()));
2479 }
2480 }
2481 }
2482
2483 final long numSyntaxViolations = getTotalAttributesViolatingSyntax();
2484 if (numSyntaxViolations > 0)
2485 {
2486 messages.add(INFO_ENTRY_SYNTAX_VIOLATION_COUNT.get(numSyntaxViolations));
2487 if (detailedResults)
2488 {
2489 for (final Map.Entry<String,AtomicLong> e :
2490 attributesViolatingSyntax.entrySet())
2491 {
2492 messages.add(INFO_ENTRY_SYNTAX_VIOLATION_NAME_COUNT.get(
2493 e.getKey(), e.getValue().longValue()));
2494 }
2495 }
2496 }
2497
2498 return Collections.unmodifiableList(messages);
2499 }
2500 }