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