001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.types; 028 029import java.io.*; 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.HashMap; 033import java.util.LinkedHashSet; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.Map; 037import java.util.Set; 038import java.util.TreeSet; 039import java.util.concurrent.ConcurrentHashMap; 040 041import org.forgerock.i18n.LocalizableMessage; 042import org.forgerock.i18n.slf4j.LocalizedLogger; 043import org.forgerock.opendj.ldap.ByteString; 044import org.forgerock.opendj.ldap.ModificationType; 045import org.forgerock.opendj.ldap.ResultCode; 046import org.forgerock.opendj.ldap.schema.MatchingRule; 047import org.forgerock.opendj.ldap.schema.Syntax; 048import org.opends.server.core.DirectoryServer; 049import org.opends.server.core.SchemaConfigManager; 050import org.opends.server.schema.AttributeTypeSyntax; 051import org.opends.server.schema.DITContentRuleSyntax; 052import org.opends.server.schema.DITStructureRuleSyntax; 053import org.opends.server.schema.MatchingRuleUseSyntax; 054import org.opends.server.schema.NameFormSyntax; 055import org.opends.server.schema.ObjectClassSyntax; 056import org.opends.server.util.StaticUtils; 057 058import static org.opends.messages.BackendMessages.*; 059import static org.opends.messages.CoreMessages.*; 060import static org.opends.server.config.ConfigConstants.*; 061import static org.opends.server.types.CommonSchemaElements.*; 062import static org.opends.server.util.CollectionUtils.*; 063import static org.opends.server.util.ServerConstants.*; 064import static org.opends.server.util.StaticUtils.*; 065 066/** 067 * This class defines a data structure that holds information about 068 * the components of the Directory Server schema. It includes the 069 * following kinds of elements: 070 * 071 * <UL> 072 * <LI>Attribute type definitions</LI> 073 * <LI>Objectclass definitions</LI> 074 * <LI>Attribute syntax definitions</LI> 075 * <LI>Matching rule definitions</LI> 076 * <LI>Matching rule use definitions</LI> 077 * <LI>DIT content rule definitions</LI> 078 * <LI>DIT structure rule definitions</LI> 079 * <LI>Name form definitions</LI> 080 * </UL> 081 */ 082@org.opends.server.types.PublicAPI( 083 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 084 mayInstantiate=false, 085 mayExtend=false, 086 mayInvoke=true) 087public final class Schema 088{ 089 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 090 091 /** 092 * The set of subordinate attribute types registered within the server schema. 093 */ 094 private ConcurrentHashMap<AttributeType,List<AttributeType>> 095 subordinateTypes; 096 097 /** 098 * The set of attribute type definitions for this schema, mapped between the 099 * lowercase names and OID for the definition and the attribute type itself. 100 */ 101 private ConcurrentHashMap<String,AttributeType> attributeTypes; 102 103 /** 104 * The set of objectclass definitions for this schema, mapped between the 105 * lowercase names and OID for the definition and the objectclass itself. 106 */ 107 private ConcurrentHashMap<String,ObjectClass> objectClasses; 108 109 /** 110 * The set of attribute syntaxes for this schema, mapped between the OID for 111 * the syntax and the syntax itself. 112 */ 113 private ConcurrentHashMap<String,Syntax> syntaxes; 114 115 /** 116 * The default attribute syntax to use for attributes with no defined syntax. 117 */ 118 private Syntax defaultSyntax; 119 120 /** 121 * The entire set of matching rules for this schema, mapped between the 122 * lowercase names and OID for the definition and the matching rule itself. 123 */ 124 private ConcurrentHashMap<String,MatchingRule> matchingRules; 125 126 127 128 /** 129 * The set of matching rule uses for this schema, mapped between the matching 130 * rule for the definition and the matching rule use itself. 131 */ 132 private ConcurrentHashMap<MatchingRule,MatchingRuleUse> 133 matchingRuleUses; 134 135 /** 136 * The set of DIT content rules for this schema, mapped between the structural 137 * objectclass for the definition and the DIT content rule itself. 138 */ 139 private ConcurrentHashMap<ObjectClass,DITContentRule> 140 ditContentRules; 141 142 /** 143 * The set of DIT structure rules for this schema, mapped between the name 144 * form for the definition and the DIT structure rule itself. 145 */ 146 private ConcurrentHashMap<Integer,DITStructureRule> 147 ditStructureRulesByID; 148 149 /** 150 * The set of DIT structure rules for this schema, mapped between the name 151 * form for the definition and the DIT structure rule itself. 152 */ 153 private ConcurrentHashMap<NameForm,DITStructureRule> 154 ditStructureRulesByNameForm; 155 156 /** 157 * The set of name forms for this schema, mapped between the structural 158 * objectclass for the definition and the list of name forms. 159 */ 160 private ConcurrentHashMap<ObjectClass,List<NameForm>> 161 nameFormsByOC; 162 163 /** 164 * The set of name forms for this schema, mapped between the names/OID and the 165 * name form itself. 166 */ 167 private ConcurrentHashMap<String,NameForm> nameFormsByName; 168 169 /** 170 * The set of ldap syntax descriptions for this schema, mapped the OID and the 171 * ldap syntax description itself. 172 */ 173 private ConcurrentHashMap<String,LDAPSyntaxDescription> 174 ldapSyntaxDescriptions; 175 176 /** The oldest modification timestamp for any schema configuration file. */ 177 private long oldestModificationTime; 178 179 /** The youngest modification timestamp for any schema configuration file. */ 180 private long youngestModificationTime; 181 182 /** 183 * A set of extra attributes that are not used directly by the schema but may 184 * be used by other component to store information in the schema. 185 * <p> 186 * ex : Replication uses this to store its state and GenerationID. 187 */ 188 private Map<String, Attribute> extraAttributes = new HashMap<>(); 189 190 191 /** Creates a new schema structure with all elements initialized but empty. */ 192 public Schema() 193 { 194 attributeTypes = new ConcurrentHashMap<>(); 195 objectClasses = new ConcurrentHashMap<>(); 196 syntaxes = new ConcurrentHashMap<>(); 197 matchingRules = new ConcurrentHashMap<>(); 198 matchingRuleUses = new ConcurrentHashMap<>(); 199 ditContentRules = new ConcurrentHashMap<>(); 200 ditStructureRulesByID = new ConcurrentHashMap<>(); 201 ditStructureRulesByNameForm = new ConcurrentHashMap<>(); 202 nameFormsByOC = new ConcurrentHashMap<>(); 203 nameFormsByName = new ConcurrentHashMap<>(); 204 ldapSyntaxDescriptions = new ConcurrentHashMap<>(); 205 subordinateTypes = new ConcurrentHashMap<>(); 206 207 oldestModificationTime = System.currentTimeMillis(); 208 youngestModificationTime = oldestModificationTime; 209 } 210 211 212 213 /** 214 * Retrieves the attribute type definitions for this schema, as a 215 * mapping between the lowercase names and OIDs for the attribute 216 * type and the attribute type itself. Each attribute type may be 217 * associated with multiple keys (once for the OID and again for 218 * each name). The contents of the returned mapping must not be 219 * altered. 220 * 221 * @return The attribute type definitions for this schema. 222 */ 223 public ConcurrentHashMap<String,AttributeType> getAttributeTypes() 224 { 225 return attributeTypes; 226 } 227 228 229 230 /** 231 * Indicates whether this schema definition includes an attribute 232 * type with the provided name or OID. 233 * 234 * @param lowerName The name or OID for which to make the 235 * determination, formatted in all lowercase 236 * characters. 237 * 238 * @return {@code true} if this schema contains an attribute type 239 * with the provided name or OID, or {@code false} if not. 240 */ 241 public boolean hasAttributeType(String lowerName) 242 { 243 return attributeTypes.containsKey(lowerName); 244 } 245 246 247 248 /** 249 * Retrieves the attribute type definition with the specified name 250 * or OID. 251 * 252 * @param lowerName The name or OID of the attribute type to 253 * retrieve, formatted in all lowercase 254 * characters. 255 * 256 * @return The requested attribute type, or <CODE>null</CODE> if no 257 * type is registered with the provided name or OID. 258 */ 259 public AttributeType getAttributeType(String lowerName) 260 { 261 return attributeTypes.get(lowerName); 262 } 263 264 265 266 /** 267 * Registers the provided attribute type definition with this 268 * schema. 269 * 270 * @param attributeType The attribute type to register with 271 * this schema. 272 * @param overwriteExisting Indicates whether to overwrite an 273 * existing mapping if there are any 274 * conflicts (i.e., another attribute 275 * type with the same OID or name). 276 * 277 * @throws DirectoryException If a conflict is encountered and the 278 * <CODE>overwriteExisting</CODE> flag 279 * is set to <CODE>false</CODE> 280 */ 281 public void registerAttributeType(AttributeType attributeType, 282 boolean overwriteExisting) 283 throws DirectoryException 284 { 285 synchronized (attributeTypes) 286 { 287 if (! overwriteExisting) 288 { 289 String oid = toLowerCase(attributeType.getOID()); 290 if (attributeTypes.containsKey(oid)) 291 { 292 AttributeType conflictingType = attributeTypes.get(oid); 293 294 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_OID. 295 get(attributeType.getNameOrOID(), oid, 296 conflictingType.getNameOrOID()); 297 throw new DirectoryException( 298 ResultCode.CONSTRAINT_VIOLATION, message); 299 } 300 301 for (String name : attributeType.getNormalizedNames()) 302 { 303 if (attributeTypes.containsKey(name)) 304 { 305 AttributeType conflictingType = attributeTypes.get(name); 306 307 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_NAME. 308 get(attributeType.getNameOrOID(), name, 309 conflictingType.getNameOrOID()); 310 throw new DirectoryException( 311 ResultCode.CONSTRAINT_VIOLATION, message); 312 } 313 } 314 } 315 316 AttributeType old = attributeTypes.put( 317 toLowerCase(attributeType.getOID()), attributeType); 318 if (old != null && old != attributeType) 319 { 320 // Mark the old attribute type as stale so that caches (such as 321 // compressed schema) can detect changes. 322 old.setDirty(); 323 } 324 325 for (String name : attributeType.getNormalizedNames()) 326 { 327 attributeTypes.put(name, attributeType); 328 } 329 330 AttributeType superiorType = attributeType.getSuperiorType(); 331 if (superiorType != null) 332 { 333 registerSubordinateType(attributeType, superiorType); 334 } 335 } 336 } 337 338 /** 339 * Deregisters the provided attribute type definition with this 340 * schema. 341 * 342 * @param attributeType The attribute type to deregister with this 343 * schema. 344 */ 345 public void deregisterAttributeType(AttributeType attributeType) 346 { 347 synchronized (attributeTypes) 348 { 349 if (attributeTypes.remove(toLowerCase(attributeType.getOID()), 350 attributeType)) 351 { 352 // Mark the old attribute type as stale so that caches (such as 353 // compressed schema) can detect changes. 354 attributeType.setDirty(); 355 } 356 357 for (String name : attributeType.getNormalizedNames()) 358 { 359 attributeTypes.remove(name, attributeType); 360 } 361 362 AttributeType superiorType = attributeType.getSuperiorType(); 363 if (superiorType != null) 364 { 365 deregisterSubordinateType(attributeType, superiorType); 366 } 367 } 368 } 369 370 371 /** 372 * Registers the provided attribute type as a subtype of the given 373 * superior attribute type, recursively following any additional 374 * elements in the superior chain. 375 * 376 * @param attributeType The attribute type to be registered as a 377 * subtype for the given superior type. 378 * @param superiorType The superior type for which to register 379 * the given attribute type as a subtype. 380 */ 381 private void registerSubordinateType(AttributeType attributeType, 382 AttributeType superiorType) 383 { 384 List<AttributeType> subTypes = subordinateTypes.get(superiorType); 385 if (subTypes == null) 386 { 387 superiorType.setMayHaveSubordinateTypes(); 388 subordinateTypes.put(superiorType, newLinkedList(attributeType)); 389 } 390 else if (! subTypes.contains(attributeType)) 391 { 392 superiorType.setMayHaveSubordinateTypes(); 393 subTypes.add(attributeType); 394 395 AttributeType higherSuperior = superiorType.getSuperiorType(); 396 if (higherSuperior != null) 397 { 398 registerSubordinateType(attributeType, higherSuperior); 399 } 400 } 401 } 402 403 404 405 /** 406 * Deregisters the provided attribute type as a subtype of the given 407 * superior attribute type, recursively following any additional 408 * elements in the superior chain. 409 * 410 * @param attributeType The attribute type to be deregistered as a 411 * subtype for the given superior type. 412 * @param superiorType The superior type for which to deregister 413 * the given attribute type as a subtype. 414 */ 415 private void deregisterSubordinateType(AttributeType attributeType, 416 AttributeType superiorType) 417 { 418 List<AttributeType> subTypes = subordinateTypes.get(superiorType); 419 if (subTypes != null && subTypes.remove(attributeType)) 420 { 421 AttributeType higherSuperior = superiorType.getSuperiorType(); 422 if (higherSuperior != null) 423 { 424 deregisterSubordinateType(attributeType, higherSuperior); 425 } 426 } 427 } 428 429 430 431 /** 432 * Retrieves the set of subtypes registered for the given attribute 433 * type. 434 * 435 * @param attributeType The attribute type for which to retrieve 436 * the set of registered subtypes. 437 * 438 * @return The set of subtypes registered for the given attribute 439 * type, or an empty set if there are no subtypes 440 * registered for the attribute type. 441 */ 442 public List<AttributeType> getSubTypes(AttributeType attributeType) 443 { 444 List<AttributeType> subTypes = subordinateTypes.get(attributeType); 445 if (subTypes == null) 446 { 447 return Collections.emptyList(); 448 } 449 else 450 { 451 return subTypes; 452 } 453 } 454 455 456 457 /** 458 * Retrieves the objectclass definitions for this schema, as a 459 * mapping between the lowercase names and OIDs for the objectclass 460 * and the objectclass itself. Each objectclass may be associated 461 * with multiple keys (once for the OID and again for each name). 462 * The contents of the returned mapping must not be altered. 463 * 464 * @return The objectclass definitions for this schema. 465 */ 466 public ConcurrentHashMap<String,ObjectClass> getObjectClasses() 467 { 468 return objectClasses; 469 } 470 471 472 473 /** 474 * Indicates whether this schema definition includes an objectclass 475 * with the provided name or OID. 476 * 477 * @param lowerName The name or OID for which to make the 478 * determination, formatted in all lowercase 479 * characters. 480 * 481 * @return {@code true} if this schema contains an objectclass with 482 * the provided name or OID, or {@code false} if not. 483 */ 484 public boolean hasObjectClass(String lowerName) 485 { 486 return objectClasses.containsKey(lowerName); 487 } 488 489 490 491 /** 492 * Retrieves the objectclass definition with the specified name or 493 * OID. 494 * 495 * @param lowerName The name or OID of the objectclass to 496 * retrieve, formatted in all lowercase 497 * characters. 498 * 499 * @return The requested objectclass, or <CODE>null</CODE> if no 500 * class is registered with the provided name or OID. 501 */ 502 public ObjectClass getObjectClass(String lowerName) 503 { 504 return objectClasses.get(lowerName); 505 } 506 507 508 509 /** 510 * Registers the provided objectclass definition with this schema. 511 * 512 * @param objectClass The objectclass to register with this 513 * schema. 514 * @param overwriteExisting Indicates whether to overwrite an 515 * existing mapping if there are any 516 * conflicts (i.e., another objectclass 517 * with the same OID or name). 518 * 519 * @throws DirectoryException If a conflict is encountered and the 520 * <CODE>overwriteExisting</CODE> flag 521 * is set to <CODE>false</CODE>. 522 */ 523 public void registerObjectClass(ObjectClass objectClass, 524 boolean overwriteExisting) 525 throws DirectoryException 526 { 527 synchronized (objectClasses) 528 { 529 if (! overwriteExisting) 530 { 531 String oid = toLowerCase(objectClass.getOID()); 532 if (objectClasses.containsKey(oid)) 533 { 534 ObjectClass conflictingClass = objectClasses.get(oid); 535 536 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_OID. 537 get(objectClass.getNameOrOID(), oid, 538 conflictingClass.getNameOrOID()); 539 throw new DirectoryException( 540 ResultCode.CONSTRAINT_VIOLATION, message); 541 } 542 543 for (String name : objectClass.getNormalizedNames()) 544 { 545 if (objectClasses.containsKey(name)) 546 { 547 ObjectClass conflictingClass = objectClasses.get(name); 548 549 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_NAME. 550 get(objectClass.getNameOrOID(), name, 551 conflictingClass.getNameOrOID()); 552 throw new DirectoryException( 553 ResultCode.CONSTRAINT_VIOLATION, message); 554 } 555 } 556 } 557 558 ObjectClass old = objectClasses.put(toLowerCase(objectClass.getOID()), 559 objectClass); 560 if (old != null && old != objectClass) 561 { 562 // Mark the old object class as stale so that caches (such as compressed 563 // schema) can detect changes. 564 old.setDirty(); 565 } 566 567 for (String name : objectClass.getNormalizedNames()) 568 { 569 objectClasses.put(name, objectClass); 570 } 571 } 572 } 573 574 575 576 /** 577 * Deregisters the provided objectclass definition with this schema. 578 * 579 * @param objectClass The objectclass to deregister with this 580 * schema. 581 */ 582 public void deregisterObjectClass(ObjectClass objectClass) 583 { 584 synchronized (objectClasses) 585 { 586 if (objectClasses.remove(toLowerCase(objectClass.getOID()), objectClass)) 587 { 588 // Mark the old object class as stale so that caches (such as 589 // compressed schema) can detect changes. 590 objectClass.setDirty(); 591 } 592 593 for (String name : objectClass.getNormalizedNames()) 594 { 595 objectClasses.remove(name, objectClass); 596 } 597 } 598 } 599 600 601 602 /** 603 * Retrieves the attribute syntax definitions for this schema, as a 604 * mapping between the OID for the syntax and the syntax itself. 605 * Each syntax should only be present once, since its only key is 606 * its OID. The contents of the returned mapping must not be 607 * altered. 608 * 609 * @return The attribute syntax definitions for this schema. 610 */ 611 public ConcurrentHashMap<String,Syntax> getSyntaxes() 612 { 613 return syntaxes; 614 } 615 616 617 618 /** 619 * Indicates whether this schema definition includes an attribute 620 * syntax with the provided name or OID. 621 * 622 * @param lowerName The name or OID for which to make the 623 * determination, formatted in all lowercase 624 * characters. 625 * 626 * @return {@code true} if this schema contains an attribute syntax 627 * with the provided name or OID, or {@code false} if not. 628 */ 629 public boolean hasSyntax(String lowerName) 630 { 631 return syntaxes.containsKey(lowerName); 632 } 633 634 /** 635 * Retrieves the requested attribute syntax. 636 * 637 * @param oid 638 * The OID of the syntax to retrieve. 639 * @param allowDefault 640 * Indicates whether to return the default attribute syntax if the 641 * requested syntax is unknown. 642 * @return The requested attribute syntax, the default syntax if the requested 643 * syntax is unknown and the caller has indicated that the default is 644 * acceptable, or <CODE>null</CODE> otherwise. 645 */ 646 public Syntax getSyntax(String oid, boolean allowDefault) 647 { 648 Syntax syntax = getSyntax(oid); 649 if (syntax == null && allowDefault) 650 { 651 return getDefaultSyntax(); 652 } 653 654 return syntax; 655 } 656 657 /** 658 * Retrieves the attribute syntax definition with the OID. 659 * 660 * @param lowerName The OID of the attribute syntax to retrieve, 661 * formatted in all lowercase characters. 662 * 663 * @return The requested attribute syntax, or <CODE>null</CODE> if 664 * no syntax is registered with the provided OID. 665 */ 666 public Syntax getSyntax(String lowerName) 667 { 668 return syntaxes.get(lowerName); 669 } 670 671 /** 672 * Retrieves the default attribute syntax that should be used for attributes 673 * that are not defined in the server schema. 674 * 675 * @return The default attribute syntax that should be used for attributes 676 * that are not defined in the server schema. 677 */ 678 public Syntax getDefaultSyntax() 679 { 680 return defaultSyntax; 681 } 682 683 684 685 /** 686 * Registers the defaut syntax for this schema. 687 * 688 * @param defaultSyntax 689 * The defautl syntax to use. 690 */ 691 public void registerDefaultSyntax( 692 Syntax defaultSyntax) 693 { 694 this.defaultSyntax = defaultSyntax; 695 } 696 697 698 699 /** 700 * Registers the provided attribute syntax definition with this 701 * schema. 702 * 703 * @param syntax The attribute syntax to register with 704 * this schema. 705 * @param overwriteExisting Indicates whether to overwrite an 706 * existing mapping if there are any 707 * conflicts (i.e., another attribute 708 * syntax with the same OID). 709 * 710 * @throws DirectoryException If a conflict is encountered and the 711 * <CODE>overwriteExisting</CODE> flag 712 * is set to <CODE>false</CODE> 713 */ 714 public void registerSyntax(Syntax syntax, 715 boolean overwriteExisting) 716 throws DirectoryException 717 { 718 synchronized (syntaxes) 719 { 720 if (! overwriteExisting) 721 { 722 String oid = toLowerCase(syntax.getOID()); 723 if (syntaxes.containsKey(oid)) 724 { 725 Syntax conflictingSyntax = syntaxes.get(oid); 726 727 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_SYNTAX_OID. 728 get(syntax.getName(), oid, 729 conflictingSyntax.getName()); 730 throw new DirectoryException( 731 ResultCode.CONSTRAINT_VIOLATION, message); 732 } 733 } 734 735 syntaxes.put(toLowerCase(syntax.getOID()), syntax); 736 } 737 } 738 739 740 741 /** 742 * Deregisters the provided attribute syntax definition with this 743 * schema. 744 * 745 * @param syntax The attribute syntax to deregister with this 746 * schema. 747 */ 748 public void deregisterSyntax(Syntax syntax) 749 { 750 synchronized (syntaxes) 751 { 752 syntaxes.remove(toLowerCase(syntax.getOID()), syntax); 753 } 754 } 755 756 757 758 /** 759 * Retrieves the ldap syntax definitions for this schema, as a 760 * mapping between the OID for the syntax and the ldap syntax 761 * definition itself. Each ldap syntax should only be present once, 762 * since its only key is its OID. The contents of the returned 763 * mapping must not be altered. 764 * 765 * @return The ldap syntax definitions for this schema. 766 */ 767 public ConcurrentHashMap<String,LDAPSyntaxDescription> 768 getLdapSyntaxDescriptions() 769 { 770 return ldapSyntaxDescriptions; 771 } 772 773 774 775 /** 776 * Indicates whether this schema definition includes an ldap 777 * syntax description with the provided name or OID. 778 * 779 * @param lowerName The OID for which to make the 780 * determination, formatted in all lowercase 781 * characters. 782 * 783 * @return {@code true} if this schema contains an ldap syntax 784 * with the provided name or OID, or {@code false} if not. 785 */ 786 public boolean hasLdapSyntaxDescription(String lowerName) 787 { 788 return ldapSyntaxDescriptions.containsKey(lowerName); 789 } 790 791 792 793 /** 794 * Retrieves the ldap syntax definition with the OID. 795 * 796 * @param lowerName The OID of the ldap syntax to retrieve, 797 * formatted in all lowercase characters. 798 * 799 * @return The requested ldap syntax, or <CODE>null</CODE> if 800 * no syntax is registered with the provided OID. 801 */ 802 public LDAPSyntaxDescription getLdapSyntaxDescription( 803 String lowerName) 804 { 805 return ldapSyntaxDescriptions.get(lowerName); 806 } 807 808 809 810 /** 811 * Registers the provided ldap syntax description with this 812 * schema. 813 * 814 * @param syntax The ldap syntax description to register 815 * with this schema. 816 * @param overwriteExisting Indicates whether to overwrite an 817 * existing mapping if there are any 818 * conflicts (i.e., another ldap 819 * syntax with the same OID). 820 * 821 * @throws DirectoryException If a conflict is encountered and 822 * <CODE>overwriteExisting</CODE> flag 823 * is set to <CODE>false</CODE> 824 */ 825 public void registerLdapSyntaxDescription( 826 LDAPSyntaxDescription syntax, 827 boolean overwriteExisting) 828 throws DirectoryException 829 { 830 /** 831 * ldapsyntaxes is part real and part virtual. For any 832 * ldapsyntaxes attribute this is real, an LDAPSyntaxDescription 833 * object is created and stored with the schema. Also, the 834 * associated LDAPSyntaxDescriptionSyntax is added into the 835 * virtual syntax set to make this available through virtual 836 * ldapsyntaxes attribute. 837 */ 838 synchronized (ldapSyntaxDescriptions) 839 { 840 String oid = toLowerCase(syntax.getSyntax().getOID()); 841 if (! overwriteExisting && ldapSyntaxDescriptions.containsKey(oid)) 842 { 843 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 844 ERR_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_LDAP_SYNTAX.get(oid)); 845 } 846 847 ldapSyntaxDescriptions.put(oid, syntax); 848 849 //Register the attribute syntax with the schema. It will ensure 850 // syntax is available along with the other virtual values for 851 // ldapsyntaxes. 852 registerSyntax(syntax.getSyntax(), overwriteExisting); 853 } 854 } 855 856 857 858 /** 859 * Deregisters the provided ldap syntax description with this 860 * schema. 861 * 862 * @param syntax The ldap syntax to deregister with this 863 * schema. 864 */ 865 public void deregisterLdapSyntaxDescription( 866 LDAPSyntaxDescription syntax) 867 { 868 synchronized (ldapSyntaxDescriptions) 869 { 870 //Remove the real value. 871 ldapSyntaxDescriptions.remove( 872 toLowerCase(syntax.getSyntax().getOID()), 873 syntax); 874 875 try 876 { 877 //Get rid of this from the virtual ldapsyntaxes. 878 deregisterSyntax(syntax.getSyntax()); 879 } 880 catch (Exception e) 881 { 882 deregisterSyntax(syntax.getSyntax()); 883 } 884 } 885 } 886 887 888 889 /** 890 * Retrieves the entire set of matching rule definitions for this 891 * schema, as a mapping between the lowercase names and OIDs for the 892 * matching rule and the matching rule itself. Each matching rule 893 * may be associated with multiple keys (once for the OID and again 894 * for each name). This should be a superset of the sets of 895 * approximate, equality, ordering, and substring matching rules. 896 * The contents of the returned mapping must not be altered. 897 * 898 * @return The matching rule definitions for this schema. 899 */ 900 public ConcurrentHashMap<String,MatchingRule> getMatchingRules() 901 { 902 return matchingRules; 903 } 904 905 906 907 /** 908 * Indicates whether this schema definition includes a matching rule 909 * with the provided name or OID. 910 * 911 * @param lowerName The name or OID for which to make the 912 * determination, formatted in all lowercase 913 * characters. 914 * 915 * @return {@code true} if this schema contains a matching rule 916 * with the provided name or OID, or {@code false} if not. 917 */ 918 public boolean hasMatchingRule(String lowerName) 919 { 920 return matchingRules.containsKey(lowerName); 921 } 922 923 924 925 /** 926 * Retrieves the matching rule definition with the specified name or 927 * OID. 928 * 929 * @param lowerName The name or OID of the matching rule to 930 * retrieve, formatted in all lowercase 931 * characters. 932 * 933 * @return The requested matching rule, or <CODE>null</CODE> if no 934 * rule is registered with the provided name or OID. 935 */ 936 public MatchingRule getMatchingRule(String lowerName) 937 { 938 return matchingRules.get(lowerName); 939 } 940 941 942 943 /** 944 * Registers the provided matching rule definition with this schema. 945 * 946 * @param matchingRule The matching rule to register with 947 * this schema. 948 * @param overwriteExisting Indicates whether to overwrite an 949 * existing mapping if there are any 950 * conflicts (i.e., 951 * another matching rule with the same 952 * OID or name). 953 * 954 * @throws DirectoryException If a conflict is encountered and the 955 * <CODE>overwriteExisting</CODE> flag 956 * is set to <CODE>false</CODE> 957 */ 958 public void registerMatchingRule(MatchingRule matchingRule, boolean overwriteExisting) 959 throws DirectoryException 960 { 961 synchronized (matchingRules) 962 { 963 if (!overwriteExisting) 964 { 965 String oid = toLowerCase(matchingRule.getOID()); 966 if (matchingRules.containsKey(oid)) 967 { 968 MatchingRule conflictingRule = matchingRules.get(oid); 969 970 LocalizableMessage message = 971 ERR_SCHEMA_CONFLICTING_MR_OID.get(matchingRule.getNameOrOID(), 972 oid, conflictingRule.getNameOrOID()); 973 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 974 } 975 976 for (String name : matchingRule.getNames()) 977 { 978 if (name != null) 979 { 980 name = toLowerCase(name); 981 if (matchingRules.containsKey(name)) 982 { 983 MatchingRule conflictingRule = matchingRules.get(name); 984 985 LocalizableMessage message = 986 ERR_SCHEMA_CONFLICTING_MR_NAME.get(matchingRule.getOID(), 987 name, conflictingRule.getOID()); 988 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 989 message); 990 } 991 } 992 } 993 } 994 matchingRules.put(toLowerCase(matchingRule.getOID()), matchingRule); 995 996 for (String name : matchingRule.getNames()) 997 { 998 if (name != null) 999 { 1000 matchingRules.put(toLowerCase(name), matchingRule); 1001 } 1002 } 1003 } 1004 } 1005 1006 1007 /** 1008 * Deregisters the provided matching rule definition with this 1009 * schema. 1010 * 1011 * @param matchingRule The matching rule to deregister with this 1012 * schema. 1013 */ 1014 public void deregisterMatchingRule(MatchingRule matchingRule) 1015 { 1016 synchronized (matchingRules) 1017 { 1018 matchingRules.remove(toLowerCase(matchingRule.getOID()), matchingRule); 1019 1020 for (String name : matchingRule.getNames()) 1021 { 1022 if (name != null) 1023 { 1024 matchingRules.remove(toLowerCase(name), matchingRule); 1025 } 1026 } 1027 } 1028 } 1029 1030 1031 /** 1032 * Retrieves the matching rule use definitions for this schema, as a 1033 * mapping between the matching rule for the matching rule use 1034 * definition and the matching rule use itself. Each matching rule 1035 * use should only be present once, since its only key is its 1036 * matching rule. The contents of the returned mapping must not be 1037 * altered. 1038 * 1039 * @return The matching rule use definitions for this schema. 1040 */ 1041 public ConcurrentHashMap<MatchingRule,MatchingRuleUse> 1042 getMatchingRuleUses() 1043 { 1044 return matchingRuleUses; 1045 } 1046 1047 1048 1049 /** 1050 * Indicates whether this schema definition includes a matching rule 1051 * use for the provided matching rule. 1052 * 1053 * @param matchingRule The matching rule for which to make the 1054 * determination. 1055 * 1056 * @return {@code true} if this schema contains a matching rule use 1057 * for the provided matching rule, or {@code false} if not. 1058 */ 1059 public boolean hasMatchingRuleUse(MatchingRule matchingRule) 1060 { 1061 return matchingRuleUses.containsKey(matchingRule); 1062 } 1063 1064 1065 1066 /** 1067 * Retrieves the matching rule use definition for the specified 1068 * matching rule. 1069 * 1070 * @param matchingRule The matching rule for which to retrieve the 1071 * matching rule use definition. 1072 * 1073 * @return The matching rule use definition, or <CODE>null</CODE> 1074 * if none exists for the specified matching rule. 1075 */ 1076 public MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule) 1077 { 1078 return matchingRuleUses.get(matchingRule); 1079 } 1080 1081 1082 1083 /** 1084 * Registers the provided matching rule use definition with this 1085 * schema. 1086 * 1087 * @param matchingRuleUse The matching rule use definition to 1088 * register. 1089 * @param overwriteExisting Indicates whether to overwrite an 1090 * existing mapping if there are any 1091 * conflicts (i.e., another matching rule 1092 * use with the same matching rule). 1093 * 1094 * @throws DirectoryException If a conflict is encountered and the 1095 * <CODE>overwriteExisting</CODE> flag 1096 * is set to <CODE>false</CODE> 1097 */ 1098 public void registerMatchingRuleUse(MatchingRuleUse matchingRuleUse, 1099 boolean overwriteExisting) 1100 throws DirectoryException 1101 { 1102 synchronized (matchingRuleUses) 1103 { 1104 MatchingRule matchingRule = matchingRuleUse.getMatchingRule(); 1105 1106 if (!overwriteExisting && matchingRuleUses.containsKey(matchingRule)) 1107 { 1108 MatchingRuleUse conflictingUse = matchingRuleUses.get(matchingRule); 1109 1110 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_MATCHING_RULE_USE. 1111 get(matchingRuleUse.getNameOrOID(), 1112 matchingRule.getNameOrOID(), 1113 conflictingUse.getNameOrOID()); 1114 throw new DirectoryException( 1115 ResultCode.CONSTRAINT_VIOLATION, message); 1116 } 1117 1118 matchingRuleUses.put(matchingRule, matchingRuleUse); 1119 } 1120 } 1121 1122 1123 1124 /** 1125 * Deregisters the provided matching rule use definition with this 1126 * schema. 1127 * 1128 * @param matchingRuleUse The matching rule use to deregister with 1129 * this schema. 1130 */ 1131 public void deregisterMatchingRuleUse( 1132 MatchingRuleUse matchingRuleUse) 1133 { 1134 synchronized (matchingRuleUses) 1135 { 1136 matchingRuleUses.remove(matchingRuleUse.getMatchingRule(), 1137 matchingRuleUse); 1138 } 1139 } 1140 1141 1142 1143 /** 1144 * Retrieves the DIT content rule definitions for this schema, as a 1145 * mapping between the objectclass for the rule and the DIT content 1146 * rule itself. Each DIT content rule should only be present once, 1147 * since its only key is its objectclass. The contents of the 1148 * returned mapping must not be altered. 1149 * 1150 * @return The DIT content rule definitions for this schema. 1151 */ 1152 public ConcurrentHashMap<ObjectClass,DITContentRule> 1153 getDITContentRules() 1154 { 1155 return ditContentRules; 1156 } 1157 1158 1159 1160 /** 1161 * Indicates whether this schema definition includes a DIT content 1162 * rule for the provided objectclass. 1163 * 1164 * @param objectClass The objectclass for which to make the 1165 * determination. 1166 * 1167 * @return {@code true} if this schema contains a DIT content rule 1168 * for the provided objectclass, or {@code false} if not. 1169 */ 1170 public boolean hasDITContentRule(ObjectClass objectClass) 1171 { 1172 return ditContentRules.containsKey(objectClass); 1173 } 1174 1175 1176 1177 /** 1178 * Retrieves the DIT content rule definition for the specified 1179 * objectclass. 1180 * 1181 * @param objectClass The objectclass for the DIT content rule to 1182 * retrieve. 1183 * 1184 * @return The requested DIT content rule, or <CODE>null</CODE> if 1185 * no DIT content rule is registered with the provided 1186 * objectclass. 1187 */ 1188 public DITContentRule getDITContentRule(ObjectClass objectClass) 1189 { 1190 return ditContentRules.get(objectClass); 1191 } 1192 1193 1194 1195 /** 1196 * Registers the provided DIT content rule definition with this 1197 * schema. 1198 * 1199 * @param ditContentRule The DIT content rule to register. 1200 * @param overwriteExisting Indicates whether to overwrite an 1201 * existing mapping if there are any 1202 * conflicts (i.e., another DIT content 1203 * rule with the same objectclass). 1204 * 1205 * @throws DirectoryException If a conflict is encountered and the 1206 * <CODE>overwriteExisting</CODE> flag 1207 * is set to <CODE>false</CODE> 1208 */ 1209 public void registerDITContentRule(DITContentRule ditContentRule, 1210 boolean overwriteExisting) 1211 throws DirectoryException 1212 { 1213 synchronized (ditContentRules) 1214 { 1215 ObjectClass objectClass = ditContentRule.getStructuralClass(); 1216 1217 if (! overwriteExisting && ditContentRules.containsKey(objectClass)) 1218 { 1219 DITContentRule conflictingRule = 1220 ditContentRules.get(objectClass); 1221 1222 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_DIT_CONTENT_RULE. 1223 get(ditContentRule.getNameOrOID(), 1224 objectClass.getNameOrOID(), 1225 conflictingRule.getNameOrOID()); 1226 throw new DirectoryException( 1227 ResultCode.CONSTRAINT_VIOLATION, message); 1228 } 1229 1230 ditContentRules.put(objectClass, ditContentRule); 1231 } 1232 } 1233 1234 1235 1236 /** 1237 * Deregisters the provided DIT content rule definition with this 1238 * schema. 1239 * 1240 * @param ditContentRule The DIT content rule to deregister with 1241 * this schema. 1242 */ 1243 public void deregisterDITContentRule(DITContentRule ditContentRule) 1244 { 1245 synchronized (ditContentRules) 1246 { 1247 ditContentRules.remove(ditContentRule.getStructuralClass(), 1248 ditContentRule); 1249 } 1250 } 1251 1252 1253 1254 /** 1255 * Retrieves the DIT structure rule definitions for this schema, as 1256 * a mapping between the rule ID for the rule and the DIT structure 1257 * rule itself. Each DIT structure rule should only be present 1258 * once, since its only key is its rule ID. The contents of the 1259 * returned mapping must not be altered. 1260 * 1261 * @return The DIT structure rule definitions for this schema. 1262 */ 1263 public ConcurrentHashMap<Integer,DITStructureRule> 1264 getDITStructureRulesByID() 1265 { 1266 return ditStructureRulesByID; 1267 } 1268 1269 1270 1271 /** 1272 * Retrieves the DIT structure rule definitions for this schema, as 1273 * a mapping between the name form for the rule and the DIT 1274 * structure rule itself. Each DIT structure rule should only be 1275 * present once, since its only key is its name form. The contents 1276 * of the returned mapping must not be altered. 1277 * 1278 * @return The DIT structure rule definitions for this schema. 1279 */ 1280 public ConcurrentHashMap<NameForm,DITStructureRule> 1281 getDITStructureRulesByNameForm() 1282 { 1283 return ditStructureRulesByNameForm; 1284 } 1285 1286 1287 1288 /** 1289 * Indicates whether this schema definition includes a DIT structure 1290 * rule with the provided rule ID. 1291 * 1292 * @param ruleID The rule ID for which to make the determination. 1293 * 1294 * @return {@code true} if this schema contains a DIT structure 1295 * rule with the provided rule ID, or {@code false} if not. 1296 */ 1297 public boolean hasDITStructureRule(int ruleID) 1298 { 1299 return ditStructureRulesByID.containsKey(ruleID); 1300 } 1301 1302 1303 1304 /** 1305 * Indicates whether this schema definition includes a DIT structure 1306 * rule for the provided name form. 1307 * 1308 * @param nameForm The name form for which to make the 1309 * determination. 1310 * 1311 * @return {@code true} if this schema contains a DIT structure 1312 * rule for the provided name form, or {@code false} if 1313 * not. 1314 */ 1315 public boolean hasDITStructureRule(NameForm nameForm) 1316 { 1317 return ditStructureRulesByNameForm.containsKey(nameForm); 1318 } 1319 1320 1321 1322 /** 1323 * Retrieves the DIT structure rule definition with the provided 1324 * rule ID. 1325 * 1326 * @param ruleID The rule ID for the DIT structure rule to 1327 * retrieve. 1328 * 1329 * @return The requested DIT structure rule, or <CODE>null</CODE> 1330 * if no DIT structure rule is registered with the provided 1331 * rule ID. 1332 */ 1333 public DITStructureRule getDITStructureRule(int ruleID) 1334 { 1335 return ditStructureRulesByID.get(ruleID); 1336 } 1337 1338 1339 1340 /** 1341 * Retrieves the DIT structure rule definition for the provided name 1342 * form. 1343 * 1344 * @param nameForm The name form for the DIT structure rule to 1345 * retrieve. 1346 * 1347 * @return The requested DIT structure rule, or <CODE>null</CODE> 1348 * if no DIT structure rule is registered with the provided 1349 * name form. 1350 */ 1351 public DITStructureRule getDITStructureRule(NameForm nameForm) 1352 { 1353 return ditStructureRulesByNameForm.get(nameForm); 1354 } 1355 1356 1357 1358 /** 1359 * Registers the provided DIT structure rule definition with this 1360 * schema. 1361 * 1362 * @param ditStructureRule The DIT structure rule to register. 1363 * @param overwriteExisting Indicates whether to overwrite an 1364 * existing mapping if there are any 1365 * conflicts (i.e., another DIT structure 1366 * rule with the same name form). 1367 * 1368 * @throws DirectoryException If a conflict is encountered and the 1369 * <CODE>overwriteExisting</CODE> flag 1370 * is set to <CODE>false</CODE> 1371 */ 1372 public void registerDITStructureRule( 1373 DITStructureRule ditStructureRule, 1374 boolean overwriteExisting) 1375 throws DirectoryException 1376 { 1377 synchronized (ditStructureRulesByNameForm) 1378 { 1379 NameForm nameForm = ditStructureRule.getNameForm(); 1380 int ruleID = ditStructureRule.getRuleID(); 1381 1382 if (! overwriteExisting) 1383 { 1384 if (ditStructureRulesByNameForm.containsKey(nameForm)) 1385 { 1386 DITStructureRule conflictingRule = 1387 ditStructureRulesByNameForm.get(nameForm); 1388 1389 LocalizableMessage message = 1390 ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_NAME_FORM. 1391 get(ditStructureRule.getNameOrRuleID(), 1392 nameForm.getNameOrOID(), 1393 conflictingRule.getNameOrRuleID()); 1394 throw new DirectoryException( 1395 ResultCode.CONSTRAINT_VIOLATION, message); 1396 } 1397 1398 if (ditStructureRulesByID.containsKey(ruleID)) 1399 { 1400 DITStructureRule conflictingRule = 1401 ditStructureRulesByID.get(ruleID); 1402 1403 LocalizableMessage message = 1404 ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID. 1405 get(ditStructureRule.getNameOrRuleID(), ruleID, 1406 conflictingRule.getNameOrRuleID()); 1407 throw new DirectoryException( 1408 ResultCode.CONSTRAINT_VIOLATION, message); 1409 } 1410 } 1411 1412 ditStructureRulesByNameForm.put(nameForm, ditStructureRule); 1413 ditStructureRulesByID.put(ruleID, ditStructureRule); 1414 } 1415 } 1416 1417 1418 1419 /** 1420 * Deregisters the provided DIT structure rule definition with this 1421 * schema. 1422 * 1423 * @param ditStructureRule The DIT structure rule to deregister 1424 * with this schema. 1425 */ 1426 public void deregisterDITStructureRule( 1427 DITStructureRule ditStructureRule) 1428 { 1429 synchronized (ditStructureRulesByNameForm) 1430 { 1431 ditStructureRulesByNameForm.remove( 1432 ditStructureRule.getNameForm(), ditStructureRule); 1433 ditStructureRulesByID.remove(ditStructureRule.getRuleID(), 1434 ditStructureRule); 1435 } 1436 } 1437 1438 1439 1440 /** 1441 * Retrieves the name form definitions for this schema, as a mapping 1442 * between the objectclass for the name forms and the name forms 1443 * themselves. 1444 * 1445 * @return The name form definitions for this schema. 1446 */ 1447 public ConcurrentHashMap<ObjectClass,List<NameForm>> 1448 getNameFormsByObjectClass() 1449 { 1450 return nameFormsByOC; 1451 } 1452 1453 1454 1455 /** 1456 * Retrieves the name form definitions for this schema, as a mapping 1457 * between the names/OID for the name form and the name form itself. 1458 * Each name form may be present multiple times with different names 1459 * and its OID. The contents of the returned mapping must not be 1460 * altered. 1461 * 1462 * @return The name form definitions for this schema. 1463 */ 1464 public ConcurrentHashMap<String,NameForm> getNameFormsByNameOrOID() 1465 { 1466 return nameFormsByName; 1467 } 1468 1469 1470 1471 /** 1472 * Indicates whether this schema definition includes a name form for 1473 * the specified objectclass. 1474 * 1475 * @param objectClass The objectclass for which to make the 1476 * determination. 1477 * 1478 * @return {@code true} if this schema contains a name form for the 1479 * provided objectclass, or {@code false} if not. 1480 */ 1481 public boolean hasNameForm(ObjectClass objectClass) 1482 { 1483 return nameFormsByOC.containsKey(objectClass); 1484 } 1485 1486 1487 1488 /** 1489 * Indicates whether this schema definition includes a name form 1490 * with the specified name or OID. 1491 * 1492 * @param lowerName The name or OID for which to make the 1493 * determination, formatted in all lowercase 1494 * characters. 1495 * 1496 * @return {@code true} if this schema contains a name form with 1497 * the provided name or OID, or {@code false} if not. 1498 */ 1499 public boolean hasNameForm(String lowerName) 1500 { 1501 return nameFormsByName.containsKey(lowerName); 1502 } 1503 1504 1505 1506 /** 1507 * Retrieves the name forms definition for the specified 1508 * objectclass. 1509 * 1510 * @param objectClass The objectclass for the name form to 1511 * retrieve. 1512 * 1513 * @return The requested name forms, or <CODE>null</CODE> if no 1514 * name forms are registered with the provided 1515 * objectClass. 1516 */ 1517 public List<NameForm> getNameForm(ObjectClass objectClass) 1518 { 1519 return nameFormsByOC.get(objectClass); 1520 } 1521 1522 1523 1524 /** 1525 * Retrieves the name form definition with the provided name or OID. 1526 * 1527 * @param lowerName The name or OID of the name form to retrieve, 1528 * formatted in all lowercase characters. 1529 * 1530 * @return The requested name form, or <CODE>null</CODE> if no name 1531 * form is registered with the provided name or OID. 1532 */ 1533 public NameForm getNameForm(String lowerName) 1534 { 1535 return nameFormsByName.get(lowerName); 1536 } 1537 1538 1539 1540 /** 1541 * Registers the provided name form definition with this schema. 1542 * 1543 * @param nameForm The name form definition to register. 1544 * @param overwriteExisting Indicates whether to overwrite an 1545 * existing mapping if there are any 1546 * conflicts (i.e., another name form 1547 * with the same objectclass). 1548 * 1549 * @throws DirectoryException If a conflict is encountered and the 1550 * <CODE>overwriteExisting</CODE> flag 1551 * is set to <CODE>false</CODE> 1552 */ 1553 public void registerNameForm(NameForm nameForm, 1554 boolean overwriteExisting) 1555 throws DirectoryException 1556 { 1557 synchronized (nameFormsByOC) 1558 { 1559 ObjectClass objectClass = nameForm.getStructuralClass(); 1560 List<NameForm> mappedForms = nameFormsByOC.get(objectClass); 1561 if (! overwriteExisting) 1562 { 1563 if(mappedForms !=null) 1564 { 1565 //Iterate over the forms to make sure we aren't adding a 1566 //duplicate. 1567 for(NameForm nf : mappedForms) 1568 { 1569 if(nf.equals(nameForm)) 1570 { 1571 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OC. 1572 get(nameForm.getNameOrOID(), 1573 objectClass.getNameOrOID(), 1574 nf.getNameOrOID()); 1575 throw new DirectoryException( 1576 ResultCode.CONSTRAINT_VIOLATION, message); 1577 } 1578 } 1579 } 1580 1581 String oid = toLowerCase(nameForm.getOID()); 1582 if (nameFormsByName.containsKey(oid)) 1583 { 1584 NameForm conflictingNameForm = nameFormsByName.get(oid); 1585 1586 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID. 1587 get(nameForm.getNameOrOID(), oid, 1588 conflictingNameForm.getNameOrOID()); 1589 throw new DirectoryException( 1590 ResultCode.CONSTRAINT_VIOLATION, message); 1591 } 1592 1593 for (String name : nameForm.getNames().keySet()) 1594 { 1595 if (nameFormsByName.containsKey(name)) 1596 { 1597 NameForm conflictingNameForm = nameFormsByName.get(name); 1598 1599 LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_NAME. 1600 get(nameForm.getNameOrOID(), oid, 1601 conflictingNameForm.getNameOrOID()); 1602 throw new DirectoryException( 1603 ResultCode.CONSTRAINT_VIOLATION, message); 1604 } 1605 } 1606 } 1607 1608 if(mappedForms == null) 1609 { 1610 mappedForms = new ArrayList<>(); 1611 } 1612 1613 mappedForms.add(nameForm); 1614 nameFormsByOC.put(objectClass, mappedForms); 1615 nameFormsByName.put(toLowerCase(nameForm.getOID()), nameForm); 1616 1617 for (String name : nameForm.getNames().keySet()) 1618 { 1619 nameFormsByName.put(name, nameForm); 1620 } 1621 } 1622 } 1623 1624 1625 1626 /** 1627 * Deregisters the provided name form definition with this schema. 1628 * 1629 * @param nameForm The name form definition to deregister. 1630 */ 1631 public void deregisterNameForm(NameForm nameForm) 1632 { 1633 synchronized (nameFormsByOC) 1634 { 1635 List<NameForm> mappedForms = nameFormsByOC.get( 1636 nameForm.getStructuralClass()); 1637 if(mappedForms != null) 1638 { 1639 mappedForms.remove(nameForm); 1640 if(mappedForms.isEmpty()) 1641 { 1642 nameFormsByOC.remove(nameForm.getStructuralClass()); 1643 } 1644 } 1645 nameFormsByOC.remove(nameForm.getStructuralClass()); 1646 nameFormsByName.remove(toLowerCase(nameForm.getOID()), 1647 nameForm); 1648 1649 for (String name : nameForm.getNames().keySet()) 1650 { 1651 nameFormsByName.remove(name, nameForm); 1652 } 1653 } 1654 } 1655 1656 1657 1658 /** 1659 * Retrieves the modification timestamp for the file in the schema 1660 * configuration directory with the oldest last modified time. 1661 * 1662 * @return The modification timestamp for the file in the schema 1663 * configuration directory with the oldest last modified 1664 * time. 1665 */ 1666 public long getOldestModificationTime() 1667 { 1668 return oldestModificationTime; 1669 } 1670 1671 1672 1673 /** 1674 * Sets the modification timestamp for the oldest file in the schema 1675 * configuration directory. 1676 * 1677 * @param oldestModificationTime The modification timestamp for 1678 * the oldest file in the schema 1679 * configuration directory. 1680 */ 1681 public void setOldestModificationTime(long oldestModificationTime) 1682 { 1683 this.oldestModificationTime = oldestModificationTime; 1684 } 1685 1686 1687 1688 /** 1689 * Retrieves the modification timestamp for the file in the schema 1690 * configuration directory with the youngest last modified time. 1691 * 1692 * @return The modification timestamp for the file in the schema 1693 * configuration directory with the youngest last modified 1694 * time. 1695 */ 1696 public long getYoungestModificationTime() 1697 { 1698 return youngestModificationTime; 1699 } 1700 1701 1702 1703 /** 1704 * Sets the modification timestamp for the youngest file in the 1705 * schema configuration directory. 1706 * 1707 * @param youngestModificationTime The modification timestamp for 1708 * the youngest file in the schema 1709 * configuration directory. 1710 */ 1711 public void setYoungestModificationTime( 1712 long youngestModificationTime) 1713 { 1714 this.youngestModificationTime = youngestModificationTime; 1715 } 1716 1717 1718 1719 /** 1720 * Recursively rebuilds all schema elements that are dependent upon 1721 * the provided element. This must be invoked whenever an existing 1722 * schema element is modified in order to ensure that any elements 1723 * that depend on it should also be recreated to reflect the change. 1724 * <BR><BR> 1725 * The following conditions create dependencies between schema 1726 * elements: 1727 * <UL> 1728 * <LI>If an attribute type references a superior attribute type, 1729 * then it is dependent upon that superior attribute 1730 * type.</LI> 1731 * <LI>If an objectclass requires or allows an attribute type, 1732 * then it is dependent upon that attribute type.</LI> 1733 * <LI>If a name form requires or allows an attribute type in the 1734 * RDN, then it is dependent upon that attribute type.</LI> 1735 * <LI>If a DIT content rule requires, allows, or forbids the use 1736 * of an attribute type, then it is dependent upon that 1737 * attribute type.</LI> 1738 * <LI>If a matching rule use references an attribute type, then 1739 * it is dependent upon that attribute type.</LI> 1740 * <LI>If an objectclass references a superior objectclass, then 1741 * it is dependent upon that superior objectclass.</LI> 1742 * <LI>If a name form references a structural objectclass, then it 1743 * is dependent upon that objectclass.</LI> 1744 * <LI>If a DIT content rule references a structural or auxiliary 1745 * objectclass, then it is dependent upon that 1746 * objectclass.</LI> 1747 * <LI>If a DIT structure rule references a name form, then it is 1748 * dependent upon that name form.</LI> 1749 * <LI>If a DIT structure rule references a superior DIT structure 1750 * rule, then it is dependent upon that superior DIT structure 1751 * rule.</LI> 1752 * </UL> 1753 * 1754 * @param element The element for which to recursively rebuild all 1755 * dependent elements. 1756 * 1757 * @throws DirectoryException If a problem occurs while rebuilding 1758 * any of the schema elements. 1759 */ 1760 public void rebuildDependentElements(SchemaFileElement element) 1761 throws DirectoryException 1762 { 1763 try 1764 { 1765 rebuildDependentElements(element, 0); 1766 } 1767 catch (DirectoryException de) 1768 { 1769 // If we got an error as a result of a circular reference, then 1770 // we want to make sure that the schema element we call out is 1771 // the one that is at the root of the problem. 1772 if (StaticUtils.hasDescriptor(de.getMessageObject(), 1773 ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE)) 1774 { 1775 LocalizableMessage message = 1776 ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get(element); 1777 throw new DirectoryException(de.getResultCode(), message, 1778 de); 1779 } 1780 1781 1782 // It wasn't a circular reference error, so just re-throw the 1783 // exception. 1784 throw de; 1785 } 1786 } 1787 1788 1789 1790 /** 1791 * Recursively rebuilds all schema elements that are dependent upon 1792 * the provided element, increasing the depth for each level of 1793 * recursion to protect against errors due to circular references. 1794 * 1795 * @param element The element for which to recursively rebuild all 1796 * dependent elements. 1797 * @param depth The current recursion depth. 1798 * 1799 * @throws DirectoryException If a problem occurs while rebuilding 1800 * any of the schema elements. 1801 */ 1802 private void rebuildDependentElements(SchemaFileElement element, 1803 int depth) 1804 throws DirectoryException 1805 { 1806 if (depth > 20) 1807 { 1808 // FIXME -- Is this an appropriate maximum depth for detecting 1809 // circular references? 1810 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1811 ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get(element)); 1812 } 1813 1814 1815 // Figure out what type of element we're dealing with and make the 1816 // appropriate determinations for that element. 1817 if (element instanceof AttributeType) 1818 { 1819 AttributeType t = (AttributeType) element; 1820 1821 for (AttributeType at : attributeTypes.values()) 1822 { 1823 if (at.getSuperiorType() != null && at.getSuperiorType().equals(t)) 1824 { 1825 AttributeType newAT = recreateFromDefinition(at); 1826 deregisterAttributeType(at); 1827 registerAttributeType(newAT, true); 1828 rebuildDependentElements(at, depth+1); 1829 } 1830 } 1831 1832 for (ObjectClass oc : objectClasses.values()) 1833 { 1834 if (oc.getRequiredAttributes().contains(t) || 1835 oc.getOptionalAttributes().contains(t)) 1836 { 1837 ObjectClass newOC = recreateFromDefinition(oc); 1838 deregisterObjectClass(oc); 1839 registerObjectClass(newOC, true); 1840 rebuildDependentElements(oc, depth+1); 1841 } 1842 } 1843 1844 for (List<NameForm> mappedForms : nameFormsByOC.values()) 1845 { 1846 for(NameForm nf : mappedForms) 1847 { 1848 if (nf.getRequiredAttributes().contains(t) || 1849 nf.getOptionalAttributes().contains(t)) 1850 { 1851 NameForm newNF = recreateFromDefinition(nf); 1852 deregisterNameForm(nf); 1853 registerNameForm(newNF, true); 1854 rebuildDependentElements(nf, depth+1); 1855 } 1856 } 1857 } 1858 1859 for (DITContentRule dcr : ditContentRules.values()) 1860 { 1861 if (dcr.getRequiredAttributes().contains(t) || 1862 dcr.getOptionalAttributes().contains(t) || 1863 dcr.getProhibitedAttributes().contains(t)) 1864 { 1865 DITContentRule newDCR = recreateFromDefinition(dcr); 1866 deregisterDITContentRule(dcr); 1867 registerDITContentRule(newDCR, true); 1868 rebuildDependentElements(dcr, depth+1); 1869 } 1870 } 1871 1872 for (MatchingRuleUse mru : matchingRuleUses.values()) 1873 { 1874 if (mru.getAttributes().contains(t)) 1875 { 1876 MatchingRuleUse newMRU = recreateFromDefinition(mru); 1877 deregisterMatchingRuleUse(mru); 1878 registerMatchingRuleUse(newMRU, true); 1879 rebuildDependentElements(mru, depth+1); 1880 } 1881 } 1882 } 1883 else if (element instanceof ObjectClass) 1884 { 1885 ObjectClass c = (ObjectClass) element; 1886 1887 for (ObjectClass oc : objectClasses.values()) 1888 { 1889 if (oc.getSuperiorClasses().contains(c)) 1890 { 1891 ObjectClass newOC = recreateFromDefinition(oc); 1892 deregisterObjectClass(oc); 1893 registerObjectClass(newOC, true); 1894 rebuildDependentElements(oc, depth+1); 1895 } 1896 } 1897 1898 List<NameForm> mappedForms = nameFormsByOC.get(c); 1899 if(mappedForms != null) 1900 { 1901 for(NameForm nf : mappedForms) 1902 { 1903 if (nf != null) 1904 { 1905 NameForm newNF = recreateFromDefinition(nf); 1906 deregisterNameForm(nf); 1907 registerNameForm(newNF, true); 1908 rebuildDependentElements(nf, depth+1); 1909 } 1910 } 1911 } 1912 1913 for (DITContentRule dcr : ditContentRules.values()) 1914 { 1915 if (dcr.getStructuralClass().equals(c) || 1916 dcr.getAuxiliaryClasses().contains(c)) 1917 { 1918 DITContentRule newDCR = recreateFromDefinition(dcr); 1919 deregisterDITContentRule(dcr); 1920 registerDITContentRule(newDCR, true); 1921 rebuildDependentElements(dcr, depth+1); 1922 } 1923 } 1924 } 1925 else if (element instanceof NameForm) 1926 { 1927 NameForm n = (NameForm) element; 1928 DITStructureRule dsr = ditStructureRulesByNameForm.get(n); 1929 if (dsr != null) 1930 { 1931 DITStructureRule newDSR = recreateFromDefinition(dsr); 1932 deregisterDITStructureRule(dsr); 1933 registerDITStructureRule(newDSR, true); 1934 rebuildDependentElements(dsr, depth+1); 1935 } 1936 } 1937 else if (element instanceof DITStructureRule) 1938 { 1939 DITStructureRule d = (DITStructureRule) element; 1940 for (DITStructureRule dsr : ditStructureRulesByID.values()) 1941 { 1942 if (dsr.getSuperiorRules().contains(d)) 1943 { 1944 DITStructureRule newDSR = recreateFromDefinition(dsr); 1945 deregisterDITStructureRule(dsr); 1946 registerDITStructureRule(newDSR, true); 1947 rebuildDependentElements(dsr, depth+1); 1948 } 1949 } 1950 } 1951 } 1952 1953 private AttributeType recreateFromDefinition(AttributeType attrType) 1954 throws DirectoryException 1955 { 1956 ByteString value = ByteString.valueOf(attrType.toString()); 1957 AttributeType copy = 1958 AttributeTypeSyntax.decodeAttributeType(value, this, false); 1959 setSchemaFile(copy, getSchemaFile(attrType)); 1960 if (attrType.mayHaveSubordinateTypes()) 1961 { 1962 copy.setMayHaveSubordinateTypes(); 1963 } 1964 return copy; 1965 } 1966 1967 private DITContentRule recreateFromDefinition(DITContentRule dcr) 1968 throws DirectoryException 1969 { 1970 ByteString value = ByteString.valueOf(dcr.toString()); 1971 DITContentRule copy = 1972 DITContentRuleSyntax.decodeDITContentRule(value, this, false); 1973 setSchemaFile(copy, getSchemaFile(dcr)); 1974 return copy; 1975 } 1976 1977 private DITStructureRule recreateFromDefinition(DITStructureRule dsr) 1978 throws DirectoryException 1979 { 1980 ByteString value = ByteString.valueOf(dsr.toString()); 1981 DITStructureRule copy = 1982 DITStructureRuleSyntax.decodeDITStructureRule(value, this, false); 1983 setSchemaFile(copy, getSchemaFile(dsr)); 1984 return copy; 1985 } 1986 1987 private MatchingRuleUse recreateFromDefinition(MatchingRuleUse mru) 1988 throws DirectoryException 1989 { 1990 ByteString value = ByteString.valueOf(mru.toString()); 1991 MatchingRuleUse copy = 1992 MatchingRuleUseSyntax.decodeMatchingRuleUse(value, this, false); 1993 setSchemaFile(copy, getSchemaFile(mru)); 1994 return copy; 1995 } 1996 1997 private NameForm recreateFromDefinition(NameForm nf) 1998 throws DirectoryException 1999 { 2000 ByteString value = ByteString.valueOf(nf.toString()); 2001 NameForm copy = NameFormSyntax.decodeNameForm(value, this, false); 2002 setSchemaFile(copy, getSchemaFile(nf)); 2003 return copy; 2004 } 2005 2006 private ObjectClass recreateFromDefinition(ObjectClass oc) 2007 throws DirectoryException 2008 { 2009 ByteString value = ByteString.valueOf(oc.toString()); 2010 ObjectClass copy = ObjectClassSyntax.decodeObjectClass(value, this, false); 2011 setSchemaFile(copy, getSchemaFile(oc)); 2012 return copy; 2013 } 2014 2015 /** 2016 * Creates a new <CODE>Schema</CODE> object that is a duplicate of 2017 * this one. It elements may be added and removed from the 2018 * duplicate without impacting this version. 2019 * 2020 * @return A new <CODE>Schema</CODE> object that is a duplicate of 2021 * this one. 2022 */ 2023 public Schema duplicate() 2024 { 2025 Schema dupSchema = new Schema(); 2026 2027 dupSchema.attributeTypes.putAll(attributeTypes); 2028 dupSchema.subordinateTypes.putAll(subordinateTypes); 2029 dupSchema.objectClasses.putAll(objectClasses); 2030 dupSchema.syntaxes.putAll(syntaxes); 2031 dupSchema.matchingRules.putAll(matchingRules); 2032 dupSchema.matchingRuleUses.putAll(matchingRuleUses); 2033 dupSchema.ditContentRules.putAll(ditContentRules); 2034 dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID); 2035 dupSchema.ditStructureRulesByNameForm.putAll( 2036 ditStructureRulesByNameForm); 2037 dupSchema.nameFormsByOC.putAll(nameFormsByOC); 2038 dupSchema.nameFormsByName.putAll(nameFormsByName); 2039 dupSchema.ldapSyntaxDescriptions.putAll(ldapSyntaxDescriptions); 2040 dupSchema.oldestModificationTime = oldestModificationTime; 2041 dupSchema.youngestModificationTime = youngestModificationTime; 2042 if (extraAttributes != null) 2043 { 2044 dupSchema.extraAttributes = new HashMap<>(extraAttributes); 2045 } 2046 2047 return dupSchema; 2048 } 2049 2050 2051 /** 2052 * Get the extraAttributes stored in this schema. 2053 * 2054 * @return The extraAttributes stored in this schema. 2055 */ 2056 public Map<String, Attribute> getExtraAttributes() 2057 { 2058 return extraAttributes; 2059 } 2060 2061 2062 /** 2063 * Add a new extra Attribute for this schema. 2064 * 2065 * @param name The identifier of the extra Attribute. 2066 * 2067 * @param attr The extra attribute that must be added to 2068 * this Schema. 2069 */ 2070 public void addExtraAttribute(String name, Attribute attr) 2071 { 2072 extraAttributes.put(name, attr); 2073 } 2074 2075 2076 /** 2077 * Writes a single file containing all schema element definitions, 2078 * which can be used on startup to determine whether the schema 2079 * files were edited with the server offline. 2080 */ 2081 public static void writeConcatenatedSchema() 2082 { 2083 String concatFilePath = null; 2084 try 2085 { 2086 Set<String> attributeTypes = new LinkedHashSet<>(); 2087 Set<String> objectClasses = new LinkedHashSet<>(); 2088 Set<String> nameForms = new LinkedHashSet<>(); 2089 Set<String> ditContentRules = new LinkedHashSet<>(); 2090 Set<String> ditStructureRules = new LinkedHashSet<>(); 2091 Set<String> matchingRuleUses = new LinkedHashSet<>(); 2092 Set<String> ldapSyntaxes = new LinkedHashSet<>(); 2093 genConcatenatedSchema(attributeTypes, objectClasses, nameForms, 2094 ditContentRules, ditStructureRules, 2095 matchingRuleUses,ldapSyntaxes); 2096 2097 2098 File configFile = new File(DirectoryServer.getConfigFile()); 2099 File configDirectory = configFile.getParentFile(); 2100 File upgradeDirectory = new File(configDirectory, "upgrade"); 2101 upgradeDirectory.mkdir(); 2102 File concatFile = new File(upgradeDirectory, 2103 SCHEMA_CONCAT_FILE_NAME); 2104 concatFilePath = concatFile.getAbsolutePath(); 2105 2106 File tempFile = new File(concatFilePath + ".tmp"); 2107 BufferedWriter writer = 2108 new BufferedWriter(new FileWriter(tempFile, false)); 2109 writer.write("dn: " + DirectoryServer.getSchemaDN()); 2110 writer.newLine(); 2111 writer.write("objectClass: top"); 2112 writer.newLine(); 2113 writer.write("objectClass: ldapSubentry"); 2114 writer.newLine(); 2115 writer.write("objectClass: subschema"); 2116 writer.newLine(); 2117 2118 for (String line : attributeTypes) 2119 { 2120 writer.write(ATTR_ATTRIBUTE_TYPES); 2121 writer.write(": "); 2122 writer.write(line); 2123 writer.newLine(); 2124 } 2125 2126 for (String line : objectClasses) 2127 { 2128 writer.write(ATTR_OBJECTCLASSES); 2129 writer.write(": "); 2130 writer.write(line); 2131 writer.newLine(); 2132 } 2133 2134 for (String line : nameForms) 2135 { 2136 writer.write(ATTR_NAME_FORMS); 2137 writer.write(": "); 2138 writer.write(line); 2139 writer.newLine(); 2140 } 2141 2142 for (String line : ditContentRules) 2143 { 2144 writer.write(ATTR_DIT_CONTENT_RULES); 2145 writer.write(": "); 2146 writer.write(line); 2147 writer.newLine(); 2148 } 2149 2150 for (String line : ditStructureRules) 2151 { 2152 writer.write(ATTR_DIT_STRUCTURE_RULES); 2153 writer.write(": "); 2154 writer.write(line); 2155 writer.newLine(); 2156 } 2157 2158 for (String line : matchingRuleUses) 2159 { 2160 writer.write(ATTR_MATCHING_RULE_USE); 2161 writer.write(": "); 2162 writer.write(line); 2163 writer.newLine(); 2164 } 2165 2166 2167 for (String line : ldapSyntaxes) 2168 { 2169 writer.write(ATTR_LDAP_SYNTAXES); 2170 writer.write(": "); 2171 writer.write(line); 2172 writer.newLine(); 2173 } 2174 2175 writer.close(); 2176 2177 if (concatFile.exists()) 2178 { 2179 concatFile.delete(); 2180 } 2181 tempFile.renameTo(concatFile); 2182 } 2183 catch (Exception e) 2184 { 2185 logger.traceException(e); 2186 2187 // This is definitely not ideal, but it's not the end of the 2188 // world. The worst that should happen is that the schema 2189 // changes could potentially be sent to the other servers again 2190 // when this server is restarted, which shouldn't hurt anything. 2191 // Still, we should log a warning message. 2192 logger.error(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE, concatFilePath, getExceptionMessage(e)); 2193 } 2194 } 2195 2196 2197 2198 /** 2199 * Reads the files contained in the schema directory and generates a 2200 * concatenated view of their contents in the provided sets. 2201 * 2202 * @param attributeTypes The set into which to place the 2203 * attribute types read from the schema 2204 * files. 2205 * @param objectClasses The set into which to place the object 2206 * classes read from the schema files. 2207 * @param nameForms The set into which to place the name 2208 * forms read from the schema files. 2209 * @param ditContentRules The set into which to place the DIT 2210 * content rules read from the schema 2211 * files. 2212 * @param ditStructureRules The set into which to place the DIT 2213 * structure rules read from the schema 2214 * files. 2215 * @param matchingRuleUses The set into which to place the 2216 * matching rule uses read from the 2217 * schema files. 2218 * @param ldapSyntaxes The set into which to place the 2219 * ldap syntaxes read from the 2220 * schema files. 2221 * 2222 * @throws IOException If a problem occurs while reading the 2223 * schema file elements. 2224 */ 2225 public static void genConcatenatedSchema( 2226 Set<String> attributeTypes, 2227 Set<String> objectClasses, 2228 Set<String> nameForms, 2229 Set<String> ditContentRules, 2230 Set<String> ditStructureRules, 2231 Set<String> matchingRuleUses, 2232 Set<String> ldapSyntaxes) 2233 throws IOException 2234 { 2235 // Get a sorted list of the files in the schema directory. 2236 TreeSet<File> schemaFiles = new TreeSet<>(); 2237 String schemaDirectory = 2238 SchemaConfigManager.getSchemaDirectoryPath(); 2239 2240 final FilenameFilter filter = new SchemaConfigManager.SchemaFileFilter(); 2241 for (File f : new File(schemaDirectory).listFiles(filter)) 2242 { 2243 if (f.isFile()) 2244 { 2245 schemaFiles.add(f); 2246 } 2247 } 2248 2249 2250 // Open each of the files in order and read the elements that they 2251 // contain, appending them to the appropriate lists. 2252 for (File f : schemaFiles) 2253 { 2254 // Read the contents of the file into a list with one schema 2255 // element per list element. 2256 LinkedList<StringBuilder> lines = new LinkedList<>(); 2257 BufferedReader reader = new BufferedReader(new FileReader(f)); 2258 2259 while (true) 2260 { 2261 String line = reader.readLine(); 2262 if (line == null) 2263 { 2264 break; 2265 } 2266 else if (line.startsWith("#") || line.length() == 0) 2267 { 2268 continue; 2269 } 2270 else if (line.startsWith(" ")) 2271 { 2272 lines.getLast().append(line.substring(1)); 2273 } 2274 else 2275 { 2276 lines.add(new StringBuilder(line)); 2277 } 2278 } 2279 2280 reader.close(); 2281 2282 2283 // Iterate through each line in the list. Find the colon and 2284 // get the attribute name at the beginning. If it's something 2285 // that we don't recognize, then skip it. Otherwise, add the 2286 // X-SCHEMA-FILE extension and add it to the appropriate schema 2287 // element list. 2288 for (StringBuilder buffer : lines) 2289 { 2290 // Get the line and add the X-SCHEMA-FILE extension to the end 2291 // of it. All of them should end with " )" but some might 2292 // have the parenthesis crammed up against the last character 2293 // so deal with that as well. 2294 String line = buffer.toString().trim(); 2295 if (line.endsWith(" )")) 2296 { 2297 line = line.substring(0, line.length()-1) + 2298 SCHEMA_PROPERTY_FILENAME + " '" + f.getName() + "' )"; 2299 } 2300 else if (line.endsWith(")")) 2301 { 2302 line = line.substring(0, line.length()-1) + " " + 2303 SCHEMA_PROPERTY_FILENAME + " '" + f.getName() + "' )"; 2304 } 2305 else 2306 { 2307 continue; 2308 } 2309 2310 parseSchemaLine(line, attributeTypes, objectClasses, 2311 nameForms, ditContentRules, ditStructureRules, matchingRuleUses, 2312 ldapSyntaxes); 2313 } 2314 } 2315 } 2316 2317 2318 2319 /** 2320 * Reads data from the specified concatenated schema file into the 2321 * provided sets. 2322 * 2323 * @param concatSchemaFile The path to the concatenated schema 2324 * file to be read. 2325 * @param attributeTypes The set into which to place the 2326 * attribute types read from the 2327 * concatenated schema file. 2328 * @param objectClasses The set into which to place the object 2329 * classes read from the concatenated 2330 * schema file. 2331 * @param nameForms The set into which to place the name 2332 * forms read from the concatenated 2333 * schema file. 2334 * @param ditContentRules The set into which to place the DIT 2335 * content rules read from the 2336 * concatenated schema file. 2337 * @param ditStructureRules The set into which to place the DIT 2338 * structure rules read from the 2339 * concatenated schema file. 2340 * @param matchingRuleUses The set into which to place the 2341 * matching rule uses read from the 2342 * concatenated schema file. 2343 * @param ldapSyntaxes The set into which to place the 2344 * ldap syntaxes read from the 2345 * concatenated schema file. 2346 * 2347 * @throws IOException If a problem occurs while reading the 2348 * schema file elements. 2349 */ 2350 public static void readConcatenatedSchema(String concatSchemaFile, 2351 Set<String> attributeTypes, 2352 Set<String> objectClasses, 2353 Set<String> nameForms, 2354 Set<String> ditContentRules, 2355 Set<String> ditStructureRules, 2356 Set<String> matchingRuleUses, 2357 Set<String> ldapSyntaxes) 2358 throws IOException 2359 { 2360 BufferedReader reader = 2361 new BufferedReader(new FileReader(concatSchemaFile)); 2362 while (true) 2363 { 2364 String line = reader.readLine(); 2365 if (line == null) 2366 { 2367 break; 2368 } 2369 parseSchemaLine(line, attributeTypes, objectClasses, 2370 nameForms, ditContentRules, ditStructureRules, matchingRuleUses, 2371 ldapSyntaxes); 2372 } 2373 2374 reader.close(); 2375 } 2376 2377 /** 2378 * Parse a line of a schema file into the provided sets. 2379 * 2380 * @param line The current line of schema. 2381 * @param attributeTypes The set into which to place the 2382 * attribute type if the line represents 2383 * one. 2384 * @param objectClasses The set into which to place the object 2385 * class if the line represents one. 2386 * @param nameForms The set into which to place the name 2387 * form if the line represents one. 2388 * @param ditContentRules The set into which to place the DIT 2389 * content rule if the line represents one. 2390 * @param ditStructureRules The set into which to place the DIT 2391 * structure rule if the line represents one. 2392 * @param matchingRuleUses The set into which to place the 2393 * matching rule use if the line represents 2394 * one. 2395 * @param ldapSyntaxes The set into which to place the ldap 2396 * syntax if the line represents one. 2397 */ 2398 2399 private static void parseSchemaLine(String line, 2400 Set<String> attributeTypes, 2401 Set<String> objectClasses, 2402 Set<String> nameForms, 2403 Set<String> ditContentRules, 2404 Set<String> ditStructureRules, 2405 Set<String> matchingRuleUses, 2406 Set<String> ldapSyntaxes) 2407 { 2408 String value; 2409 String lowerLine = toLowerCase(line); 2410 if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) 2411 { 2412 value = 2413 line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim(); 2414 attributeTypes.add(value); 2415 } 2416 else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) 2417 { 2418 value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim(); 2419 objectClasses.add(value); 2420 } 2421 else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) 2422 { 2423 value = line.substring(ATTR_NAME_FORMS.length()+1).trim(); 2424 nameForms.add(value); 2425 } 2426 else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) 2427 { 2428 value = line.substring( 2429 ATTR_DIT_CONTENT_RULES.length()+1).trim(); 2430 ditContentRules.add(value); 2431 } 2432 else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) 2433 { 2434 value = line.substring( 2435 ATTR_DIT_STRUCTURE_RULES.length()+1).trim(); 2436 ditStructureRules.add(value); 2437 } 2438 else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) 2439 { 2440 value = line.substring( 2441 ATTR_MATCHING_RULE_USE.length()+1).trim(); 2442 matchingRuleUses.add(value); 2443 } 2444 else if (lowerLine.startsWith(ATTR_LDAP_SYNTAXES_LC)) 2445 { 2446 value = line.substring( 2447 ATTR_LDAP_SYNTAXES.length()+1).trim(); 2448 ldapSyntaxes.add(value); 2449 } 2450 } 2451 2452 /** 2453 * Compares the provided sets of schema element definitions and 2454 * writes any differences found into the given list of 2455 * modifications. 2456 * 2457 * @param oldElements The set of elements of the specified type 2458 * read from the previous concatenated schema 2459 * files. 2460 * @param newElements The set of elements of the specified type 2461 * read from the server's current schema. 2462 * @param elementType The attribute type associated with the 2463 * schema element being compared. 2464 * @param mods The list of modifications into which any 2465 * identified differences should be written. 2466 */ 2467 public static void compareConcatenatedSchema( 2468 Set<String> oldElements, 2469 Set<String> newElements, 2470 AttributeType elementType, 2471 List<Modification> mods) 2472 { 2473 AttributeBuilder builder = new AttributeBuilder(elementType); 2474 for (String s : oldElements) 2475 { 2476 if (!newElements.contains(s)) 2477 { 2478 builder.add(s); 2479 } 2480 } 2481 2482 if (!builder.isEmpty()) 2483 { 2484 mods.add(new Modification(ModificationType.DELETE, 2485 builder.toAttribute())); 2486 } 2487 2488 builder.setAttributeType(elementType); 2489 for (String s : newElements) 2490 { 2491 if (!oldElements.contains(s)) 2492 { 2493 builder.add(s); 2494 } 2495 } 2496 2497 if (!builder.isEmpty()) 2498 { 2499 mods.add(new Modification(ModificationType.ADD, 2500 builder.toAttribute())); 2501 } 2502 } 2503 2504 2505 2506 /** 2507 * Destroys the structures maintained by the schema so that they are 2508 * no longer usable. This should only be called at the end of the 2509 * server shutdown process, and it can help detect inappropriate 2510 * cached references. 2511 */ 2512 @org.opends.server.types.PublicAPI( 2513 stability=org.opends.server.types.StabilityLevel.PRIVATE, 2514 mayInstantiate=false, 2515 mayExtend=false, 2516 mayInvoke=true) 2517 public synchronized void destroy() 2518 { 2519 if (attributeTypes != null) 2520 { 2521 attributeTypes.clear(); 2522 attributeTypes = null; 2523 } 2524 2525 if (ditContentRules != null) 2526 { 2527 ditContentRules.clear(); 2528 ditContentRules = null; 2529 } 2530 2531 if (ditStructureRulesByID != null) 2532 { 2533 ditStructureRulesByID.clear(); 2534 ditStructureRulesByID = null; 2535 } 2536 2537 if (ditStructureRulesByNameForm != null) 2538 { 2539 ditStructureRulesByNameForm.clear(); 2540 ditStructureRulesByNameForm = null; 2541 } 2542 2543 if (matchingRules != null) 2544 { 2545 matchingRules.clear(); 2546 matchingRules = null; 2547 } 2548 2549 if (matchingRuleUses != null) 2550 { 2551 matchingRuleUses.clear(); 2552 matchingRuleUses = null; 2553 } 2554 2555 if (nameFormsByName != null) 2556 { 2557 nameFormsByName.clear(); 2558 nameFormsByName = null; 2559 } 2560 2561 if (nameFormsByOC != null) 2562 { 2563 nameFormsByOC.clear(); 2564 nameFormsByOC = null; 2565 } 2566 2567 if (objectClasses != null) 2568 { 2569 objectClasses.clear(); 2570 objectClasses = null; 2571 } 2572 2573 if (subordinateTypes != null) 2574 { 2575 subordinateTypes.clear(); 2576 subordinateTypes = null; 2577 } 2578 2579 if (extraAttributes != null) 2580 { 2581 extraAttributes.clear(); 2582 extraAttributes = null; 2583 } 2584 2585 if (syntaxes != null) 2586 { 2587 syntaxes.clear(); 2588 syntaxes = null; 2589 } 2590 2591 if(ldapSyntaxDescriptions != null) 2592 { 2593 ldapSyntaxDescriptions.clear(); 2594 ldapSyntaxDescriptions = null; 2595 } 2596 2597 } 2598} 2599