001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.schema.registries; 021 022 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.directory.api.i18n.I18n; 032import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants; 033import org.apache.directory.api.ldap.model.exception.LdapException; 034import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException; 035import org.apache.directory.api.ldap.model.exception.LdapSchemaException; 036import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes; 037import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException; 038import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException; 039import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 040import org.apache.directory.api.ldap.model.schema.AttributeType; 041import org.apache.directory.api.ldap.model.schema.DitContentRule; 042import org.apache.directory.api.ldap.model.schema.DitStructureRule; 043import org.apache.directory.api.ldap.model.schema.LdapComparator; 044import org.apache.directory.api.ldap.model.schema.LdapSyntax; 045import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject; 046import org.apache.directory.api.ldap.model.schema.MatchingRule; 047import org.apache.directory.api.ldap.model.schema.MatchingRuleUse; 048import org.apache.directory.api.ldap.model.schema.MutableAttributeType; 049import org.apache.directory.api.ldap.model.schema.MutableMatchingRule; 050import org.apache.directory.api.ldap.model.schema.NameForm; 051import org.apache.directory.api.ldap.model.schema.Normalizer; 052import org.apache.directory.api.ldap.model.schema.ObjectClass; 053import org.apache.directory.api.ldap.model.schema.SchemaObject; 054import org.apache.directory.api.ldap.model.schema.SchemaObjectWrapper; 055import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 056import org.apache.directory.api.ldap.model.schema.registries.helper.AttributeTypeHelper; 057import org.apache.directory.api.ldap.model.schema.registries.helper.DitContentRuleHelper; 058import org.apache.directory.api.ldap.model.schema.registries.helper.DitStructureRuleHelper; 059import org.apache.directory.api.ldap.model.schema.registries.helper.LdapSyntaxHelper; 060import org.apache.directory.api.ldap.model.schema.registries.helper.MatchingRuleHelper; 061import org.apache.directory.api.ldap.model.schema.registries.helper.MatchingRuleUseHelper; 062import org.apache.directory.api.ldap.model.schema.registries.helper.NameFormHelper; 063import org.apache.directory.api.ldap.model.schema.registries.helper.ObjectClassHelper; 064import org.apache.directory.api.util.Strings; 065import org.slf4j.Logger; 066import org.slf4j.LoggerFactory; 067 068 069/** 070 * Document this class. 071 * 072 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 073 */ 074public class Registries implements SchemaLoaderListener, Cloneable 075{ 076 /** A logger for this class */ 077 private static final Logger LOG = LoggerFactory.getLogger( Registries.class ); 078 079 /** 080 * A String name to Schema object map for the schemas loaded into this 081 * registry. The loaded schemas may be disabled. 082 */ 083 protected Map<String, Schema> loadedSchemas = new HashMap<>(); 084 085 /** The AttributeType registry */ 086 protected DefaultAttributeTypeRegistry attributeTypeRegistry; 087 088 /** The ObjectClass registry */ 089 protected DefaultObjectClassRegistry objectClassRegistry; 090 091 /** The LdapSyntax registry */ 092 protected DefaultComparatorRegistry comparatorRegistry; 093 094 /** The DitContentRule registry */ 095 protected DefaultDitContentRuleRegistry ditContentRuleRegistry; 096 097 /** The DitStructureRule registry */ 098 protected DefaultDitStructureRuleRegistry ditStructureRuleRegistry; 099 100 /** The MatchingRule registry */ 101 protected DefaultMatchingRuleRegistry matchingRuleRegistry; 102 103 /** The MatchingRuleUse registry */ 104 protected DefaultMatchingRuleUseRegistry matchingRuleUseRegistry; 105 106 /** The NameForm registry */ 107 protected DefaultNameFormRegistry nameFormRegistry; 108 109 /** The Normalizer registry */ 110 protected DefaultNormalizerRegistry normalizerRegistry; 111 112 /** The global OID registry */ 113 protected OidRegistry<SchemaObject> globalOidRegistry; 114 115 /** The SyntaxChecker registry */ 116 protected DefaultSyntaxCheckerRegistry syntaxCheckerRegistry; 117 118 /** The LdapSyntax registry */ 119 protected DefaultLdapSyntaxRegistry ldapSyntaxRegistry; 120 121 /** A map storing all the schema objects associated with a schema */ 122 private Map<String, Set<SchemaObjectWrapper>> schemaObjects; 123 124 /** A flag indicating that the Registries is relaxed or not */ 125 private boolean isRelaxed; 126 127 /** A flag indicating that disabled SchemaObject are accepted */ 128 private boolean disabledAccepted; 129 130 /** Two flags for RELAXED and STRICT modes */ 131 /** The strict mode */ 132 public static final boolean STRICT = false; 133 134 /** The relaxed mode */ 135 public static final boolean RELAXED = true; 136 137 /** 138 * A map storing a relation between a SchemaObject and all the 139 * referencing SchemaObjects. 140 */ 141 protected Map<SchemaObjectWrapper, Set<SchemaObjectWrapper>> usedBy; 142 143 /** 144 * A map storing a relation between a SchemaObject and all the 145 * SchemaObjects it uses. 146 */ 147 protected Map<SchemaObjectWrapper, Set<SchemaObjectWrapper>> using; 148 149 150 /** 151 * Creates a new instance of Registries. 152 */ 153 public Registries() 154 { 155 globalOidRegistry = new OidRegistry<>(); 156 attributeTypeRegistry = new DefaultAttributeTypeRegistry(); 157 comparatorRegistry = new DefaultComparatorRegistry(); 158 ditContentRuleRegistry = new DefaultDitContentRuleRegistry(); 159 ditStructureRuleRegistry = new DefaultDitStructureRuleRegistry(); 160 ldapSyntaxRegistry = new DefaultLdapSyntaxRegistry(); 161 matchingRuleRegistry = new DefaultMatchingRuleRegistry(); 162 matchingRuleUseRegistry = new DefaultMatchingRuleUseRegistry(); 163 nameFormRegistry = new DefaultNameFormRegistry(); 164 normalizerRegistry = new DefaultNormalizerRegistry(); 165 objectClassRegistry = new DefaultObjectClassRegistry(); 166 syntaxCheckerRegistry = new DefaultSyntaxCheckerRegistry(); 167 schemaObjects = new HashMap<>(); 168 usedBy = new HashMap<>(); 169 using = new HashMap<>(); 170 171 isRelaxed = STRICT; 172 disabledAccepted = false; 173 } 174 175 176 /** 177 * @return The AttributeType registry 178 */ 179 public AttributeTypeRegistry getAttributeTypeRegistry() 180 { 181 return attributeTypeRegistry; 182 } 183 184 185 /** 186 * @return The Comparator registry 187 */ 188 public ComparatorRegistry getComparatorRegistry() 189 { 190 return comparatorRegistry; 191 } 192 193 194 /** 195 * @return The DitContentRule registry 196 */ 197 public DitContentRuleRegistry getDitContentRuleRegistry() 198 { 199 return ditContentRuleRegistry; 200 } 201 202 203 /** 204 * @return The DitStructureRule registry 205 */ 206 public DitStructureRuleRegistry getDitStructureRuleRegistry() 207 { 208 return ditStructureRuleRegistry; 209 } 210 211 212 /** 213 * @return The MatchingRule registry 214 */ 215 public MatchingRuleRegistry getMatchingRuleRegistry() 216 { 217 return matchingRuleRegistry; 218 } 219 220 221 /** 222 * @return The MatchingRuleUse registry 223 */ 224 public MatchingRuleUseRegistry getMatchingRuleUseRegistry() 225 { 226 return matchingRuleUseRegistry; 227 } 228 229 230 /** 231 * @return The NameForm registry 232 */ 233 public NameFormRegistry getNameFormRegistry() 234 { 235 return nameFormRegistry; 236 } 237 238 239 /** 240 * @return The Normalizer registry 241 */ 242 public NormalizerRegistry getNormalizerRegistry() 243 { 244 return normalizerRegistry; 245 } 246 247 248 /** 249 * @return The ObjectClass registry 250 */ 251 public ObjectClassRegistry getObjectClassRegistry() 252 { 253 return objectClassRegistry; 254 } 255 256 257 /** 258 * @return The global Oid registry 259 */ 260 public OidRegistry<SchemaObject> getGlobalOidRegistry() 261 { 262 return globalOidRegistry; 263 } 264 265 266 /** 267 * @return The SyntaxChecker registry 268 */ 269 public SyntaxCheckerRegistry getSyntaxCheckerRegistry() 270 { 271 return syntaxCheckerRegistry; 272 } 273 274 275 /** 276 * @return The LdapSyntax registry 277 */ 278 public LdapSyntaxRegistry getLdapSyntaxRegistry() 279 { 280 return ldapSyntaxRegistry; 281 } 282 283 284 /** 285 * Get an OID from a name. As we have many possible registries, we 286 * have to look in all of them to get the one containing the OID. 287 * 288 * @param name The name we are looking at 289 * @return The associated OID 290 */ 291 public String getOid( String name ) 292 { 293 // we have many possible Registries to look at. 294 // AttributeType 295 try 296 { 297 AttributeType attributeType = attributeTypeRegistry.lookup( name ); 298 299 if ( attributeType != null ) 300 { 301 return attributeType.getOid(); 302 } 303 } 304 catch ( LdapException ne ) 305 { 306 // Fall down to the next registry 307 } 308 309 // ObjectClass 310 try 311 { 312 ObjectClass objectClass = objectClassRegistry.lookup( name ); 313 314 if ( objectClass != null ) 315 { 316 return objectClass.getOid(); 317 } 318 } 319 catch ( LdapException ne ) 320 { 321 // Fall down to the next registry 322 } 323 324 // LdapSyntax 325 try 326 { 327 LdapSyntax ldapSyntax = ldapSyntaxRegistry.lookup( name ); 328 329 if ( ldapSyntax != null ) 330 { 331 return ldapSyntax.getOid(); 332 } 333 } 334 catch ( LdapException ne ) 335 { 336 // Fall down to the next registry 337 } 338 339 // MatchingRule 340 try 341 { 342 MatchingRule matchingRule = matchingRuleRegistry.lookup( name ); 343 344 if ( matchingRule != null ) 345 { 346 return matchingRule.getOid(); 347 } 348 } 349 catch ( LdapException ne ) 350 { 351 // Fall down to the next registry 352 } 353 354 // MatchingRuleUse 355 try 356 { 357 MatchingRuleUse matchingRuleUse = matchingRuleUseRegistry.lookup( name ); 358 359 if ( matchingRuleUse != null ) 360 { 361 return matchingRuleUse.getOid(); 362 } 363 } 364 catch ( LdapException ne ) 365 { 366 // Fall down to the next registry 367 } 368 369 // NameForm 370 try 371 { 372 NameForm nameForm = nameFormRegistry.lookup( name ); 373 374 if ( nameForm != null ) 375 { 376 return nameForm.getOid(); 377 } 378 } 379 catch ( LdapException ne ) 380 { 381 // Fall down to the next registry 382 } 383 384 // DitContentRule 385 try 386 { 387 DitContentRule ditContentRule = ditContentRuleRegistry.lookup( name ); 388 389 if ( ditContentRule != null ) 390 { 391 return ditContentRule.getOid(); 392 } 393 } 394 catch ( LdapException ne ) 395 { 396 // Fall down to the next registry 397 } 398 399 // DitStructureRule 400 try 401 { 402 DitStructureRule ditStructureRule = ditStructureRuleRegistry.lookup( name ); 403 404 if ( ditStructureRule != null ) 405 { 406 return ditStructureRule.getOid(); 407 } 408 } 409 catch ( LdapException ne ) 410 { 411 // No more registries to look at... 412 } 413 414 return null; 415 } 416 417 418 /** 419 * Gets a schema that has been loaded into these Registries. 420 * 421 * @param schemaName the name of the schema to lookup 422 * @return the loaded Schema if one corresponding to the name exists 423 */ 424 public Schema getLoadedSchema( String schemaName ) 425 { 426 return loadedSchemas.get( Strings.toLowerCaseAscii( schemaName ) ); 427 } 428 429 430 /** 431 * Checks to see if a particular Schema is loaded. 432 * 433 * @param schemaName the name of the Schema to check 434 * @return true if the Schema is loaded, false otherwise 435 */ 436 public boolean isSchemaLoaded( String schemaName ) 437 { 438 return loadedSchemas.containsKey( Strings.toLowerCaseAscii( schemaName ) ); 439 } 440 441 442 // ------------------------------------------------------------------------ 443 // Code used to sanity check the resolution of entities in registries 444 // ------------------------------------------------------------------------ 445 /** 446 * Attempts to resolve the dependent schema objects of all entities that 447 * refer to other objects within the registries. Null references will be 448 * handed appropriately. 449 * The order in which the SchemaObjects must be : 450 * <ul> 451 * <li>1) Normalizers, Comparators and SyntaxCheckers (as they depend on nothing)</li> 452 * <li>2) Syntaxes (depend on SyntaxCheckers)</li> 453 * <li>3) MatchingRules (depend on Syntaxes, Normalizers and Comparators</li> 454 * <li>4) AttributeTypes (depend on MatchingRules, Syntaxes and AttributeTypes : in this case, we first handle the superior)</li> 455 * <li>5) ObjectClasses (depend on AttributeTypes and ObjectClasses)</li> 456 * </ul> 457 * <br><br> 458 * Later, when we will support them : 459 * <ul> 460 * <li>6) MatchingRuleUses (depend on matchingRules and AttributeTypes)</li> 461 * <li>7) DitContentRules (depend on ObjectClasses and AttributeTypes)</li> 462 * <li>8) NameForms (depends on ObjectClasses and AttributeTypes)</li> 463 * <li>9) DitStructureRules (depends onNameForms and DitStructureRules)</li> 464 * </ul> 465 * 466 * @return a list of exceptions encountered while resolving entities 467 */ 468 public List<Throwable> checkRefInteg() 469 { 470 ArrayList<Throwable> errors = new ArrayList<>(); 471 472 // Step 1 : 473 // We start with Normalizers, Comparators and SyntaxCheckers 474 // as they depend on nothing 475 // Check the Normalizers 476 for ( Normalizer normalizer : normalizerRegistry ) 477 { 478 resolve( normalizer, errors ); 479 } 480 481 // Check the Comparators 482 for ( LdapComparator<?> comparator : comparatorRegistry ) 483 { 484 resolve( comparator, errors ); 485 } 486 487 // Check the SyntaxCheckers 488 for ( SyntaxChecker syntaxChecker : syntaxCheckerRegistry ) 489 { 490 resolve( syntaxChecker, errors ); 491 } 492 493 // Step 2 : 494 // Check the LdapSyntaxes 495 for ( LdapSyntax ldapSyntax : ldapSyntaxRegistry ) 496 { 497 resolve( ldapSyntax, errors ); 498 } 499 500 // Step 3 : 501 // Check the matchingRules 502 for ( MatchingRule matchingRule : matchingRuleRegistry ) 503 { 504 resolve( matchingRule, errors ); 505 } 506 507 // Step 4 : 508 // Check the AttributeTypes 509 for ( AttributeType attributeType : attributeTypeRegistry ) 510 { 511 resolve( attributeType, errors ); 512 } 513 514 // Step 5 : 515 // Check the ObjectClasses 516 for ( ObjectClass objectClass : objectClassRegistry ) 517 { 518 resolve( objectClass, errors ); 519 } 520 521 // Step 6-9 aren't yet defined 522 return errors; 523 } 524 525 526 /** 527 * Add the SchemaObjectReferences. This method does nothing, it's just 528 * a catch all. The other methods will be called for each specific 529 * schemaObject 530 * 531 public void addCrossReferences( SchemaObject schemaObject ) 532 { 533 // Do nothing : it's a catch all method. 534 } 535 536 537 /** 538 * Delete the AT references (using and usedBy) : 539 * AT -> MR (for EQUALITY, ORDERING and SUBSTR) 540 * AT -> S 541 * AT -> AT 542 * 543 * @param attributeType The AttributeType to remove 544 */ 545 public void delCrossReferences( AttributeType attributeType ) 546 { 547 if ( attributeType.getEquality() != null ) 548 { 549 delReference( attributeType, attributeType.getEquality() ); 550 } 551 552 if ( attributeType.getOrdering() != null ) 553 { 554 delReference( attributeType, attributeType.getOrdering() ); 555 } 556 557 if ( attributeType.getSubstring() != null ) 558 { 559 delReference( attributeType, attributeType.getSubstring() ); 560 } 561 562 if ( attributeType.getSyntax() != null ) 563 { 564 delReference( attributeType, attributeType.getSyntax() ); 565 } 566 567 if ( attributeType.getSuperior() != null ) 568 { 569 delReference( attributeType, attributeType.getSuperior() ); 570 } 571 } 572 573 574 /** 575 * Build the AttributeType references. This has to be done recursively, as 576 * an AttributeType may inherit its parent's MatchingRules. The references 577 * to update are : 578 * - EQUALITY MR 579 * - ORDERING MR 580 * - SUBSTRING MR 581 * - SUP AT 582 * - SYNTAX 583 */ 584 private void buildAttributeTypeReferences( List<Throwable> errors ) 585 { 586 for ( AttributeType attributeType : attributeTypeRegistry ) 587 { 588 if ( ( getUsing( attributeType ) == null ) || getUsing( attributeType ).isEmpty() ) 589 { 590 buildReference( errors, attributeType ); 591 } 592 } 593 } 594 595 596 /** 597 * Build the Comparator references 598 */ 599 private void buildComparatorReferences( List<Throwable> errors ) 600 { 601 for ( LdapComparator<?> comparator : comparatorRegistry ) 602 { 603 buildReference( errors, comparator ); 604 } 605 } 606 607 608 /** 609 * Build the DitContentRule references 610 */ 611 private void buildDitContentRuleReferences( List<Throwable> errors ) 612 { 613 // TODO: implement 614 } 615 616 617 /** 618 * Build the DitStructureRule references 619 */ 620 private void buildDitStructureRuleReferences( List<Throwable> errors ) 621 { 622 // TODO: implement 623 } 624 625 626 /** 627 * Delete the MR references (using and usedBy) : 628 * MR -> C 629 * MR -> N 630 * MR -> S 631 * 632 * @param matchingRule The MatchinRule refere ce to delete 633 */ 634 public void delCrossReferences( MatchingRule matchingRule ) 635 { 636 if ( matchingRule.getLdapComparator() != null ) 637 { 638 delReference( matchingRule, matchingRule.getLdapComparator() ); 639 } 640 641 if ( matchingRule.getNormalizer() != null ) 642 { 643 delReference( matchingRule, matchingRule.getNormalizer() ); 644 } 645 646 if ( matchingRule.getSyntax() != null ) 647 { 648 delReference( matchingRule, matchingRule.getSyntax() ); 649 } 650 } 651 652 653 /** 654 * Build the SchemaObject references 655 * 656 * @param errors The List that collect errors 657 * @param schemaObject The SchemaObject to add 658 */ 659 public void buildReference( List<Throwable> errors, SchemaObject schemaObject ) 660 { 661 try 662 { 663 switch ( schemaObject.getObjectType() ) 664 { 665 case ATTRIBUTE_TYPE: 666 AttributeTypeHelper.addToRegistries( ( MutableAttributeType ) schemaObject, errors, this ); 667 break; 668 669 case DIT_CONTENT_RULE: 670 DitContentRuleHelper.addToRegistries( ( DitContentRule ) schemaObject, errors, this ); 671 break; 672 673 case DIT_STRUCTURE_RULE: 674 DitStructureRuleHelper.addToRegistries( ( DitStructureRule ) schemaObject, errors, this ); 675 break; 676 677 case LDAP_SYNTAX: 678 LdapSyntaxHelper.addToRegistries( ( LdapSyntax ) schemaObject, errors, this ); 679 break; 680 681 case MATCHING_RULE: 682 MatchingRuleHelper.addToRegistries( ( MutableMatchingRule ) schemaObject, errors, this ); 683 break; 684 685 case MATCHING_RULE_USE: 686 MatchingRuleUseHelper.addToRegistries( ( MatchingRuleUse ) schemaObject, errors, this ); 687 break; 688 689 case NAME_FORM: 690 NameFormHelper.addToRegistries( ( NameForm ) schemaObject, errors, this ); 691 break; 692 693 case OBJECT_CLASS: 694 ObjectClassHelper.addToRegistries( ( ObjectClass ) schemaObject, errors, this ); 695 break; 696 697 case SYNTAX_CHECKER: 698 case NORMALIZER: 699 case COMPARATOR: 700 // Those are not registered 701 break; 702 703 default: 704 throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() ); 705 } 706 } 707 catch ( LdapException ne ) 708 { 709 // Not allowed. 710 String msg = I18n.err( I18n.ERR_04292, schemaObject.getName(), ne.getLocalizedMessage() ); 711 712 Throwable error = new LdapProtocolErrorException( msg, ne ); 713 errors.add( error ); 714 LOG.info( msg ); 715 } 716 } 717 718 719 /** 720 * Unlink the SchemaObject references 721 * 722 * @param errors The List that collect errors 723 * @param schemaObject The SchemaObject to remove 724 */ 725 public void removeReference( List<Throwable> errors, SchemaObject schemaObject ) 726 { 727 try 728 { 729 switch ( schemaObject.getObjectType() ) 730 { 731 case ATTRIBUTE_TYPE: 732 AttributeTypeHelper.removeFromRegistries( ( AttributeType ) schemaObject, errors, this ); 733 break; 734 735 case LDAP_SYNTAX: 736 LdapSyntaxHelper.removeFromRegistries( ( LdapSyntax ) schemaObject, errors, this ); 737 break; 738 739 case MATCHING_RULE: 740 MatchingRuleHelper.removeFromRegistries( ( MatchingRule ) schemaObject, errors, this ); 741 break; 742 743 case OBJECT_CLASS: 744 ObjectClassHelper.removeFromRegistries( ( ObjectClass ) schemaObject, errors, this ); 745 break; 746 747 case DIT_CONTENT_RULE : 748 // TODO 749 break; 750 751 case DIT_STRUCTURE_RULE : 752 // TODO 753 break; 754 755 case NAME_FORM : 756 // TODO 757 break; 758 759 case MATCHING_RULE_USE : 760 // TODO 761 break; 762 763 case SYNTAX_CHECKER: 764 case NORMALIZER: 765 case COMPARATOR: 766 // Those were not registered 767 break; 768 769 default: 770 throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() ); 771 } 772 } 773 catch ( LdapException ne ) 774 { 775 // Not allowed. 776 String msg = I18n.err( I18n.ERR_04293, schemaObject.getName(), ne.getLocalizedMessage() ); 777 778 Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg, ne ); 779 errors.add( error ); 780 LOG.info( msg ); 781 } 782 } 783 784 785 /** 786 * Build the MatchingRule references 787 */ 788 private void buildMatchingRuleReferences( List<Throwable> errors ) 789 { 790 for ( MatchingRule matchingRule : matchingRuleRegistry ) 791 { 792 buildReference( errors, matchingRule ); 793 } 794 } 795 796 797 /** 798 * Build the MatchingRuleUse references 799 */ 800 private void buildMatchingRuleUseReferences( List<Throwable> errors ) 801 { 802 for ( MatchingRuleUse matchingRuleUse : matchingRuleUseRegistry ) 803 { 804 buildReference( errors, matchingRuleUse ); 805 } 806 } 807 808 809 /** 810 * Build the NameForm references 811 */ 812 private void buildNameFormReferences( List<Throwable> errors ) 813 { 814 // TODO: implement 815 } 816 817 818 /** 819 * Build the Normalizer references 820 */ 821 private void buildNormalizerReferences( List<Throwable> errors ) 822 { 823 for ( Normalizer normalizer : normalizerRegistry ) 824 { 825 buildReference( errors, normalizer ); 826 } 827 } 828 829 830 /** 831 * Build the ObjectClasses references 832 */ 833 private void buildObjectClassReferences( List<Throwable> errors ) 834 { 835 // Remember the OC we have already processed 836 Set<String> done = new HashSet<>(); 837 838 // The ObjectClass 839 for ( ObjectClass objectClass : objectClassRegistry ) 840 { 841 if ( done.contains( objectClass.getOid() ) ) 842 { 843 continue; 844 } 845 else 846 { 847 done.add( objectClass.getOid() ); 848 } 849 850 buildReference( errors, objectClass ); 851 } 852 } 853 854 855 /** 856 * Build the Syntax references 857 */ 858 private void buildLdapSyntaxReferences( List<Throwable> errors ) 859 { 860 for ( LdapSyntax syntax : ldapSyntaxRegistry ) 861 { 862 buildReference( errors, syntax ); 863 } 864 } 865 866 867 /** 868 * Build the SyntaxChecker references 869 */ 870 private void buildSyntaxCheckerReferences( List<Throwable> errors ) 871 { 872 for ( SyntaxChecker syntaxChecker : syntaxCheckerRegistry ) 873 { 874 buildReference( errors, syntaxChecker ); 875 } 876 } 877 878 879 /** 880 * Build the usedBy and using references from the stored elements. 881 * 882 * @return A list of all the errors we met during the cross reference update 883 */ 884 public List<Throwable> buildReferences() 885 { 886 List<Throwable> errors = new ArrayList<>(); 887 888 // The Comparator references 889 buildComparatorReferences( errors ); 890 891 // The Normalizer references 892 buildNormalizerReferences( errors ); 893 894 // The SyntaxChecker references 895 buildSyntaxCheckerReferences( errors ); 896 897 // The Syntax references 898 buildLdapSyntaxReferences( errors ); 899 900 // The MatchingRules references 901 buildMatchingRuleReferences( errors ); 902 903 // The AttributeType references 904 buildAttributeTypeReferences( errors ); 905 906 // The MatchingRuleUse references 907 buildMatchingRuleUseReferences( errors ); 908 909 // The ObjectClasses references 910 buildObjectClassReferences( errors ); 911 912 // The DitContentRules references 913 buildDitContentRuleReferences( errors ); 914 915 // The NameForms references 916 buildNameFormReferences( errors ); 917 918 // The DitStructureRules references 919 buildDitStructureRuleReferences( errors ); 920 921 return errors; 922 } 923 924 925 /** 926 * Attempts to resolve the SyntaxChecker associated with a Syntax. 927 * 928 * @param syntax the LdapSyntax to resolve the SyntaxChecker of 929 * @param errors the list of errors to add exceptions to 930 */ 931 private void resolve( LdapSyntax syntax, List<Throwable> errors ) 932 { 933 // A LdapSyntax must point to a valid SyntaxChecker 934 // or to the OctetString SyntaxChecker 935 try 936 { 937 LdapSyntaxHelper.addToRegistries( syntax, errors, this ); 938 } 939 catch ( LdapException e ) 940 { 941 errors.add( e ); 942 } 943 } 944 945 946 /** 947 * Attempts to resolve the Normalizer 948 * 949 * @param normalizer the Normalizer 950 * @param errors the list of errors to add exceptions to 951 */ 952 private void resolve( Normalizer normalizer, List<Throwable> errors ) 953 { 954 // This is currently doing nothing. 955 } 956 957 958 /** 959 * Attempts to resolve the LdapComparator 960 * 961 * @param comparator the LdapComparator 962 * @param errors the list of errors to add exceptions to 963 */ 964 private void resolve( LdapComparator<?> comparator, List<Throwable> errors ) 965 { 966 // This is currently doing nothing. 967 } 968 969 970 /** 971 * Attempts to resolve the SyntaxChecker 972 * 973 * @param normalizer the SyntaxChecker 974 * @param errors the list of errors to add exceptions to 975 */ 976 private void resolve( SyntaxChecker syntaxChecker, List<Throwable> errors ) 977 { 978 // This is currently doing nothing. 979 } 980 981 982 /** 983 * Check if the Comparator, Normalizer and the syntax are 984 * existing for a matchingRule. 985 */ 986 private void resolve( MatchingRule matchingRule, List<Throwable> errors ) 987 { 988 // Process the Syntax. It can't be null 989 String syntaxOid = matchingRule.getSyntaxOid(); 990 991 if ( syntaxOid != null ) 992 { 993 // Check if the Syntax is present in the registries 994 try 995 { 996 ldapSyntaxRegistry.lookup( syntaxOid ); 997 } 998 catch ( LdapException ne ) 999 { 1000 // This MR's syntax has not been loaded into the Registries. 1001 LdapSchemaException ldapSchemaException = new LdapSchemaException( 1002 LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_04294, matchingRule.getOid() ), 1003 ne ); 1004 ldapSchemaException.setSourceObject( matchingRule ); 1005 errors.add( ldapSchemaException ); 1006 } 1007 } 1008 else 1009 { 1010 // This is an error. 1011 LdapSchemaException ldapSchemaException = new LdapSchemaException( 1012 LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_04294, matchingRule.getOid() ) ); 1013 ldapSchemaException.setSourceObject( matchingRule ); 1014 errors.add( ldapSchemaException ); 1015 } 1016 1017 // Process the Normalizer 1018 Normalizer normalizer = matchingRule.getNormalizer(); 1019 1020 if ( normalizer == null ) 1021 { 1022 // Ok, no normalizer, this is an error 1023 Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( 1024 I18n.ERR_04295, matchingRule.getOid() ) ); 1025 errors.add( error ); 1026 } 1027 1028 // Process the Comparator 1029 LdapComparator<?> comparator = matchingRule.getLdapComparator(); 1030 1031 if ( comparator == null ) 1032 { 1033 // Ok, no comparator, this is an error 1034 Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( 1035 I18n.ERR_04296, matchingRule.getOid() ) ); 1036 errors.add( error ); 1037 } 1038 } 1039 1040 1041 /** 1042 * Check AttributeType referential integrity 1043 */ 1044 private void resolveRecursive( AttributeType attributeType, Set<String> processed, List<Throwable> errors ) 1045 { 1046 // Process the Superior, if any 1047 String superiorOid = attributeType.getSuperiorOid(); 1048 1049 AttributeType superior = null; 1050 1051 if ( superiorOid != null ) 1052 { 1053 // Check if the Superior is present in the registries 1054 try 1055 { 1056 superior = attributeTypeRegistry.lookup( superiorOid ); 1057 } 1058 catch ( LdapException ne ) 1059 { 1060 // This AT's superior has not been loaded into the Registries. 1061 if ( !processed.contains( superiorOid ) ) 1062 { 1063 errors.add( ne ); 1064 } 1065 } 1066 1067 // We now have to process the superior, if it hasn't been 1068 // processed yet. 1069 if ( superior != null ) 1070 { 1071 if ( !processed.contains( superiorOid ) ) 1072 { 1073 resolveRecursive( superior, processed, errors ); 1074 processed.add( attributeType.getOid() ); 1075 } 1076 else 1077 { 1078 // Not allowed : we have a cyle 1079 Throwable error = new LdapSchemaViolationException( ResultCodeEnum.OTHER, I18n.err( I18n.ERR_04297, 1080 attributeType.getOid() ) ); 1081 errors.add( error ); 1082 return; 1083 } 1084 } 1085 } 1086 1087 // Process the Syntax. If it's null, the attributeType must have 1088 // a Superior. 1089 String syntaxOid = attributeType.getSyntaxOid(); 1090 1091 if ( syntaxOid != null ) 1092 { 1093 // Check if the Syntax is present in the registries 1094 try 1095 { 1096 ldapSyntaxRegistry.lookup( syntaxOid ); 1097 } 1098 catch ( LdapException ne ) 1099 { 1100 // This AT's syntax has not been loaded into the Registries. 1101 errors.add( ne ); 1102 } 1103 } 1104 else 1105 { 1106 // No Syntax : get it from the AttributeType's superior 1107 if ( superior == null ) 1108 { 1109 // This is an error. if the AT does not have a Syntax, 1110 // then it must have a superior, which syntax is get from. 1111 Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err( 1112 I18n.ERR_04298, attributeType.getOid() ) ); 1113 errors.add( error ); 1114 } 1115 } 1116 1117 // Process the EQUALITY MatchingRule. It may be null, but if it's not 1118 // it must have been processed before 1119 String equalityOid = attributeType.getEqualityOid(); 1120 1121 if ( equalityOid != null ) 1122 { 1123 // Check if the MatchingRule is present in the registries 1124 try 1125 { 1126 matchingRuleRegistry.lookup( equalityOid ); 1127 } 1128 catch ( LdapException ne ) 1129 { 1130 // This AT's EQUALITY matchingRule has not been loaded into the Registries. 1131 errors.add( ne ); 1132 } 1133 } 1134 1135 // Process the ORDERING MatchingRule. It may be null, but if it's not 1136 // it must have been processed before 1137 String orderingOid = attributeType.getOrderingOid(); 1138 1139 if ( orderingOid != null ) 1140 { 1141 // Check if the MatchingRule is present in the registries 1142 try 1143 { 1144 matchingRuleRegistry.lookup( orderingOid ); 1145 } 1146 catch ( LdapException ne ) 1147 { 1148 // This AT's ORDERING matchingRule has not been loaded into the Registries. 1149 errors.add( ne ); 1150 } 1151 } 1152 1153 // Process the SUBSTR MatchingRule. It may be null, but if it's not 1154 // it must have been processed before 1155 String substringOid = attributeType.getSubstringOid(); 1156 1157 if ( substringOid != null ) 1158 { 1159 // Check if the MatchingRule is present in the registries 1160 try 1161 { 1162 matchingRuleRegistry.lookup( substringOid ); 1163 } 1164 catch ( LdapException ne ) 1165 { 1166 // This AT's SUBSTR matchingRule has not been loaded into the Registries. 1167 errors.add( ne ); 1168 } 1169 } 1170 } 1171 1172 1173 /** 1174 * Check the inheritance, and the existence of MatchingRules and LdapSyntax 1175 * for an attribute 1176 */ 1177 private void resolve( AttributeType attributeType, List<Throwable> errors ) 1178 { 1179 // This set is used to avoid having more than one error 1180 // for an AttributeType. It's mandatory when processing 1181 // a Superior, as it may be broken and referenced more than once. 1182 Set<String> processed = new HashSet<>(); 1183 1184 // Store the AttributeType itself in the processed, to avoid cycle 1185 processed.add( attributeType.getOid() ); 1186 1187 // Call the recursive method, as we may have superiors to deal with 1188 resolveRecursive( attributeType, processed, errors ); 1189 } 1190 1191 1192 private List<AttributeType> getMustRecursive( List<AttributeType> musts, Set<ObjectClass> processed, 1193 ObjectClass objectClass ) 1194 { 1195 if ( objectClass != null ) 1196 { 1197 if ( processed.contains( objectClass ) ) 1198 { 1199 // We have found a cycle. It has already been reported, 1200 // don't add a new error, just exit. 1201 return null; 1202 } 1203 1204 processed.add( objectClass ); 1205 1206 for ( AttributeType must : objectClass.getMustAttributeTypes() ) 1207 { 1208 musts.add( must ); 1209 } 1210 1211 for ( ObjectClass superior : objectClass.getSuperiors() ) 1212 { 1213 getMustRecursive( musts, processed, superior ); 1214 } 1215 } 1216 1217 return musts; 1218 } 1219 1220 1221 private void resolve( ObjectClass objectClass, List<Throwable> errors ) 1222 { 1223 // This set is used to avoid having more than one error 1224 // for an ObjectClass. It's mandatory when processing 1225 // the Superiors, as they may be broken and referenced more than once. 1226 Set<String> processed = new HashSet<>(); 1227 1228 // Store the ObjectClass itself in the processed, to avoid cycle 1229 processed.add( objectClass.getOid() ); 1230 1231 // Call the recursive method, as we may have superiors to deal with 1232 resolveRecursive( objectClass, processed, errors ); 1233 1234 // Check that the MAY and MUST AT are consistent (no AT in MAY and in MUST 1235 // in one of its superior 1236 List<AttributeType> musts = getMustRecursive( new ArrayList<AttributeType>(), new HashSet<ObjectClass>(), 1237 objectClass ); 1238 1239 if ( musts != null ) 1240 { 1241 for ( AttributeType may : objectClass.getMayAttributeTypes() ) 1242 { 1243 if ( musts.contains( may ) ) 1244 { 1245 // This is not allowed. 1246 LdapSchemaException ldapSchemaException = new LdapSchemaException( 1247 LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY_AND_MUST ); 1248 ldapSchemaException.setSourceObject( objectClass ); 1249 ldapSchemaException.setOtherObject( may ); 1250 errors.add( ldapSchemaException ); 1251 } 1252 } 1253 } 1254 } 1255 1256 1257 private void resolveRecursive( ObjectClass objectClass, Set<String> processed, List<Throwable> errors ) 1258 { 1259 // Process the Superiors, if any 1260 List<String> superiorOids = objectClass.getSuperiorOids(); 1261 ObjectClass superior = null; 1262 1263 for ( String superiorOid : superiorOids ) 1264 { 1265 // Check if the Superior is present in the registries 1266 try 1267 { 1268 superior = objectClassRegistry.lookup( superiorOid ); 1269 } 1270 catch ( LdapException ne ) 1271 { 1272 // This OC's superior has not been loaded into the Registries. 1273 if ( !processed.contains( superiorOid ) ) 1274 { 1275 LdapSchemaException ldapSchemaException = new LdapSchemaException( 1276 LdapSchemaExceptionCodes.OC_NONEXISTENT_SUPERIOR, ne ); 1277 ldapSchemaException.setSourceObject( objectClass ); 1278 ldapSchemaException.setRelatedId( superiorOid ); 1279 errors.add( ldapSchemaException ); 1280 } 1281 } 1282 1283 // We now have to process the superior, if it hasn't been 1284 // processed yet. 1285 if ( superior != null ) 1286 { 1287 if ( !processed.contains( superior.getOid() ) ) 1288 { 1289 resolveRecursive( superior, processed, errors ); 1290 processed.add( objectClass.getOid() ); 1291 } 1292 else 1293 { 1294 // Not allowed : we have a cyle 1295 LdapSchemaException ldapSchemaException = new LdapSchemaException( 1296 LdapSchemaExceptionCodes.OC_CYCLE_CLASS_HIERARCHY ); 1297 ldapSchemaException.setSourceObject( objectClass ); 1298 ldapSchemaException.setOtherObject( superior ); 1299 errors.add( ldapSchemaException ); 1300 return; 1301 } 1302 } 1303 } 1304 1305 // Process the MAY attributeTypes. 1306 for ( String mayOid : objectClass.getMayAttributeTypeOids() ) 1307 { 1308 // Check if the MAY AttributeType is present in the registries 1309 try 1310 { 1311 attributeTypeRegistry.lookup( mayOid ); 1312 } 1313 catch ( LdapException ne ) 1314 { 1315 // This AT has not been loaded into the Registries. 1316 errors.add( ne ); 1317 } 1318 } 1319 1320 // Process the MUST attributeTypes. 1321 for ( String mustOid : objectClass.getMustAttributeTypeOids() ) 1322 { 1323 // Check if the MUST AttributeType is present in the registries 1324 try 1325 { 1326 attributeTypeRegistry.lookup( mustOid ); 1327 } 1328 catch ( LdapException ne ) 1329 { 1330 // This AT has not been loaded into the Registries. 1331 errors.add( ne ); 1332 } 1333 } 1334 1335 // All is done for this ObjectClass, let's apply the registries 1336 try 1337 { 1338 ObjectClassHelper.addToRegistries( objectClass, errors, this ); 1339 } 1340 catch ( LdapException ne ) 1341 { 1342 // Do nothing. We may have a broken OC, 1343 // but at this point, it doesn't matter. 1344 } 1345 } 1346 1347 1348 /** 1349 * Applies the added SchemaObject to the given register 1350 * 1351 * @param errors The list of collected errors 1352 * @param schemaObject The SchemaObject to add 1353 * @param check A flag set when we want the schema checks to be done 1354 * @return The list of found errors 1355 * @throws LdapException If we weren't able to add the SchemaObject 1356 */ 1357 public List<Throwable> add( List<Throwable> errors, SchemaObject schemaObject, boolean check ) throws LdapException 1358 { 1359 // Relax the registries 1360 boolean wasRelaxed = isRelaxed; 1361 setRelaxed(); 1362 1363 // Register the SchemaObject in the registries 1364 register( errors, schemaObject ); 1365 1366 // Associate the SchemaObject with its schema 1367 associateWithSchema( errors, schemaObject ); 1368 1369 // Build the SchemaObject references 1370 if ( check ) 1371 { 1372 buildReference( errors, schemaObject ); 1373 } 1374 1375 // Lock the SchemaObject 1376 schemaObject.lock(); 1377 1378 if ( check && ( errors.isEmpty() ) ) 1379 { 1380 // Check the registries now 1381 List<Throwable> checkErrors = checkRefInteg(); 1382 1383 errors.addAll( checkErrors ); 1384 } 1385 1386 // Get back to Strict mode 1387 if ( !wasRelaxed ) 1388 { 1389 setStrict(); 1390 } 1391 1392 // return the errors 1393 return errors; 1394 } 1395 1396 1397 /** 1398 * Remove the given SchemaObject from the registries 1399 * 1400 * @param errors The list of collected errors 1401 * @param schemaObject The SchemaObject to delete 1402 * @return The list of errors 1403 * @throws LdapException If the deletion failed 1404 */ 1405 public List<Throwable> delete( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException 1406 { 1407 // Relax the registries 1408 boolean wasRelaxed = isRelaxed; 1409 setRelaxed(); 1410 1411 // Remove the SchemaObject from the registries 1412 SchemaObject removed = unregister( errors, schemaObject ); 1413 1414 // Remove the SchemaObject from its schema 1415 dissociateFromSchema( errors, removed ); 1416 1417 // Unlink the SchemaObject references 1418 removeReference( errors, removed ); 1419 1420 if ( errors.isEmpty() ) 1421 { 1422 // Check the registries now 1423 List<Throwable> checkErrors = checkRefInteg(); 1424 1425 errors.addAll( checkErrors ); 1426 } 1427 1428 // Restore the previous registries state 1429 if ( !wasRelaxed ) 1430 { 1431 setStrict(); 1432 } 1433 1434 // return the errors 1435 return errors; 1436 } 1437 1438 1439 /** 1440 * Merely adds the schema to the set of loaded schemas. Does not 1441 * actually do any work to add schema objects to registries. 1442 * 1443 * {@inheritDoc} 1444 */ 1445 @Override 1446 public void schemaLoaded( Schema schema ) 1447 { 1448 this.loadedSchemas.put( Strings.toLowerCaseAscii( schema.getSchemaName() ), schema ); 1449 } 1450 1451 1452 /** 1453 * Merely removes the schema from the set of loaded schemas. Does not 1454 * actually do any work to remove schema objects from registries. 1455 * 1456 * {@inheritDoc} 1457 */ 1458 @Override 1459 public void schemaUnloaded( Schema schema ) 1460 { 1461 this.loadedSchemas.remove( Strings.toLowerCaseAscii( schema.getSchemaName() ) ); 1462 } 1463 1464 1465 /** 1466 * Gets an unmodifiable Map of schema names to loaded Schema objects. 1467 * 1468 * @return the map of loaded Schema objects 1469 */ 1470 public Map<String, Schema> getLoadedSchemas() 1471 { 1472 return Collections.unmodifiableMap( loadedSchemas ); 1473 } 1474 1475 1476 /** 1477 * @return Gets a reference to the Map associating a schemaName to 1478 * its contained SchemaObjects 1479 */ 1480 public Map<String, Set<SchemaObjectWrapper>> getObjectBySchemaName() 1481 { 1482 return schemaObjects; 1483 } 1484 1485 1486 /** 1487 * Retrieve the schema name for a specific SchemaObject, or return "other" if none is found. 1488 */ 1489 private String getSchemaName( SchemaObject schemaObject ) 1490 { 1491 String schemaName = Strings.toLowerCaseAscii( schemaObject.getSchemaName() ); 1492 1493 if ( loadedSchemas.containsKey( schemaName ) ) 1494 { 1495 return schemaName; 1496 } 1497 else 1498 { 1499 return MetaSchemaConstants.SCHEMA_OTHER; 1500 } 1501 } 1502 1503 1504 /** 1505 * Tells if the given SchemaObject is present in one schema. The schema 1506 * may be disabled. 1507 * 1508 * @param schemaObject The schemaObject we are looking for 1509 * @return true if the schemaObject is present in a schema 1510 */ 1511 public boolean contains( SchemaObject schemaObject ) 1512 { 1513 String schemaName = schemaObject.getSchemaName(); 1514 1515 Set<SchemaObjectWrapper> setSchemaObjects = schemaObjects.get( schemaName ); 1516 1517 if ( ( setSchemaObjects == null ) || setSchemaObjects.isEmpty() ) 1518 { 1519 return false; 1520 } 1521 1522 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); 1523 1524 return setSchemaObjects.contains( wrapper ); 1525 } 1526 1527 1528 /** 1529 * Create a new schema association with its content 1530 * 1531 * @param schemaName The schema name 1532 * @return A set containing the associations 1533 */ 1534 public Set<SchemaObjectWrapper> addSchema( String schemaName ) 1535 { 1536 Set<SchemaObjectWrapper> content = new HashSet<>(); 1537 schemaObjects.put( schemaName, content ); 1538 1539 return content; 1540 } 1541 1542 1543 /** 1544 * Register the given SchemaObject into the associated Registry 1545 */ 1546 private void register( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException 1547 { 1548 LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); 1549 1550 // Check that the SchemaObject is not already registered 1551 if ( !( schemaObject instanceof LoadableSchemaObject ) && globalOidRegistry.contains( schemaObject.getOid() ) ) 1552 { 1553 String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() ); 1554 LOG.error( msg ); 1555 Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 1556 errors.add( error ); 1557 return; 1558 } 1559 1560 try 1561 { 1562 // First call the specific registry's register method 1563 switch ( schemaObject.getObjectType() ) 1564 { 1565 case ATTRIBUTE_TYPE: 1566 attributeTypeRegistry.register( ( AttributeType ) schemaObject ); 1567 break; 1568 1569 case COMPARATOR: 1570 comparatorRegistry.register( ( LdapComparator<?> ) schemaObject ); 1571 break; 1572 1573 case DIT_CONTENT_RULE: 1574 ditContentRuleRegistry.register( ( DitContentRule ) schemaObject ); 1575 break; 1576 1577 case DIT_STRUCTURE_RULE: 1578 ditStructureRuleRegistry.register( ( DitStructureRule ) schemaObject ); 1579 break; 1580 1581 case LDAP_SYNTAX: 1582 ldapSyntaxRegistry.register( ( LdapSyntax ) schemaObject ); 1583 break; 1584 1585 case MATCHING_RULE: 1586 matchingRuleRegistry.register( ( MatchingRule ) schemaObject ); 1587 break; 1588 1589 case MATCHING_RULE_USE: 1590 matchingRuleUseRegistry.register( ( MatchingRuleUse ) schemaObject ); 1591 break; 1592 1593 case NAME_FORM: 1594 nameFormRegistry.register( ( NameForm ) schemaObject ); 1595 break; 1596 1597 case NORMALIZER: 1598 normalizerRegistry.register( ( Normalizer ) schemaObject ); 1599 break; 1600 1601 case OBJECT_CLASS: 1602 objectClassRegistry.register( ( ObjectClass ) schemaObject ); 1603 break; 1604 1605 case SYNTAX_CHECKER: 1606 syntaxCheckerRegistry.register( ( SyntaxChecker ) schemaObject ); 1607 break; 1608 1609 default: 1610 throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() ); 1611 } 1612 } 1613 catch ( Exception e ) 1614 { 1615 errors.add( e ); 1616 } 1617 } 1618 1619 1620 /** 1621 * Store the given SchemaObject in the Map associating SchemaObjetcs to their 1622 * related Schema. 1623 * 1624 * @param errors The list of errors we are accumulating while checking the schema 1625 * @param schemaObject The schemaObject to register 1626 */ 1627 public void associateWithSchema( List<Throwable> errors, SchemaObject schemaObject ) 1628 { 1629 LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); 1630 1631 // Check that the SchemaObject is not already registered 1632 if ( !( schemaObject instanceof LoadableSchemaObject ) && globalOidRegistry.contains( schemaObject.getOid() ) ) 1633 { 1634 String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() ); 1635 LOG.error( msg ); 1636 Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 1637 errors.add( error ); 1638 1639 return; 1640 } 1641 1642 // Get a normalized form of schema name 1643 String schemaName = getSchemaName( schemaObject ); 1644 1645 // And register the schemaObject within its schema 1646 Set<SchemaObjectWrapper> content = schemaObjects.get( schemaName ); 1647 1648 if ( content == null ) 1649 { 1650 content = new HashSet<>(); 1651 schemaObjects.put( Strings.toLowerCaseAscii( schemaName ), content ); 1652 } 1653 1654 SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); 1655 1656 if ( content.contains( schemaObjectWrapper ) ) 1657 { 1658 // Already present ! 1659 // What should we do ? 1660 LOG.info( "Registering of {}:{} failed, is already present in the Registries", 1661 schemaObject.getObjectType(), schemaObject.getOid() ); 1662 } 1663 else 1664 { 1665 // Create the association 1666 content.add( schemaObjectWrapper ); 1667 1668 // Update the global OidRegistry if the SchemaObject is not 1669 // an instance of LoadableSchemaObject 1670 if ( !( schemaObject instanceof LoadableSchemaObject ) ) 1671 { 1672 try 1673 { 1674 globalOidRegistry.register( schemaObject ); 1675 } 1676 catch ( LdapException ne ) 1677 { 1678 errors.add( ne ); 1679 return; 1680 } 1681 } 1682 1683 LOG.debug( "registered {} for OID {}", schemaObject.getName(), schemaObject.getOid() ); 1684 } 1685 } 1686 1687 1688 /** 1689 * Store the given SchemaObject in the Map associating SchemaObjetcs to their 1690 * related Schema. 1691 * 1692 * @param errors The list that collect errors 1693 * @param schemaObject The schemaObject to register 1694 * @throws LdapException If there is a problem 1695 */ 1696 1697 public void dissociateFromSchema( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException 1698 { 1699 LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); 1700 1701 // Check that the SchemaObject is already registered 1702 if ( !( schemaObject instanceof LoadableSchemaObject ) && !globalOidRegistry.contains( schemaObject.getOid() ) ) 1703 { 1704 String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() ); 1705 LOG.error( msg ); 1706 Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 1707 errors.add( error ); 1708 1709 return; 1710 } 1711 1712 // Get a normalized form of schema name 1713 String schemaName = getSchemaName( schemaObject ); 1714 String oid = schemaObject.getOid(); 1715 1716 // And unregister the schemaObject from its schema 1717 Set<SchemaObjectWrapper> content = schemaObjects.get( schemaName ); 1718 1719 SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); 1720 1721 if ( !content.contains( schemaObjectWrapper ) ) 1722 { 1723 // Not present ! 1724 // What should we do ? 1725 LOG.info( "Unregistering of {}:{} failed, is not present in the Registries", schemaObject.getObjectType(), 1726 schemaObject.getOid() ); 1727 } 1728 else 1729 { 1730 // Remove the association 1731 content.remove( schemaObjectWrapper ); 1732 1733 // Update the global OidRegistry if the SchemaObject is not 1734 // an instance of LoadableSchemaObject 1735 if ( !( schemaObject instanceof LoadableSchemaObject ) ) 1736 { 1737 try 1738 { 1739 globalOidRegistry.unregister( oid ); 1740 } 1741 catch ( LdapException ne ) 1742 { 1743 errors.add( ne ); 1744 return; 1745 } 1746 } 1747 1748 LOG.debug( "Unregistered {} for OID {}", schemaObject.getName(), schemaObject.getOid() ); 1749 } 1750 } 1751 1752 1753 /** 1754 * Unregister a SchemaObject from the registries 1755 * 1756 * @param schemaObject The SchemaObject we want to deregister 1757 * @throws LdapException If the removal failed 1758 */ 1759 private SchemaObject unregister( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException 1760 { 1761 LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); 1762 1763 // Check that the SchemaObject is present in the registries 1764 if ( !( schemaObject instanceof LoadableSchemaObject ) && !globalOidRegistry.contains( schemaObject.getOid() ) ) 1765 { 1766 String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() ); 1767 LOG.error( msg ); 1768 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 1769 } 1770 1771 SchemaObject unregistered; 1772 1773 // First call the specific registry's register method 1774 switch ( schemaObject.getObjectType() ) 1775 { 1776 case ATTRIBUTE_TYPE: 1777 unregistered = attributeTypeRegistry.unregister( ( AttributeType ) schemaObject ); 1778 break; 1779 1780 case COMPARATOR: 1781 unregistered = comparatorRegistry.unregister( ( LdapComparator<?> ) schemaObject ); 1782 break; 1783 1784 case DIT_CONTENT_RULE: 1785 unregistered = ditContentRuleRegistry.unregister( ( DitContentRule ) schemaObject ); 1786 break; 1787 1788 case DIT_STRUCTURE_RULE: 1789 unregistered = ditStructureRuleRegistry.unregister( ( DitStructureRule ) schemaObject ); 1790 break; 1791 1792 case LDAP_SYNTAX: 1793 unregistered = ldapSyntaxRegistry.unregister( ( LdapSyntax ) schemaObject ); 1794 break; 1795 1796 case MATCHING_RULE: 1797 unregistered = matchingRuleRegistry.unregister( ( MatchingRule ) schemaObject ); 1798 break; 1799 1800 case MATCHING_RULE_USE: 1801 unregistered = matchingRuleUseRegistry.unregister( ( MatchingRuleUse ) schemaObject ); 1802 break; 1803 1804 case NAME_FORM: 1805 unregistered = nameFormRegistry.unregister( ( NameForm ) schemaObject ); 1806 break; 1807 1808 case NORMALIZER: 1809 unregistered = normalizerRegistry.unregister( ( Normalizer ) schemaObject ); 1810 break; 1811 1812 case OBJECT_CLASS: 1813 unregistered = objectClassRegistry.unregister( ( ObjectClass ) schemaObject ); 1814 break; 1815 1816 case SYNTAX_CHECKER: 1817 unregistered = syntaxCheckerRegistry.unregister( ( SyntaxChecker ) schemaObject ); 1818 break; 1819 1820 default: 1821 throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() ); 1822 } 1823 1824 return unregistered; 1825 } 1826 1827 1828 /** 1829 * Remove the given SchemaObject from the Map associating SchemaObjetcs to their 1830 * related Schema. 1831 * 1832 * @param schemaObject The schemaObject to remove 1833 * @throws LdapException If there is a problem 1834 */ 1835 public void dissociateFromSchema( SchemaObject schemaObject ) throws LdapException 1836 { 1837 // And unregister the schemaObject within its schema 1838 Set<SchemaObjectWrapper> content = schemaObjects.get( Strings.toLowerCaseAscii( schemaObject.getSchemaName() ) ); 1839 1840 if ( content != null ) 1841 { 1842 SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); 1843 1844 if ( content.contains( schemaObjectWrapper ) ) 1845 { 1846 // remove the schemaObject 1847 content.remove( schemaObjectWrapper ); 1848 1849 // Update the global OidRegistry if the SchemaObject is not 1850 // an instance of LoadableSchemaObject 1851 if ( !( schemaObject instanceof LoadableSchemaObject ) ) 1852 { 1853 globalOidRegistry.unregister( schemaObject.getOid() ); 1854 } 1855 1856 LOG.debug( "Unregistered {}:{}", schemaObject.getObjectType(), schemaObject.getOid() ); 1857 } 1858 else 1859 { 1860 // Not present !! 1861 // What should we do ? 1862 LOG.debug( "Unregistering of {}:{} failed, not found in Registries", schemaObject.getObjectType(), 1863 schemaObject.getOid() ); 1864 } 1865 } 1866 } 1867 1868 1869 /** 1870 * Checks if a specific SchemaObject is referenced by any other SchemaObject. 1871 * 1872 * @param schemaObject The SchemaObject we are looking for 1873 * @return true if there is at least one SchemaObjetc referencing the given one 1874 */ 1875 public boolean isReferenced( SchemaObject schemaObject ) 1876 { 1877 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); 1878 1879 Set<SchemaObjectWrapper> set = usedBy.get( wrapper ); 1880 1881 boolean referenced = ( set != null ) && !set.isEmpty(); 1882 1883 if ( LOG.isDebugEnabled() ) 1884 { 1885 if ( referenced ) 1886 { 1887 LOG.debug( "The {}:{} is referenced", schemaObject.getObjectType(), schemaObject.getOid() ); 1888 } 1889 else 1890 { 1891 LOG.debug( "The {}:{} is not referenced", schemaObject.getObjectType(), schemaObject.getOid() ); 1892 } 1893 } 1894 1895 return referenced; 1896 } 1897 1898 1899 /** 1900 * Gets the Set of SchemaObjects referencing the given SchemaObject 1901 * 1902 * @param schemaObject The SchemaObject we are looking for 1903 * @return The Set of referencing SchemaObject, or null 1904 */ 1905 public Set<SchemaObjectWrapper> getUsedBy( SchemaObject schemaObject ) 1906 { 1907 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); 1908 1909 return usedBy.get( wrapper ); 1910 } 1911 1912 1913 /** 1914 * Dump the UsedBy data structure as a String 1915 * 1916 * @return The UsedBy data structure 1917 */ 1918 public String dumpUsedBy() 1919 { 1920 StringBuilder sb = new StringBuilder(); 1921 1922 sb.append( "USED BY :\n" ); 1923 1924 try 1925 { 1926 for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : usedBy.entrySet() ) 1927 { 1928 SchemaObjectWrapper wrapper = entry.getKey(); 1929 1930 sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ) 1931 .append( "] : {" ); 1932 1933 boolean isFirst = true; 1934 1935 for ( SchemaObjectWrapper uses : entry.getValue() ) 1936 { 1937 if ( isFirst ) 1938 { 1939 isFirst = false; 1940 } 1941 else 1942 { 1943 sb.append( ", " ); 1944 } 1945 1946 sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" ); 1947 } 1948 1949 sb.append( "}\n" ); 1950 } 1951 } 1952 catch ( Exception e ) 1953 { 1954 e.printStackTrace(); 1955 } 1956 1957 return sb.toString(); 1958 } 1959 1960 1961 /** 1962 * Dump the Using data structure as a String 1963 * 1964 * @return The Using data structure 1965 */ 1966 public String dumpUsing() 1967 { 1968 StringBuilder sb = new StringBuilder(); 1969 1970 sb.append( "USING :\n" ); 1971 1972 try 1973 { 1974 for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : using.entrySet() ) 1975 { 1976 SchemaObjectWrapper wrapper = entry.getKey(); 1977 1978 sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ) 1979 .append( "] : {" ); 1980 1981 boolean isFirst = true; 1982 1983 for ( SchemaObjectWrapper uses : entry.getValue() ) 1984 { 1985 if ( isFirst ) 1986 { 1987 isFirst = false; 1988 } 1989 else 1990 { 1991 sb.append( ", " ); 1992 } 1993 1994 sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" ); 1995 } 1996 1997 sb.append( "}\n" ); 1998 } 1999 } 2000 catch ( Exception e ) 2001 { 2002 e.printStackTrace(); 2003 } 2004 2005 return sb.toString(); 2006 } 2007 2008 2009 /** 2010 * Gets the Set of SchemaObjects referenced by the given SchemaObject 2011 * 2012 * @param schemaObject The SchemaObject we are looking for 2013 * @return The Set of referenced SchemaObject, or null 2014 */ 2015 public Set<SchemaObjectWrapper> getUsing( SchemaObject schemaObject ) 2016 { 2017 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject ); 2018 2019 return using.get( wrapper ); 2020 } 2021 2022 2023 /** 2024 * Add an association between a SchemaObject an the SchemaObject it refers 2025 * 2026 * @param reference The base SchemaObject 2027 * @param referee The SchemaObject pointing on the reference 2028 */ 2029 private void addUsing( SchemaObject reference, SchemaObject referee ) 2030 { 2031 if ( ( reference == null ) || ( referee == null ) ) 2032 { 2033 return; 2034 } 2035 2036 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference ); 2037 2038 Set<SchemaObjectWrapper> uses = getUsing( reference ); 2039 2040 if ( uses == null ) 2041 { 2042 uses = new HashSet<>(); 2043 } 2044 2045 uses.add( new SchemaObjectWrapper( referee ) ); 2046 2047 // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly 2048 using.put( wrapper, uses ); 2049 } 2050 2051 2052 /** 2053 * Add an association between a SchemaObject an the SchemaObject it refers 2054 * 2055 * @param base The base SchemaObject 2056 * @param referenced The referenced SchemaObject 2057 */ 2058 public void addReference( SchemaObject base, SchemaObject referenced ) 2059 { 2060 if ( LOG.isDebugEnabled() ) 2061 { 2062 LOG.debug( dump( "add", base, referenced ) ); 2063 } 2064 2065 addUsing( base, referenced ); 2066 addUsedBy( referenced, base ); 2067 2068 // do not change to debug mode, this makes the server logs hard to read and useless 2069 // and even prevents the server from starting up 2070 if ( LOG.isTraceEnabled() ) 2071 { 2072 LOG.trace( dumpUsedBy() ); 2073 LOG.trace( dumpUsing() ); 2074 } 2075 } 2076 2077 2078 /** 2079 * Add an association between a SchemaObject an the SchemaObject that refers it 2080 * 2081 * @param reference The base SchemaObject 2082 * @param referee The SchemaObject pointing on the reference 2083 */ 2084 private void addUsedBy( SchemaObject referee, SchemaObject reference ) 2085 { 2086 if ( ( reference == null ) || ( referee == null ) ) 2087 { 2088 return; 2089 } 2090 2091 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee ); 2092 2093 Set<SchemaObjectWrapper> uses = getUsedBy( referee ); 2094 2095 if ( uses == null ) 2096 { 2097 uses = new HashSet<>(); 2098 } 2099 2100 uses.add( new SchemaObjectWrapper( reference ) ); 2101 2102 // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly 2103 usedBy.put( wrapper, uses ); 2104 } 2105 2106 2107 /** 2108 * Del an association between a SchemaObject an the SchemaObject it refers 2109 * 2110 * @param reference The base SchemaObject 2111 * @param referee The SchemaObject pointing on the reference 2112 */ 2113 private void delUsing( SchemaObject reference, SchemaObject referee ) 2114 { 2115 if ( ( reference == null ) || ( referee == null ) ) 2116 { 2117 return; 2118 } 2119 2120 Set<SchemaObjectWrapper> uses = getUsing( reference ); 2121 2122 if ( uses == null ) 2123 { 2124 return; 2125 } 2126 2127 uses.remove( new SchemaObjectWrapper( referee ) ); 2128 2129 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference ); 2130 2131 if ( uses.isEmpty() ) 2132 { 2133 using.remove( wrapper ); 2134 } 2135 else 2136 { 2137 using.put( wrapper, uses ); 2138 } 2139 } 2140 2141 2142 /** 2143 * Del an association between a SchemaObject an the SchemaObject that refers it 2144 * 2145 * @param reference The base SchemaObject 2146 * @param referee The SchemaObject pointing on the reference 2147 */ 2148 private void delUsedBy( SchemaObject referee, SchemaObject reference ) 2149 { 2150 if ( ( reference == null ) || ( referee == null ) ) 2151 { 2152 return; 2153 } 2154 2155 Set<SchemaObjectWrapper> uses = getUsedBy( referee ); 2156 2157 if ( uses == null ) 2158 { 2159 return; 2160 } 2161 2162 uses.remove( new SchemaObjectWrapper( reference ) ); 2163 2164 SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee ); 2165 2166 if ( uses.isEmpty() ) 2167 { 2168 usedBy.remove( wrapper ); 2169 } 2170 else 2171 { 2172 usedBy.put( wrapper, uses ); 2173 } 2174 } 2175 2176 2177 /** 2178 * Delete an association between a SchemaObject an the SchemaObject it refers 2179 * 2180 * @param base The base SchemaObject 2181 * @param referenced The referenced SchemaObject 2182 */ 2183 public void delReference( SchemaObject base, SchemaObject referenced ) 2184 { 2185 if ( LOG.isDebugEnabled() ) 2186 { 2187 LOG.debug( dump( "del", base, referenced ) ); 2188 } 2189 2190 delUsing( base, referenced ); 2191 delUsedBy( referenced, base ); 2192 2193 if ( LOG.isDebugEnabled() ) 2194 { 2195 LOG.debug( dumpUsedBy() ); 2196 LOG.debug( dumpUsing() ); 2197 } 2198 } 2199 2200 2201 /** 2202 * Dump the reference operation as a String 2203 */ 2204 private String dump( String op, SchemaObject reference, SchemaObject referee ) 2205 { 2206 return op + " : " + reference.getObjectType() + "[" + reference.getOid() + "]/[" + referee.getObjectType() 2207 + "[" + referee.getOid() + "]"; 2208 } 2209 2210 2211 private boolean checkReferences( SchemaObject reference, SchemaObject referee, String message ) 2212 { 2213 SchemaObjectWrapper referenceWrapper = new SchemaObjectWrapper( reference ); 2214 SchemaObjectWrapper refereeWrapper = new SchemaObjectWrapper( referee ); 2215 2216 // Check the references : Syntax -> SyntaxChecker 2217 if ( !using.containsKey( referenceWrapper ) ) 2218 { 2219 LOG.debug( "The Syntax {}:{} does not reference any " + message, reference.getObjectType(), reference 2220 .getOid() ); 2221 2222 return false; 2223 } 2224 2225 Set<SchemaObjectWrapper> usings = using.get( referenceWrapper ); 2226 2227 if ( !usings.contains( refereeWrapper ) ) 2228 { 2229 LOG.debug( "The {}:{} does not reference any " + message, reference.getObjectType(), reference.getOid() ); 2230 2231 return false; 2232 } 2233 2234 // Check the referees : SyntaxChecker -> Syntax 2235 if ( !usedBy.containsKey( refereeWrapper ) ) 2236 { 2237 LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() ); 2238 2239 return false; 2240 } 2241 2242 Set<SchemaObjectWrapper> used = usedBy.get( refereeWrapper ); 2243 2244 if ( !used.contains( referenceWrapper ) ) 2245 { 2246 LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() ); 2247 2248 return false; 2249 } 2250 2251 return true; 2252 } 2253 2254 2255 /** 2256 * Check the registries for invalid relations. This check stops at the first error. 2257 * 2258 * @return true if the Registries is consistent, false otherwise 2259 */ 2260 public boolean check() 2261 { 2262 // Check the Syntaxes : check for a SyntaxChecker 2263 LOG.debug( "Checking Syntaxes" ); 2264 2265 for ( LdapSyntax syntax : ldapSyntaxRegistry ) 2266 { 2267 // Check that each Syntax has a SyntaxChecker 2268 if ( syntax.getSyntaxChecker() == null ) 2269 { 2270 LOG.debug( "The Syntax {} has no SyntaxChecker", syntax ); 2271 2272 return false; 2273 } 2274 2275 if ( !syntaxCheckerRegistry.contains( syntax.getSyntaxChecker().getOid() ) ) 2276 { 2277 LOG.debug( "Cannot find the SyntaxChecker {} for the Syntax {}", syntax.getSyntaxChecker().getOid(), 2278 syntax ); 2279 2280 return false; 2281 } 2282 2283 // Check the references : Syntax -> SyntaxChecker and SyntaxChecker -> Syntax 2284 if ( !checkReferences( syntax, syntax.getSyntaxChecker(), "SyntaxChecker" ) ) 2285 { 2286 return false; 2287 } 2288 } 2289 2290 // Check the MatchingRules : check for a Normalizer, a Comparator and a Syntax 2291 LOG.debug( "Checking MatchingRules..." ); 2292 2293 for ( MatchingRule matchingRule : matchingRuleRegistry ) 2294 { 2295 // Check that each MatchingRule has a Normalizer 2296 if ( matchingRule.getNormalizer() == null ) 2297 { 2298 LOG.debug( "The MatchingRule {} has no Normalizer", matchingRule ); 2299 2300 return false; 2301 } 2302 2303 // Check that each MatchingRule has a Normalizer 2304 if ( !normalizerRegistry.contains( matchingRule.getNormalizer().getOid() ) ) 2305 { 2306 LOG.debug( "Cannot find the Normalizer {} for the MatchingRule {}", matchingRule.getNormalizer() 2307 .getOid(), matchingRule ); 2308 2309 return false; 2310 } 2311 2312 // Check that each MatchingRule has a Comparator 2313 if ( matchingRule.getLdapComparator() == null ) 2314 { 2315 LOG.debug( "The MatchingRule {} has no Comparator", matchingRule ); 2316 2317 return false; 2318 } 2319 2320 if ( !comparatorRegistry.contains( matchingRule.getLdapComparator().getOid() ) ) 2321 { 2322 LOG.debug( "Cannot find the Comparator {} for the MatchingRule {}", matchingRule.getLdapComparator() 2323 .getOid(), matchingRule ); 2324 2325 return false; 2326 } 2327 2328 // Check that each MatchingRule has a Syntax 2329 if ( matchingRule.getSyntax() == null ) 2330 { 2331 LOG.debug( "The MatchingRule {} has no Syntax", matchingRule ); 2332 2333 return false; 2334 } 2335 2336 if ( !ldapSyntaxRegistry.contains( matchingRule.getSyntax().getOid() ) ) 2337 { 2338 LOG.debug( "Cannot find the Syntax {} for the MatchingRule {}", matchingRule.getSyntax().getOid(), 2339 matchingRule ); 2340 2341 return false; 2342 } 2343 2344 // Check the references : MR -> S and S -> MR 2345 if ( !checkReferences( matchingRule, matchingRule.getSyntax(), "Syntax" ) ) 2346 { 2347 return false; 2348 } 2349 2350 // Check the references : MR -> N 2351 if ( !checkReferences( matchingRule, matchingRule.getNormalizer(), "Normalizer" ) ) 2352 { 2353 return false; 2354 } 2355 2356 // Check the references : MR -> C and C -> MR 2357 if ( !checkReferences( matchingRule, matchingRule.getLdapComparator(), "Comparator" ) ) 2358 { 2359 return false; 2360 } 2361 } 2362 2363 // Check the ObjectClasses : check for MAY, MUST, SUPERIORS 2364 LOG.debug( "Checking ObjectClasses..." ); 2365 2366 for ( ObjectClass objectClass : objectClassRegistry ) 2367 { 2368 // Check that each ObjectClass has all the MAY AttributeTypes 2369 if ( objectClass.getMayAttributeTypes() != null ) 2370 { 2371 for ( AttributeType may : objectClass.getMayAttributeTypes() ) 2372 { 2373 if ( !attributeTypeRegistry.contains( may.getOid() ) ) 2374 { 2375 LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MAY", may, objectClass ); 2376 2377 return false; 2378 } 2379 2380 // Check the references : OC -> AT and AT -> OC (MAY) 2381 if ( !checkReferences( objectClass, may, "AttributeType" ) ) 2382 { 2383 return false; 2384 } 2385 } 2386 } 2387 2388 // Check that each ObjectClass has all the MUST AttributeTypes 2389 if ( objectClass.getMustAttributeTypes() != null ) 2390 { 2391 for ( AttributeType must : objectClass.getMustAttributeTypes() ) 2392 { 2393 if ( !attributeTypeRegistry.contains( must.getOid() ) ) 2394 { 2395 LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MUST", must, objectClass ); 2396 2397 return false; 2398 } 2399 2400 // Check the references : OC -> AT and AT -> OC (MUST) 2401 if ( !checkReferences( objectClass, must, "AttributeType" ) ) 2402 { 2403 return false; 2404 } 2405 } 2406 } 2407 2408 // Check that each ObjectClass has all the SUPERIORS ObjectClasses 2409 if ( objectClass.getSuperiors() != null ) 2410 { 2411 for ( ObjectClass superior : objectClass.getSuperiors() ) 2412 { 2413 if ( !objectClassRegistry.contains( objectClass.getOid() ) ) 2414 { 2415 LOG.debug( "Cannot find the ObjectClass {} for the ObjectClass {} SUPERIORS", superior, 2416 objectClass ); 2417 2418 return false; 2419 } 2420 2421 // Check the references : OC -> OC and OC -> OC (SUPERIORS) 2422 if ( !checkReferences( objectClass, superior, "ObjectClass" ) ) 2423 { 2424 return false; 2425 } 2426 } 2427 } 2428 } 2429 2430 // Check the AttributeTypes : check for MatchingRules, Syntaxes 2431 LOG.debug( "Checking AttributeTypes..." ); 2432 2433 for ( AttributeType attributeType : attributeTypeRegistry ) 2434 { 2435 // Check that each AttributeType has a SYNTAX 2436 if ( attributeType.getSyntax() == null ) 2437 { 2438 LOG.debug( "The AttributeType {} has no Syntax", attributeType ); 2439 2440 return false; 2441 } 2442 2443 if ( !ldapSyntaxRegistry.contains( attributeType.getSyntax().getOid() ) ) 2444 { 2445 LOG.debug( "Cannot find the Syntax {} for the AttributeType {}", attributeType.getSyntax().getOid(), 2446 attributeType ); 2447 2448 return false; 2449 } 2450 2451 // Check the references for AT -> S and S -> AT 2452 if ( !checkReferences( attributeType, attributeType.getSyntax(), "AttributeType" ) ) 2453 { 2454 return false; 2455 } 2456 2457 // Check the EQUALITY MatchingRule 2458 if ( attributeType.getEquality() != null ) 2459 { 2460 if ( !matchingRuleRegistry.contains( attributeType.getEquality().getOid() ) ) 2461 { 2462 LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getEquality() 2463 .getOid(), attributeType ); 2464 2465 return false; 2466 } 2467 2468 // Check the references for AT -> MR and MR -> AT 2469 if ( !checkReferences( attributeType, attributeType.getEquality(), "AttributeType" ) ) 2470 { 2471 return false; 2472 } 2473 } 2474 2475 // Check the ORDERING MatchingRule 2476 if ( attributeType.getOrdering() != null ) 2477 { 2478 if ( !matchingRuleRegistry.contains( attributeType.getOrdering().getOid() ) ) 2479 { 2480 LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getOrdering() 2481 .getOid(), attributeType ); 2482 2483 return false; 2484 } 2485 2486 // Check the references for AT -> MR and MR -> AT 2487 if ( !checkReferences( attributeType, attributeType.getOrdering(), "AttributeType" ) ) 2488 { 2489 return false; 2490 } 2491 } 2492 2493 // Check the SUBSTR MatchingRule 2494 if ( attributeType.getSubstring() != null ) 2495 { 2496 if ( !matchingRuleRegistry.contains( attributeType.getSubstring().getOid() ) ) 2497 { 2498 LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getSubstring() 2499 .getOid(), attributeType ); 2500 2501 return false; 2502 } 2503 2504 // Check the references for AT -> MR and MR -> AT 2505 if ( !checkReferences( attributeType, attributeType.getSubstring(), "AttributeType" ) ) 2506 { 2507 return false; 2508 } 2509 } 2510 2511 // Check the SUP 2512 if ( attributeType.getSuperior() != null ) 2513 { 2514 AttributeType superior = attributeType.getSuperior(); 2515 2516 if ( !attributeTypeRegistry.contains( superior.getOid() ) ) 2517 { 2518 LOG.debug( "Cannot find the AttributeType {} for the AttributeType {} SUPERIOR", superior, 2519 attributeType ); 2520 2521 return false; 2522 } 2523 2524 // Check the references : AT -> AT and AT -> AT (SUPERIOR) 2525 if ( !checkReferences( attributeType, superior, "AttributeType" ) ) 2526 { 2527 return false; 2528 } 2529 } 2530 } 2531 2532 return true; 2533 } 2534 2535 2536 /** 2537 * Clone the Registries. This is done in two steps : 2538 * - first clone the SchemaObjetc registries 2539 * - second restore the relation between them 2540 */ 2541 // False positive 2542 @Override 2543 public Registries clone() throws CloneNotSupportedException 2544 { 2545 // First clone the structure 2546 Registries clone = ( Registries ) super.clone(); 2547 2548 // Now, clone the oidRegistry 2549 clone.globalOidRegistry = globalOidRegistry.copy(); 2550 2551 // We have to clone every SchemaObject registries now 2552 clone.attributeTypeRegistry = attributeTypeRegistry.copy(); 2553 clone.comparatorRegistry = comparatorRegistry.copy(); 2554 clone.ditContentRuleRegistry = ditContentRuleRegistry.copy(); 2555 clone.ditStructureRuleRegistry = ditStructureRuleRegistry.copy(); 2556 clone.ldapSyntaxRegistry = ldapSyntaxRegistry.copy(); 2557 clone.matchingRuleRegistry = matchingRuleRegistry.copy(); 2558 clone.matchingRuleUseRegistry = matchingRuleUseRegistry.copy(); 2559 clone.nameFormRegistry = nameFormRegistry.copy(); 2560 clone.normalizerRegistry = normalizerRegistry.copy(); 2561 clone.objectClassRegistry = objectClassRegistry.copy(); 2562 clone.syntaxCheckerRegistry = syntaxCheckerRegistry.copy(); 2563 2564 // Store all the SchemaObjects into the globalOid registry 2565 for ( AttributeType attributeType : clone.attributeTypeRegistry ) 2566 { 2567 clone.globalOidRegistry.put( attributeType ); 2568 } 2569 2570 for ( DitContentRule ditContentRule : clone.ditContentRuleRegistry ) 2571 { 2572 clone.globalOidRegistry.put( ditContentRule ); 2573 } 2574 2575 for ( DitStructureRule ditStructureRule : clone.ditStructureRuleRegistry ) 2576 { 2577 clone.globalOidRegistry.put( ditStructureRule ); 2578 } 2579 2580 for ( MatchingRule matchingRule : clone.matchingRuleRegistry ) 2581 { 2582 clone.globalOidRegistry.put( matchingRule ); 2583 } 2584 2585 for ( MatchingRuleUse matchingRuleUse : clone.matchingRuleUseRegistry ) 2586 { 2587 clone.globalOidRegistry.put( matchingRuleUse ); 2588 } 2589 2590 for ( NameForm nameForm : clone.nameFormRegistry ) 2591 { 2592 clone.globalOidRegistry.put( nameForm ); 2593 } 2594 2595 for ( ObjectClass objectClass : clone.objectClassRegistry ) 2596 { 2597 clone.globalOidRegistry.put( objectClass ); 2598 } 2599 2600 for ( LdapSyntax syntax : clone.ldapSyntaxRegistry ) 2601 { 2602 clone.globalOidRegistry.put( syntax ); 2603 } 2604 2605 // Clone the schema list 2606 clone.loadedSchemas = new HashMap<>(); 2607 2608 for ( Map.Entry<String, Set<SchemaObjectWrapper>> entry : schemaObjects.entrySet() ) 2609 { 2610 // We don't clone the schemas 2611 clone.loadedSchemas.put( entry.getKey(), loadedSchemas.get( entry.getKey() ) ); 2612 } 2613 2614 // Clone the Using and usedBy structures 2615 // They will be empty 2616 clone.using = new HashMap<>(); 2617 clone.usedBy = new HashMap<>(); 2618 2619 // Last, rebuild the using and usedBy references 2620 clone.buildReferences(); 2621 2622 // Now, check the registries. We don't care about errors 2623 clone.checkRefInteg(); 2624 2625 clone.schemaObjects = new HashMap<>(); 2626 2627 // Last, not least, clone the SchemaObjects Map, and reference all the copied 2628 // SchemaObjects 2629 for ( Map.Entry<String, Set<SchemaObjectWrapper>> entry : schemaObjects.entrySet() ) 2630 { 2631 Set<SchemaObjectWrapper> objects = new HashSet<>(); 2632 2633 for ( SchemaObjectWrapper schemaObjectWrapper : entry.getValue() ) 2634 { 2635 SchemaObject original = schemaObjectWrapper.get(); 2636 2637 try 2638 { 2639 if ( !( original instanceof LoadableSchemaObject ) ) 2640 { 2641 SchemaObject copy = clone.globalOidRegistry.getSchemaObject( original.getOid() ); 2642 SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( copy ); 2643 objects.add( newWrapper ); 2644 } 2645 else 2646 { 2647 SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( original ); 2648 objects.add( newWrapper ); 2649 } 2650 } 2651 catch ( LdapException ne ) 2652 { 2653 // Nothing to do 2654 } 2655 } 2656 2657 clone.schemaObjects.put( entry.getKey(), objects ); 2658 } 2659 2660 return clone; 2661 } 2662 2663 2664 /** 2665 * Tells if the Registries is permissive or if it must be checked 2666 * against inconsistencies. 2667 * 2668 * @return True if SchemaObjects can be added even if they break the consistency 2669 */ 2670 public boolean isRelaxed() 2671 { 2672 return isRelaxed; 2673 } 2674 2675 2676 /** 2677 * Tells if the Registries is strict. 2678 * 2679 * @return True if SchemaObjects cannot be added if they break the consistency 2680 */ 2681 public boolean isStrict() 2682 { 2683 return !isRelaxed; 2684 } 2685 2686 2687 /** 2688 * Change the Registries to a relaxed mode, where invalid SchemaObjects 2689 * can be registered. 2690 */ 2691 public void setRelaxed() 2692 { 2693 isRelaxed = RELAXED; 2694 globalOidRegistry.setRelaxed(); 2695 attributeTypeRegistry.setRelaxed(); 2696 comparatorRegistry.setRelaxed(); 2697 ditContentRuleRegistry.setRelaxed(); 2698 ditStructureRuleRegistry.setRelaxed(); 2699 ldapSyntaxRegistry.setRelaxed(); 2700 matchingRuleRegistry.setRelaxed(); 2701 matchingRuleUseRegistry.setRelaxed(); 2702 nameFormRegistry.setRelaxed(); 2703 normalizerRegistry.setRelaxed(); 2704 objectClassRegistry.setRelaxed(); 2705 syntaxCheckerRegistry.setRelaxed(); 2706 } 2707 2708 2709 /** 2710 * Change the Registries to a strict mode, where invalid SchemaObjects 2711 * cannot be registered. 2712 */ 2713 public void setStrict() 2714 { 2715 isRelaxed = STRICT; 2716 globalOidRegistry.setStrict(); 2717 attributeTypeRegistry.setStrict(); 2718 comparatorRegistry.setStrict(); 2719 ditContentRuleRegistry.setStrict(); 2720 ditStructureRuleRegistry.setStrict(); 2721 ldapSyntaxRegistry.setStrict(); 2722 matchingRuleRegistry.setStrict(); 2723 matchingRuleUseRegistry.setStrict(); 2724 nameFormRegistry.setStrict(); 2725 normalizerRegistry.setStrict(); 2726 objectClassRegistry.setStrict(); 2727 syntaxCheckerRegistry.setStrict(); 2728 } 2729 2730 2731 /** 2732 * Tells if the Registries accept disabled elements. 2733 * 2734 * @return True if disabled SchemaObjects can be added 2735 */ 2736 public boolean isDisabledAccepted() 2737 { 2738 return disabledAccepted; 2739 } 2740 2741 2742 /** 2743 * Check that we can remove a given SchemaObject without breaking some of its references. 2744 * We will return the list of refereing objects. 2745 * 2746 * @param schemaObject The SchemaObject to remove 2747 * @return The list of SchemaObjects referencing the SchemaObjetc we want to remove 2748 */ 2749 public Set<SchemaObjectWrapper> getReferencing( SchemaObject schemaObject ) 2750 { 2751 SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject ); 2752 2753 return usedBy.get( schemaObjectWrapper ); 2754 } 2755 2756 2757 /** 2758 * Change the Registries behavior regarding disabled SchemaObject element. 2759 * 2760 * @param disabledAccepted If <code>false</code>, then the Registries won't accept 2761 * disabled SchemaObject or enabled SchemaObject from disabled schema 2762 */ 2763 public void setDisabledAccepted( boolean disabledAccepted ) 2764 { 2765 this.disabledAccepted = disabledAccepted; 2766 } 2767 2768 2769 /** 2770 * Clear the registries from all its elements 2771 * 2772 * @throws LdapException If something goes wrong 2773 */ 2774 public void clear() throws LdapException 2775 { 2776 // The AttributeTypeRegistry 2777 if ( attributeTypeRegistry != null ) 2778 { 2779 attributeTypeRegistry.clear(); 2780 } 2781 2782 // The ComparatorRegistry 2783 if ( comparatorRegistry != null ) 2784 { 2785 comparatorRegistry.clear(); 2786 } 2787 2788 // The DitContentRuleRegistry 2789 if ( ditContentRuleRegistry != null ) 2790 { 2791 ditContentRuleRegistry.clear(); 2792 } 2793 2794 // The DitStructureRuleRegistry 2795 if ( ditStructureRuleRegistry != null ) 2796 { 2797 ditStructureRuleRegistry.clear(); 2798 } 2799 2800 // The MatchingRuleRegistry 2801 if ( matchingRuleRegistry != null ) 2802 { 2803 matchingRuleRegistry.clear(); 2804 } 2805 2806 // The MatchingRuleUseRegistry 2807 if ( matchingRuleUseRegistry != null ) 2808 { 2809 matchingRuleUseRegistry.clear(); 2810 } 2811 2812 // The NameFormRegistry 2813 if ( nameFormRegistry != null ) 2814 { 2815 nameFormRegistry.clear(); 2816 } 2817 2818 // The NormalizerRegistry 2819 if ( normalizerRegistry != null ) 2820 { 2821 normalizerRegistry.clear(); 2822 } 2823 2824 // The ObjectClassRegistry 2825 if ( objectClassRegistry != null ) 2826 { 2827 objectClassRegistry.clear(); 2828 } 2829 2830 // The SyntaxRegistry 2831 if ( ldapSyntaxRegistry != null ) 2832 { 2833 ldapSyntaxRegistry.clear(); 2834 } 2835 2836 // The SyntaxCheckerRegistry 2837 if ( syntaxCheckerRegistry != null ) 2838 { 2839 syntaxCheckerRegistry.clear(); 2840 } 2841 2842 // Clear the schemaObjects map 2843 for ( Map.Entry<String, Set<SchemaObjectWrapper>> entry : schemaObjects.entrySet() ) 2844 { 2845 entry.getValue().clear(); 2846 } 2847 2848 schemaObjects.clear(); 2849 2850 // Clear the usedBy map 2851 for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : usedBy.entrySet() ) 2852 { 2853 entry.getValue().clear(); 2854 } 2855 2856 usedBy.clear(); 2857 2858 // Clear the using map 2859 for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : using.entrySet() ) 2860 { 2861 entry.getValue().clear(); 2862 } 2863 2864 using.clear(); 2865 2866 // Clear the global OID registry 2867 globalOidRegistry.clear(); 2868 2869 // Clear the loadedSchema Map 2870 loadedSchemas.clear(); 2871 } 2872 2873 2874 /** 2875 * @see Object#toString() 2876 */ 2877 @Override 2878 public String toString() 2879 { 2880 StringBuilder sb = new StringBuilder(); 2881 2882 sb.append( "Registries [" ); 2883 2884 if ( isRelaxed ) 2885 { 2886 sb.append( "RELAXED," ); 2887 } 2888 else 2889 { 2890 sb.append( "STRICT," ); 2891 } 2892 2893 if ( disabledAccepted ) 2894 { 2895 sb.append( " Disabled accepted] :\n" ); 2896 } 2897 else 2898 { 2899 sb.append( " Disabled forbidden] :\n" ); 2900 } 2901 2902 sb.append( "loaded schemas [" ); 2903 boolean isFirst = true; 2904 2905 for ( String schema : loadedSchemas.keySet() ) 2906 { 2907 if ( isFirst ) 2908 { 2909 isFirst = false; 2910 } 2911 else 2912 { 2913 sb.append( ", " ); 2914 } 2915 2916 sb.append( schema ); 2917 } 2918 2919 sb.append( "]\n" ); 2920 2921 sb.append( "AttributeTypes : " ).append( attributeTypeRegistry.size() ).append( "\n" ); 2922 sb.append( "Comparators : " ).append( comparatorRegistry.size() ).append( "\n" ); 2923 sb.append( "DitContentRules : " ).append( ditContentRuleRegistry.size() ).append( "\n" ); 2924 sb.append( "DitStructureRules : " ).append( ditStructureRuleRegistry.size() ).append( "\n" ); 2925 sb.append( "MatchingRules : " ).append( matchingRuleRegistry.size() ).append( "\n" ); 2926 sb.append( "MatchingRuleUses : " ).append( matchingRuleUseRegistry.size() ).append( "\n" ); 2927 sb.append( "NameForms : " ).append( nameFormRegistry.size() ).append( "\n" ); 2928 sb.append( "Normalizers : " ).append( normalizerRegistry.size() ).append( "\n" ); 2929 sb.append( "ObjectClasses : " ).append( objectClassRegistry.size() ).append( "\n" ); 2930 sb.append( "Syntaxes : " ).append( ldapSyntaxRegistry.size() ).append( "\n" ); 2931 sb.append( "SyntaxCheckers : " ).append( syntaxCheckerRegistry.size() ).append( "\n" ); 2932 2933 sb.append( "GlobalOidRegistry : " ).append( globalOidRegistry.size() ).append( '\n' ); 2934 2935 return sb.toString(); 2936 } 2937}