001/* 002 * Copyright 2008-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 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 */ 021package com.unboundid.ldap.sdk.schema; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.HashSet; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033import java.util.Set; 034import java.util.TreeMap; 035import java.util.concurrent.ConcurrentHashMap; 036import java.util.concurrent.atomic.AtomicLong; 037import java.util.concurrent.atomic.AtomicReference; 038import java.util.regex.Pattern; 039 040import com.unboundid.asn1.ASN1OctetString; 041import com.unboundid.ldap.matchingrules.MatchingRule; 042import com.unboundid.ldap.sdk.Attribute; 043import com.unboundid.ldap.sdk.Entry; 044import com.unboundid.ldap.sdk.LDAPException; 045import com.unboundid.ldap.sdk.RDN; 046import com.unboundid.util.ThreadSafety; 047import com.unboundid.util.ThreadSafetyLevel; 048 049import static com.unboundid.ldap.sdk.schema.SchemaMessages.*; 050import static com.unboundid.util.Debug.*; 051import static com.unboundid.util.StaticUtils.*; 052import 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) 104public 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}