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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.types; 028 029 030 031 import java.io.BufferedReader; 032 import java.io.BufferedWriter; 033 import java.io.File; 034 import java.io.FileReader; 035 import java.io.FileWriter; 036 import java.io.IOException; 037 import java.util.Collections; 038 import java.util.HashMap; 039 import java.util.LinkedHashSet; 040 import java.util.LinkedList; 041 import java.util.List; 042 import java.util.Map; 043 import java.util.TreeSet; 044 import java.util.concurrent.ConcurrentHashMap; 045 046 import org.opends.messages.Message; 047 import org.opends.server.api.ApproximateMatchingRule; 048 import org.opends.server.api.AttributeSyntax; 049 import org.opends.server.api.EqualityMatchingRule; 050 import org.opends.server.api.MatchingRule; 051 import org.opends.server.api.OrderingMatchingRule; 052 import org.opends.server.api.SubstringMatchingRule; 053 import org.opends.server.core.DirectoryServer; 054 import org.opends.server.core.SchemaConfigManager; 055 import org.opends.server.loggers.debug.DebugTracer; 056 import org.opends.server.protocols.asn1.ASN1OctetString; 057 import org.opends.server.schema.CaseIgnoreEqualityMatchingRule; 058 059 import static org.opends.messages.BackendMessages.*; 060 import static org.opends.messages.CoreMessages.*; 061 import static org.opends.server.config.ConfigConstants.*; 062 import static org.opends.server.loggers.debug.DebugLogger.*; 063 import static org.opends.server.loggers.ErrorLogger.*; 064 import static org.opends.server.util.ServerConstants.*; 065 import static org.opends.server.util.StaticUtils.*; 066 067 068 069 /** 070 * This class defines a data structure that holds information about 071 * the components of the Directory Server schema. It includes the 072 * following kinds of elements: 073 * 074 * <UL> 075 * <LI>Attribute type definitions</LI> 076 * <LI>Objectclass definitions</LI> 077 * <LI>Attribute syntax definitions</LI> 078 * <LI>Matching rule definitions</LI> 079 * <LI>Matching rule use definitions</LI> 080 * <LI>DIT content rule definitions</LI> 081 * <LI>DIT structure rule definitions</LI> 082 * <LI>Name form definitions</LI> 083 * </UL> 084 */ 085 @org.opends.server.types.PublicAPI( 086 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 087 mayInstantiate=false, 088 mayExtend=false, 089 mayInvoke=true) 090 public final class Schema 091 { 092 /** 093 * The tracer object for the debug logger. 094 */ 095 private static final DebugTracer TRACER = getTracer(); 096 097 098 099 100 // The matching rule that will be used to normalize schema element 101 // definitions. 102 private EqualityMatchingRule normalizationMatchingRule; 103 104 // The set of subordinate attribute types registered within the 105 // server schema. 106 private ConcurrentHashMap<AttributeType,List<AttributeType>> 107 subordinateTypes; 108 109 // The set of attribute type definitions for this schema, mapped 110 // between the lowercase names and OID for the definition and the 111 // attribute type itself. 112 private ConcurrentHashMap<String,AttributeType> attributeTypes; 113 114 // The set of objectclass definitions for this schema, mapped 115 // between the lowercase names and OID for the definition and the 116 // objectclass itself. 117 private ConcurrentHashMap<String,ObjectClass> objectClasses; 118 119 // The set of attribute syntaxes for this schema, mapped between the 120 // OID for the syntax and the syntax itself. 121 private ConcurrentHashMap<String,AttributeSyntax> syntaxes; 122 123 // The entire set of matching rules for this schema, mapped between 124 // the lowercase names and OID for the definition and the matching 125 // rule itself. 126 private ConcurrentHashMap<String,MatchingRule> matchingRules; 127 128 // The set of approximate matching rules for this schema, mapped 129 // between the lowercase names and OID for the definition and the 130 // matching rule itself. 131 private ConcurrentHashMap<String,ApproximateMatchingRule> 132 approximateMatchingRules; 133 134 // The set of equality matching rules for this schema, mapped 135 // between the lowercase names and OID for the definition and the 136 // matching rule itself. 137 private ConcurrentHashMap<String,EqualityMatchingRule> 138 equalityMatchingRules; 139 140 // The set of ordering matching rules for this schema, mapped 141 // between the lowercase names and OID for the definition and the 142 // matching rule itself. 143 private ConcurrentHashMap<String,OrderingMatchingRule> 144 orderingMatchingRules; 145 146 // The set of substring matching rules for this schema, mapped 147 // between the lowercase names and OID for the definition and the 148 // matching rule itself. 149 private ConcurrentHashMap<String,SubstringMatchingRule> 150 substringMatchingRules; 151 152 // The set of matching rule uses for this schema, mapped between the 153 // matching rule for the definition and the matching rule use 154 // itself. 155 private ConcurrentHashMap<MatchingRule,MatchingRuleUse> 156 matchingRuleUses; 157 158 // The set of DIT content rules for this schema, mapped between the 159 // structural objectclass for the definition and the DIT content 160 // rule itself. 161 private ConcurrentHashMap<ObjectClass,DITContentRule> 162 ditContentRules; 163 164 // The set of DIT structure rules for this schema, mapped between 165 // the name form for the definition and the DIT structure rule 166 // itself. 167 private ConcurrentHashMap<Integer,DITStructureRule> 168 ditStructureRulesByID; 169 170 // The set of DIT structure rules for this schema, mapped between 171 // the name form for the definition and the DIT structure rule 172 // itself. 173 private ConcurrentHashMap<NameForm,DITStructureRule> 174 ditStructureRulesByNameForm; 175 176 // The set of name forms for this schema, mapped between the 177 // structural objectclass for the definition and the name form 178 // itself. 179 private ConcurrentHashMap<ObjectClass,NameForm> nameFormsByOC; 180 181 // The set of name forms for this schema, mapped between the 182 // names/OID and the name form itself. 183 private ConcurrentHashMap<String,NameForm> nameFormsByName; 184 185 // The set of pre-encoded attribute syntax representations. 186 private LinkedHashSet<AttributeValue> syntaxSet; 187 188 // The set of pre-encoded attribute type representations. 189 private LinkedHashSet<AttributeValue> attributeTypeSet; 190 191 // The set of pre-encoded DIT content rule representations. 192 private LinkedHashSet<AttributeValue> ditContentRuleSet; 193 194 // The set of pre-encoded DIT structure rule representations. 195 private LinkedHashSet<AttributeValue> ditStructureRuleSet; 196 197 // The set of pre-encoded matching rule representations. 198 private LinkedHashSet<AttributeValue> matchingRuleSet; 199 200 // The set of pre-encoded matching rule use representations. 201 private LinkedHashSet<AttributeValue> matchingRuleUseSet; 202 203 // The set of pre-encoded name form representations. 204 private LinkedHashSet<AttributeValue> nameFormSet; 205 206 // The set of pre-encoded objectclass representations. 207 private LinkedHashSet<AttributeValue> objectClassSet; 208 209 // The oldest modification timestamp for any schema configuration 210 // file. 211 private long oldestModificationTime; 212 213 // The youngest modification timestamp for any schema configuration 214 // file. 215 private long youngestModificationTime; 216 217 // A set of extra attributes that are not used directly by 218 // the schema but may be used by other component to store 219 // information in the schema. 220 // ex : Replication uses this to store its state and GenerationID. 221 222 private Map<String, Attribute> extraAttributes = 223 new HashMap<String, Attribute>(); 224 225 226 227 /** 228 * Creates a new schema structure with all elements initialized but 229 * empty. 230 */ 231 public Schema() 232 { 233 attributeTypes = new ConcurrentHashMap<String,AttributeType>(); 234 objectClasses = new ConcurrentHashMap<String,ObjectClass>(); 235 syntaxes = new ConcurrentHashMap<String,AttributeSyntax>(); 236 matchingRules = new ConcurrentHashMap<String,MatchingRule>(); 237 approximateMatchingRules = 238 new ConcurrentHashMap<String,ApproximateMatchingRule>(); 239 equalityMatchingRules = 240 new ConcurrentHashMap<String,EqualityMatchingRule>(); 241 orderingMatchingRules = 242 new ConcurrentHashMap<String,OrderingMatchingRule>(); 243 substringMatchingRules = 244 new ConcurrentHashMap<String,SubstringMatchingRule>(); 245 matchingRuleUses = 246 new ConcurrentHashMap<MatchingRule,MatchingRuleUse>(); 247 ditContentRules = 248 new ConcurrentHashMap<ObjectClass,DITContentRule>(); 249 ditStructureRulesByID = 250 new ConcurrentHashMap<Integer,DITStructureRule>(); 251 ditStructureRulesByNameForm = 252 new ConcurrentHashMap<NameForm,DITStructureRule>(); 253 nameFormsByOC = new ConcurrentHashMap<ObjectClass,NameForm>(); 254 nameFormsByName = new ConcurrentHashMap<String,NameForm>(); 255 subordinateTypes = 256 new ConcurrentHashMap<AttributeType,List<AttributeType>>(); 257 258 259 syntaxSet = new LinkedHashSet<AttributeValue>(); 260 attributeTypeSet = new LinkedHashSet<AttributeValue>(); 261 ditContentRuleSet = new LinkedHashSet<AttributeValue>(); 262 ditStructureRuleSet = new LinkedHashSet<AttributeValue>(); 263 matchingRuleSet = new LinkedHashSet<AttributeValue>(); 264 matchingRuleUseSet = new LinkedHashSet<AttributeValue>(); 265 nameFormSet = new LinkedHashSet<AttributeValue>(); 266 objectClassSet = new LinkedHashSet<AttributeValue>(); 267 268 normalizationMatchingRule = new CaseIgnoreEqualityMatchingRule(); 269 oldestModificationTime = System.currentTimeMillis(); 270 youngestModificationTime = oldestModificationTime; 271 } 272 273 274 275 /** 276 * Retrieves the attribute type definitions for this schema, as a 277 * mapping between the lowercase names and OIDs for the attribute 278 * type and the attribute type itself. Each attribute type may be 279 * associated with multiple keys (once for the OID and again for 280 * each name). The contents of the returned mapping must not be 281 * altered. 282 * 283 * @return The attribute type definitions for this schema. 284 */ 285 public ConcurrentHashMap<String,AttributeType> getAttributeTypes() 286 { 287 return attributeTypes; 288 } 289 290 291 292 /** 293 * Retrieves the set of defined attribute types for this schema. 294 * 295 * @return The set of defined attribute types for this schema. 296 */ 297 public LinkedHashSet<AttributeValue> getAttributeTypeSet() 298 { 299 return attributeTypeSet; 300 } 301 302 303 304 /** 305 * Indicates whether this schema definition includes an attribute 306 * type with the provided name or OID. 307 * 308 * @param lowerName The name or OID for which to make the 309 * determination, formatted in all lowercase 310 * characters. 311 * 312 * @return {@code true} if this schema contains an attribute type 313 * with the provided name or OID, or {@code false} if not. 314 */ 315 public boolean hasAttributeType(String lowerName) 316 { 317 return attributeTypes.containsKey(lowerName); 318 } 319 320 321 322 /** 323 * Retrieves the attribute type definition with the specified name 324 * or OID. 325 * 326 * @param lowerName The name or OID of the attribute type to 327 * retrieve, formatted in all lowercase 328 * characters. 329 * 330 * @return The requested attribute type, or <CODE>null</CODE> if no 331 * type is registered with the provided name or OID. 332 */ 333 public AttributeType getAttributeType(String lowerName) 334 { 335 return attributeTypes.get(lowerName); 336 } 337 338 339 340 /** 341 * Registers the provided attribute type definition with this 342 * schema. 343 * 344 * @param attributeType The attribute type to register with 345 * this schema. 346 * @param overwriteExisting Indicates whether to overwrite an 347 * existing mapping if there are any 348 * conflicts (i.e., another attribute 349 * type with the same OID or name). 350 * 351 * @throws DirectoryException If a conflict is encountered and the 352 * <CODE>overwriteExisting</CODE> flag 353 * is set to <CODE>false</CODE> 354 */ 355 public void registerAttributeType(AttributeType attributeType, 356 boolean overwriteExisting) 357 throws DirectoryException 358 { 359 synchronized (attributeTypes) 360 { 361 if (! overwriteExisting) 362 { 363 String oid = toLowerCase(attributeType.getOID()); 364 if (attributeTypes.containsKey(oid)) 365 { 366 AttributeType conflictingType = attributeTypes.get(oid); 367 368 Message message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_OID. 369 get(attributeType.getNameOrOID(), oid, 370 conflictingType.getNameOrOID()); 371 throw new DirectoryException( 372 ResultCode.CONSTRAINT_VIOLATION, message); 373 } 374 375 for (String name : attributeType.getNormalizedNames()) 376 { 377 if (attributeTypes.containsKey(name)) 378 { 379 AttributeType conflictingType = attributeTypes.get(name); 380 381 Message message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_NAME. 382 get(attributeType.getNameOrOID(), name, 383 conflictingType.getNameOrOID()); 384 throw new DirectoryException( 385 ResultCode.CONSTRAINT_VIOLATION, message); 386 } 387 } 388 } 389 390 attributeTypes.put(toLowerCase(attributeType.getOID()), 391 attributeType); 392 393 for (String name : attributeType.getNormalizedNames()) 394 { 395 attributeTypes.put(name, attributeType); 396 } 397 398 AttributeType superiorType = attributeType.getSuperiorType(); 399 if (superiorType != null) 400 { 401 registerSubordinateType(attributeType, superiorType); 402 } 403 404 // We'll use an attribute value including the normalized value 405 // rather than the attribute type because otherwise it would use 406 // a very expensive matching rule (OID first component match) 407 // that would kill performance. 408 String valueString = attributeType.getDefinition(); 409 ASN1OctetString rawValue = new ASN1OctetString(valueString); 410 ByteString normValue = normalizationMatchingRule.normalizeValue( 411 new ASN1OctetString(valueString)); 412 attributeTypeSet.add(new AttributeValue(rawValue, normValue)); 413 } 414 } 415 416 417 418 /** 419 * Deregisters the provided attribute type definition with this 420 * schema. 421 * 422 * @param attributeType The attribute type to deregister with this 423 * schema. 424 */ 425 public void deregisterAttributeType(AttributeType attributeType) 426 { 427 synchronized (attributeTypes) 428 { 429 attributeTypes.remove(toLowerCase(attributeType.getOID()), 430 attributeType); 431 432 for (String name : attributeType.getNormalizedNames()) 433 { 434 attributeTypes.remove(name, attributeType); 435 } 436 437 AttributeType superiorType = attributeType.getSuperiorType(); 438 if (superiorType != null) 439 { 440 deregisterSubordinateType(attributeType, superiorType); 441 } 442 443 // We'll use an attribute value including the normalized value 444 // rather than the attribute type because otherwise it would use 445 // a very expensive matching rule (OID first component match) 446 // that would kill performance. 447 try 448 { 449 String valueString = attributeType.getDefinition(); 450 ASN1OctetString rawValue = new ASN1OctetString(valueString); 451 ByteString normValue = 452 normalizationMatchingRule.normalizeValue( 453 new ASN1OctetString(valueString)); 454 attributeTypeSet.remove(new AttributeValue(rawValue, 455 normValue)); 456 } 457 catch (Exception e) 458 { 459 String valueString = attributeType.getDefinition(); 460 ASN1OctetString rawValue = new ASN1OctetString(valueString); 461 ASN1OctetString normValue = 462 new ASN1OctetString(toLowerCase(valueString)); 463 attributeTypeSet.remove(new AttributeValue(rawValue, 464 normValue)); 465 } 466 } 467 } 468 469 470 471 /** 472 * Registers the provided attribute type as a subtype of the given 473 * superior attribute type, recursively following any additional 474 * elements in the superior chain. 475 * 476 * @param attributeType The attribute type to be registered as a 477 * subtype for the given superior type. 478 * @param superiorType The superior type for which to register 479 * the given attribute type as a subtype. 480 */ 481 private void registerSubordinateType(AttributeType attributeType, 482 AttributeType superiorType) 483 { 484 List<AttributeType> subTypes = subordinateTypes.get(superiorType); 485 if (subTypes == null) 486 { 487 superiorType.setMayHaveSubordinateTypes(); 488 subTypes = new LinkedList<AttributeType>(); 489 subTypes.add(attributeType); 490 subordinateTypes.put(superiorType, subTypes); 491 } 492 else if (! subTypes.contains(attributeType)) 493 { 494 superiorType.setMayHaveSubordinateTypes(); 495 subTypes.add(attributeType); 496 497 AttributeType higherSuperior = superiorType.getSuperiorType(); 498 if (higherSuperior != null) 499 { 500 registerSubordinateType(attributeType, higherSuperior); 501 } 502 } 503 } 504 505 506 507 /** 508 * Deregisters the provided attribute type as a subtype of the given 509 * superior attribute type, recursively following any additional 510 * elements in the superior chain. 511 * 512 * @param attributeType The attribute type to be deregistered as a 513 * subtype for the given superior type. 514 * @param superiorType The superior type for which to deregister 515 * the given attribute type as a subtype. 516 */ 517 private void deregisterSubordinateType(AttributeType attributeType, 518 AttributeType superiorType) 519 { 520 List<AttributeType> subTypes = subordinateTypes.get(superiorType); 521 if (subTypes != null) 522 { 523 if (subTypes.remove(attributeType)) 524 { 525 AttributeType higherSuperior = superiorType.getSuperiorType(); 526 if (higherSuperior != null) 527 { 528 deregisterSubordinateType(attributeType, higherSuperior); 529 } 530 } 531 } 532 } 533 534 535 536 /** 537 * Retrieves the set of subtypes registered for the given attribute 538 * type. 539 * 540 * @param attributeType The attribute type for which to retrieve 541 * the set of registered subtypes. 542 * 543 * @return The set of subtypes registered for the given attribute 544 * type, or an empty set if there are no subtypes 545 * registered for the attribute type. 546 */ 547 public Iterable<AttributeType> 548 getSubTypes(AttributeType attributeType) 549 { 550 List<AttributeType> subTypes = 551 subordinateTypes.get(attributeType); 552 if (subTypes == null) 553 { 554 return Collections.<AttributeType>emptyList(); 555 } 556 else 557 { 558 return subTypes; 559 } 560 } 561 562 563 564 /** 565 * Retrieves the objectclass definitions for this schema, as a 566 * mapping between the lowercase names and OIDs for the objectclass 567 * and the objectclass itself. Each objectclass may be associated 568 * with multiple keys (once for the OID and again for each name). 569 * The contents of the returned mapping must not be altered. 570 * 571 * @return The objectclass definitions for this schema. 572 */ 573 public ConcurrentHashMap<String,ObjectClass> getObjectClasses() 574 { 575 return objectClasses; 576 } 577 578 579 580 /** 581 * Retrieves the set of defined objectclasses for this schema. 582 * 583 * @return The set of defined objectclasses for this schema. 584 */ 585 public LinkedHashSet<AttributeValue> getObjectClassSet() 586 { 587 return objectClassSet; 588 } 589 590 591 592 /** 593 * Indicates whether this schema definition includes an objectclass 594 * with the provided name or OID. 595 * 596 * @param lowerName The name or OID for which to make the 597 * determination, formatted in all lowercase 598 * characters. 599 * 600 * @return {@code true} if this schema contains an objectclass with 601 * the provided name or OID, or {@code false} if not. 602 */ 603 public boolean hasObjectClass(String lowerName) 604 { 605 return objectClasses.containsKey(lowerName); 606 } 607 608 609 610 /** 611 * Retrieves the objectclass definition with the specified name or 612 * OID. 613 * 614 * @param lowerName The name or OID of the objectclass to 615 * retrieve, formatted in all lowercase 616 * characters. 617 * 618 * @return The requested objectclass, or <CODE>null</CODE> if no 619 * class is registered with the provided name or OID. 620 */ 621 public ObjectClass getObjectClass(String lowerName) 622 { 623 return objectClasses.get(lowerName); 624 } 625 626 627 628 /** 629 * Registers the provided objectclass definition with this schema. 630 * 631 * @param objectClass The objectclass to register with this 632 * schema. 633 * @param overwriteExisting Indicates whether to overwrite an 634 * existing mapping if there are any 635 * conflicts (i.e., another objectclass 636 * with the same OID or name). 637 * 638 * @throws DirectoryException If a conflict is encountered and the 639 * <CODE>overwriteExisting</CODE> flag 640 * is set to <CODE>false</CODE>. 641 */ 642 public void registerObjectClass(ObjectClass objectClass, 643 boolean overwriteExisting) 644 throws DirectoryException 645 { 646 synchronized (objectClasses) 647 { 648 if (! overwriteExisting) 649 { 650 String oid = toLowerCase(objectClass.getOID()); 651 if (objectClasses.containsKey(oid)) 652 { 653 ObjectClass conflictingClass = objectClasses.get(oid); 654 655 Message message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_OID. 656 get(objectClass.getNameOrOID(), oid, 657 conflictingClass.getNameOrOID()); 658 throw new DirectoryException( 659 ResultCode.CONSTRAINT_VIOLATION, message); 660 } 661 662 for (String name : objectClass.getNormalizedNames()) 663 { 664 if (objectClasses.containsKey(name)) 665 { 666 ObjectClass conflictingClass = objectClasses.get(name); 667 668 Message message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_NAME. 669 get(objectClass.getNameOrOID(), name, 670 conflictingClass.getNameOrOID()); 671 throw new DirectoryException( 672 ResultCode.CONSTRAINT_VIOLATION, message); 673 } 674 } 675 } 676 677 objectClasses.put(toLowerCase(objectClass.getOID()), 678 objectClass); 679 680 for (String name : objectClass.getNormalizedNames()) 681 { 682 objectClasses.put(name, objectClass); 683 } 684 685 // We'll use an attribute value including the normalized value 686 // rather than the attribute type because otherwise it would use 687 // a very expensive matching rule (OID first component match) 688 // that would kill performance. 689 String valueString = objectClass.getDefinition(); 690 ASN1OctetString rawValue = new ASN1OctetString(valueString); 691 ByteString normValue = normalizationMatchingRule.normalizeValue( 692 new ASN1OctetString(valueString)); 693 objectClassSet.add(new AttributeValue(rawValue, normValue)); 694 } 695 } 696 697 698 699 /** 700 * Deregisters the provided objectclass definition with this schema. 701 * 702 * @param objectClass The objectclass to deregister with this 703 * schema. 704 */ 705 public void deregisterObjectClass(ObjectClass objectClass) 706 { 707 synchronized (objectClasses) 708 { 709 objectClasses.remove(toLowerCase(objectClass.getOID()), 710 objectClass); 711 712 for (String name : objectClass.getNormalizedNames()) 713 { 714 objectClasses.remove(name, objectClass); 715 } 716 717 718 // We'll use an attribute value including the normalized value 719 // rather than the attribute type because otherwise it would use 720 // a very expensive matching rule (OID first component match) 721 // that would kill performance. 722 try 723 { 724 String valueString = objectClass.getDefinition(); 725 ASN1OctetString rawValue = new ASN1OctetString(valueString); 726 ByteString normValue = 727 normalizationMatchingRule.normalizeValue( 728 new ASN1OctetString(valueString)); 729 objectClassSet.remove(new AttributeValue(rawValue, 730 normValue)); 731 } 732 catch (Exception e) 733 { 734 String valueString = objectClass.getDefinition(); 735 ASN1OctetString rawValue = new ASN1OctetString(valueString); 736 ASN1OctetString normValue = 737 new ASN1OctetString(toLowerCase(valueString)); 738 objectClassSet.remove(new AttributeValue(rawValue, 739 normValue)); 740 } 741 } 742 } 743 744 745 746 /** 747 * Retrieves the attribute syntax definitions for this schema, as a 748 * mapping between the OID for the syntax and the syntax itself. 749 * Each syntax should only be present once, since its only key is 750 * its OID. The contents of the returned mapping must not be 751 * altered. 752 * 753 * @return The attribute syntax definitions for this schema. 754 */ 755 public ConcurrentHashMap<String,AttributeSyntax> getSyntaxes() 756 { 757 return syntaxes; 758 } 759 760 761 762 /** 763 * Retrieves the set of defined attribute syntaxes for this schema. 764 * 765 * @return The set of defined attribute syntaxes for this schema. 766 */ 767 public LinkedHashSet<AttributeValue> getSyntaxSet() 768 { 769 return syntaxSet; 770 } 771 772 773 774 /** 775 * Indicates whether this schema definition includes an attribute 776 * syntax with the provided name or OID. 777 * 778 * @param lowerName The name or OID for which to make the 779 * determination, formatted in all lowercase 780 * characters. 781 * 782 * @return {@code true} if this schema contains an attribute syntax 783 * with the provided name or OID, or {@code false} if not. 784 */ 785 public boolean hasSyntax(String lowerName) 786 { 787 return syntaxes.containsKey(lowerName); 788 } 789 790 791 792 /** 793 * Retrieves the attribute syntax definition with the OID. 794 * 795 * @param lowerName The OID of the attribute syntax to retrieve, 796 * formatted in all lowercase characters. 797 * 798 * @return The requested attribute syntax, or <CODE>null</CODE> if 799 * no syntax is registered with the provided OID. 800 */ 801 public AttributeSyntax getSyntax(String lowerName) 802 { 803 return syntaxes.get(lowerName); 804 } 805 806 807 808 /** 809 * Registers the provided attribute syntax definition with this 810 * schema. 811 * 812 * @param syntax The attribute syntax to register with 813 * this schema. 814 * @param overwriteExisting Indicates whether to overwrite an 815 * existing mapping if there are any 816 * conflicts (i.e., another attribute 817 * syntax with the same OID). 818 * 819 * @throws DirectoryException If a conflict is encountered and the 820 * <CODE>overwriteExisting</CODE> flag 821 * is set to <CODE>false</CODE> 822 */ 823 public void registerSyntax(AttributeSyntax syntax, 824 boolean overwriteExisting) 825 throws DirectoryException 826 { 827 synchronized (syntaxes) 828 { 829 if (! overwriteExisting) 830 { 831 String oid = toLowerCase(syntax.getOID()); 832 if (syntaxes.containsKey(oid)) 833 { 834 AttributeSyntax conflictingSyntax = syntaxes.get(oid); 835 836 Message message = ERR_SCHEMA_CONFLICTING_SYNTAX_OID. 837 get(syntax.getSyntaxName(), oid, 838 conflictingSyntax.getSyntaxName()); 839 throw new DirectoryException( 840 ResultCode.CONSTRAINT_VIOLATION, message); 841 } 842 } 843 844 syntaxes.put(toLowerCase(syntax.getOID()), syntax); 845 846 // We'll use an attribute value including the normalized value 847 // rather than the attribute type because otherwise it would use 848 // a very expensive matching rule (OID first component match) 849 // that would kill performance. 850 String valueString = syntax.toString(); 851 ASN1OctetString rawValue = new ASN1OctetString(valueString); 852 ByteString normValue = normalizationMatchingRule.normalizeValue( 853 new ASN1OctetString(valueString)); 854 syntaxSet.add(new AttributeValue(rawValue, normValue)); 855 } 856 } 857 858 859 860 /** 861 * Deregisters the provided attribute syntax definition with this 862 * schema. 863 * 864 * @param syntax The attribute syntax to deregister with this 865 * schema. 866 */ 867 public void deregisterSyntax(AttributeSyntax syntax) 868 { 869 synchronized (syntaxes) 870 { 871 syntaxes.remove(toLowerCase(syntax.getOID()), syntax); 872 873 // We'll use an attribute value including the normalized value 874 // rather than the attribute type because otherwise it would use 875 // a very expensive matching rule (OID first component match) 876 // that would kill performance. 877 try 878 { 879 String valueString = syntax.toString(); 880 ASN1OctetString rawValue = new ASN1OctetString(valueString); 881 ByteString normValue = 882 normalizationMatchingRule.normalizeValue( 883 new ASN1OctetString(valueString)); 884 syntaxSet.remove(new AttributeValue(rawValue, normValue)); 885 } 886 catch (Exception e) 887 { 888 String valueString = syntax.toString(); 889 ASN1OctetString rawValue = new ASN1OctetString(valueString); 890 ASN1OctetString normValue = 891 new ASN1OctetString(toLowerCase(valueString)); 892 syntaxSet.remove(new AttributeValue(rawValue, normValue)); 893 } 894 } 895 } 896 897 898 899 /** 900 * Retrieves the entire set of matching rule definitions for this 901 * schema, as a mapping between the lowercase names and OIDs for the 902 * matching rule and the matching rule itself. Each matching rule 903 * may be associated with multiple keys (once for the OID and again 904 * for each name). This should be a superset of the sets of 905 * approximate, equality, ordering, and substring matching rules. 906 * The contents of the returned mapping must not be altered. 907 * 908 * @return The matching rule definitions for this schema. 909 */ 910 public ConcurrentHashMap<String,MatchingRule> getMatchingRules() 911 { 912 return matchingRules; 913 } 914 915 916 917 /** 918 * Retrieves the set of defined matching rules for this schema. 919 * 920 * @return The set of defined matching rules for this schema. 921 */ 922 public LinkedHashSet<AttributeValue> getMatchingRuleSet() 923 { 924 return matchingRuleSet; 925 } 926 927 928 929 /** 930 * Indicates whether this schema definition includes a matching rule 931 * with the provided name or OID. 932 * 933 * @param lowerName The name or OID for which to make the 934 * determination, formatted in all lowercase 935 * characters. 936 * 937 * @return {@code true} if this schema contains a matching rule 938 * with the provided name or OID, or {@code false} if not. 939 */ 940 public boolean hasMatchingRule(String lowerName) 941 { 942 return matchingRules.containsKey(lowerName); 943 } 944 945 946 947 /** 948 * Retrieves the matching rule definition with the specified name or 949 * OID. 950 * 951 * @param lowerName The name or OID of the matching rule to 952 * retrieve, formatted in all lowercase 953 * characters. 954 * 955 * @return The requested matching rule, or <CODE>null</CODE> if no 956 * rule is registered with the provided name or OID. 957 */ 958 public MatchingRule getMatchingRule(String lowerName) 959 { 960 return matchingRules.get(lowerName); 961 } 962 963 964 965 /** 966 * Registers the provided matching rule definition with this schema. 967 * 968 * @param matchingRule The matching rule to register with 969 * this schema. 970 * @param overwriteExisting Indicates whether to overwrite an 971 * existing mapping if there are any 972 * conflicts (i.e., 973 * another matching rule with the same 974 * OID or name). 975 * 976 * @throws DirectoryException If a conflict is encountered and the 977 * <CODE>overwriteExisting</CODE> flag 978 * is set to <CODE>false</CODE> 979 */ 980 public void registerMatchingRule(MatchingRule matchingRule, 981 boolean overwriteExisting) 982 throws DirectoryException 983 { 984 if (matchingRule instanceof ApproximateMatchingRule) 985 { 986 registerApproximateMatchingRule( 987 (ApproximateMatchingRule) matchingRule, overwriteExisting); 988 } 989 else if (matchingRule instanceof EqualityMatchingRule) 990 { 991 registerEqualityMatchingRule( 992 (EqualityMatchingRule) matchingRule, overwriteExisting); 993 } 994 else if (matchingRule instanceof OrderingMatchingRule) 995 { 996 registerOrderingMatchingRule( 997 (OrderingMatchingRule) matchingRule, overwriteExisting); 998 } 999 else if (matchingRule instanceof SubstringMatchingRule) 1000 { 1001 registerSubstringMatchingRule( 1002 (SubstringMatchingRule) matchingRule, overwriteExisting); 1003 } 1004 else 1005 { 1006 synchronized (matchingRules) 1007 { 1008 if (! overwriteExisting) 1009 { 1010 String oid = toLowerCase(matchingRule.getOID()); 1011 if (matchingRules.containsKey(oid)) 1012 { 1013 MatchingRule conflictingRule = matchingRules.get(oid); 1014 1015 Message message = ERR_SCHEMA_CONFLICTING_MR_OID. 1016 get(matchingRule.getNameOrOID(), oid, 1017 conflictingRule.getNameOrOID()); 1018 throw new DirectoryException( 1019 ResultCode.CONSTRAINT_VIOLATION, message); 1020 } 1021 1022 String name = matchingRule.getName(); 1023 if (name != null) 1024 { 1025 name = toLowerCase(name); 1026 if (matchingRules.containsKey(name)) 1027 { 1028 MatchingRule conflictingRule = matchingRules.get(name); 1029 1030 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME. 1031 get(matchingRule.getOID(), name, 1032 conflictingRule.getOID()); 1033 throw new DirectoryException( 1034 ResultCode.CONSTRAINT_VIOLATION, message); 1035 } 1036 } 1037 } 1038 1039 matchingRules.put(toLowerCase(matchingRule.getOID()), 1040 matchingRule); 1041 1042 String name = matchingRule.getName(); 1043 if (name != null) 1044 { 1045 matchingRules.put(toLowerCase(name), matchingRule); 1046 } 1047 1048 // We'll use an attribute value including the normalized value 1049 // rather than the attribute type because otherwise it would 1050 // use a very expensive matching rule (OID first component 1051 // match) that would kill performance. 1052 String valueString = matchingRule.toString(); 1053 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1054 ByteString normValue = 1055 normalizationMatchingRule.normalizeValue( 1056 new ASN1OctetString(valueString)); 1057 matchingRuleSet.add(new AttributeValue(rawValue, normValue)); 1058 } 1059 } 1060 } 1061 1062 1063 1064 /** 1065 * Deregisters the provided matching rule definition with this 1066 * schema. 1067 * 1068 * @param matchingRule The matching rule to deregister with this 1069 * schema. 1070 */ 1071 public void deregisterMatchingRule(MatchingRule matchingRule) 1072 { 1073 if (matchingRule instanceof ApproximateMatchingRule) 1074 { 1075 deregisterApproximateMatchingRule( 1076 (ApproximateMatchingRule) matchingRule); 1077 } 1078 else if (matchingRule instanceof EqualityMatchingRule) 1079 { 1080 deregisterEqualityMatchingRule( 1081 (EqualityMatchingRule) matchingRule); 1082 } 1083 else if (matchingRule instanceof OrderingMatchingRule) 1084 { 1085 deregisterOrderingMatchingRule( 1086 (OrderingMatchingRule) matchingRule); 1087 } 1088 else if (matchingRule instanceof SubstringMatchingRule) 1089 { 1090 deregisterSubstringMatchingRule( 1091 (SubstringMatchingRule) matchingRule); 1092 } 1093 else 1094 { 1095 synchronized (matchingRules) 1096 { 1097 matchingRules.remove(toLowerCase(matchingRule.getOID()), 1098 matchingRule); 1099 1100 String name = matchingRule.getName(); 1101 if (name != null) 1102 { 1103 matchingRules.remove(toLowerCase(name), matchingRule); 1104 } 1105 1106 1107 // We'll use an attribute value including the normalized value 1108 // rather than the attribute type because otherwise it would 1109 // use a very expensive matching rule (OID first component 1110 // match) that would kill performance. 1111 try 1112 { 1113 String valueString = matchingRule.toString(); 1114 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1115 ByteString normValue = 1116 normalizationMatchingRule.normalizeValue( 1117 new ASN1OctetString(valueString)); 1118 matchingRuleSet.remove(new AttributeValue(rawValue, 1119 normValue)); 1120 } 1121 catch (Exception e) 1122 { 1123 String valueString = matchingRule.toString(); 1124 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1125 ASN1OctetString normValue = 1126 new ASN1OctetString(toLowerCase(valueString)); 1127 matchingRuleSet.remove(new AttributeValue(rawValue, 1128 normValue)); 1129 } 1130 } 1131 } 1132 } 1133 1134 1135 1136 /** 1137 * Retrieves the approximate matching rule definitions for this 1138 * schema, as a mapping between the lowercase names and OIDs for the 1139 * matching rule and the matching rule itself. Each matching rule 1140 * may be associated with multiple keys (once for the OID and again 1141 * for each name). The contents of the returned mapping must not be 1142 * altered. 1143 * 1144 * @return The approximate matching rule definitions for this 1145 * schema. 1146 */ 1147 public ConcurrentHashMap<String,ApproximateMatchingRule> 1148 getApproximateMatchingRules() 1149 { 1150 return approximateMatchingRules; 1151 } 1152 1153 1154 1155 /** 1156 * Retrieves the approximate matching rule definition with the 1157 * specified name or OID. 1158 * 1159 * @param lowerName The name or OID of the matching rule to 1160 * retrieve, formatted in all lowercase 1161 * characters. 1162 * 1163 * @return The requested matching rule, or <CODE>null</CODE> if no 1164 * approximate matching rule is registered with the 1165 * provided name or OID. 1166 */ 1167 public ApproximateMatchingRule getApproximateMatchingRule( 1168 String lowerName) 1169 { 1170 return approximateMatchingRules.get(lowerName); 1171 } 1172 1173 1174 1175 /** 1176 * Registers the provided approximate matching rule with this 1177 * schema. 1178 * 1179 * @param matchingRule The approximate matching rule to 1180 * register. 1181 * @param overwriteExisting Indicates whether to overwrite an 1182 * existing mapping if there are any 1183 * conflicts (i.e., another matching rule 1184 * with the same OID or name). 1185 * 1186 * @throws DirectoryException If a conflict is encountered and the 1187 * <CODE>overwriteExisting</CODE> flag 1188 * is set to <CODE>false</CODE> 1189 */ 1190 public void registerApproximateMatchingRule( 1191 ApproximateMatchingRule matchingRule, 1192 boolean overwriteExisting) 1193 throws DirectoryException 1194 { 1195 synchronized (matchingRules) 1196 { 1197 if (! overwriteExisting) 1198 { 1199 String oid = toLowerCase(matchingRule.getOID()); 1200 if (matchingRules.containsKey(oid)) 1201 { 1202 MatchingRule conflictingRule = matchingRules.get(oid); 1203 1204 Message message = ERR_SCHEMA_CONFLICTING_MR_OID. 1205 get(matchingRule.getNameOrOID(), oid, 1206 conflictingRule.getNameOrOID()); 1207 throw new DirectoryException( 1208 ResultCode.CONSTRAINT_VIOLATION, message); 1209 } 1210 1211 String name = matchingRule.getName(); 1212 if (name != null) 1213 { 1214 name = toLowerCase(name); 1215 if (matchingRules.containsKey(name)) 1216 { 1217 MatchingRule conflictingRule = matchingRules.get(name); 1218 1219 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME. 1220 get(matchingRule.getOID(), name, 1221 conflictingRule.getOID()); 1222 throw new DirectoryException( 1223 ResultCode.CONSTRAINT_VIOLATION, message); 1224 } 1225 } 1226 } 1227 1228 String oid = toLowerCase(matchingRule.getOID()); 1229 approximateMatchingRules.put(oid, matchingRule); 1230 matchingRules.put(oid, matchingRule); 1231 1232 String name = matchingRule.getName(); 1233 if (name != null) 1234 { 1235 name = toLowerCase(name); 1236 approximateMatchingRules.put(name, matchingRule); 1237 matchingRules.put(name, matchingRule); 1238 } 1239 1240 // We'll use an attribute value including the normalized value 1241 // rather than the attribute type because otherwise it would use 1242 // a very expensive matching rule (OID first component match) 1243 // that would kill performance. 1244 String valueString = matchingRule.toString(); 1245 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1246 ByteString normValue = normalizationMatchingRule.normalizeValue( 1247 new ASN1OctetString(valueString)); 1248 matchingRuleSet.add(new AttributeValue(rawValue, normValue)); 1249 } 1250 } 1251 1252 1253 1254 /** 1255 * Deregisters the provided approximate matching rule definition 1256 * with this schema. 1257 * 1258 * @param matchingRule The approximate matching rule to deregister 1259 * with this schema. 1260 */ 1261 public void deregisterApproximateMatchingRule( 1262 ApproximateMatchingRule matchingRule) 1263 { 1264 synchronized (matchingRules) 1265 { 1266 String oid = matchingRule.getOID(); 1267 approximateMatchingRules.remove(oid, matchingRule); 1268 matchingRules.remove(oid, matchingRule); 1269 1270 String name = matchingRule.getName(); 1271 if (name != null) 1272 { 1273 name = toLowerCase(name); 1274 approximateMatchingRules.remove(name, matchingRule); 1275 matchingRules.remove(name, matchingRule); 1276 } 1277 1278 // We'll use an attribute value including the normalized value 1279 // rather than the attribute type because otherwise it would use 1280 // a very expensive matching rule (OID first component match) 1281 // that would kill performance. 1282 try 1283 { 1284 String valueString = matchingRule.toString(); 1285 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1286 ByteString normValue = 1287 normalizationMatchingRule.normalizeValue( 1288 new ASN1OctetString(valueString)); 1289 matchingRuleSet.remove(new AttributeValue(rawValue, 1290 normValue)); 1291 } 1292 catch (Exception e) 1293 { 1294 String valueString = matchingRule.toString(); 1295 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1296 ASN1OctetString normValue = 1297 new ASN1OctetString(toLowerCase(valueString)); 1298 matchingRuleSet.remove(new AttributeValue(rawValue, 1299 normValue)); 1300 } 1301 } 1302 } 1303 1304 1305 1306 /** 1307 * Retrieves the equality matching rule definitions for this schema, 1308 * as a mapping between the lowercase names and OIDs for the 1309 * matching rule and the matching rule itself. Each matching rule 1310 * may be associated with multiple keys (once for the OID and again 1311 * for each name). The contents of the returned mapping must not be 1312 * altered. 1313 * 1314 * @return The equality matching rule definitions for this schema. 1315 */ 1316 public ConcurrentHashMap<String,EqualityMatchingRule> 1317 getEqualityMatchingRules() 1318 { 1319 return equalityMatchingRules; 1320 } 1321 1322 1323 1324 /** 1325 * Retrieves the equality matching rule definition with the 1326 * specified name or OID. 1327 * 1328 * @param lowerName The name or OID of the matching rule to 1329 * retrieve, formatted in all lowercase 1330 * characters. 1331 * 1332 * @return The requested matching rule, or <CODE>null</CODE> if no 1333 * equality matching rule is registered with the provided 1334 * name or OID. 1335 */ 1336 public EqualityMatchingRule getEqualityMatchingRule( 1337 String lowerName) 1338 { 1339 return equalityMatchingRules.get(lowerName); 1340 } 1341 1342 1343 1344 /** 1345 * Registers the provided equality matching rule with this schema. 1346 * 1347 * @param matchingRule The equality matching rule to 1348 * register. 1349 * @param overwriteExisting Indicates whether to overwrite an 1350 * existing mapping if there are any 1351 * conflicts (i.e., another matching rule 1352 * with the same OID or name). 1353 * 1354 * @throws DirectoryException If a conflict is encountered and the 1355 * <CODE>overwriteExisting</CODE> flag 1356 * is set to <CODE>false</CODE> 1357 */ 1358 public void registerEqualityMatchingRule( 1359 EqualityMatchingRule matchingRule, 1360 boolean overwriteExisting) 1361 throws DirectoryException 1362 { 1363 synchronized (matchingRules) 1364 { 1365 if (! overwriteExisting) 1366 { 1367 String oid = toLowerCase(matchingRule.getOID()); 1368 if (matchingRules.containsKey(oid)) 1369 { 1370 MatchingRule conflictingRule = matchingRules.get(oid); 1371 1372 Message message = ERR_SCHEMA_CONFLICTING_MR_OID. 1373 get(matchingRule.getNameOrOID(), oid, 1374 conflictingRule.getNameOrOID()); 1375 throw new DirectoryException( 1376 ResultCode.CONSTRAINT_VIOLATION, message); 1377 } 1378 1379 String name = matchingRule.getName(); 1380 if (name != null) 1381 { 1382 name = toLowerCase(name); 1383 if (matchingRules.containsKey(name)) 1384 { 1385 MatchingRule conflictingRule = matchingRules.get(name); 1386 1387 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME. 1388 get(matchingRule.getOID(), name, 1389 conflictingRule.getOID()); 1390 throw new DirectoryException( 1391 ResultCode.CONSTRAINT_VIOLATION, message); 1392 } 1393 } 1394 } 1395 1396 String oid = toLowerCase(matchingRule.getOID()); 1397 equalityMatchingRules.put(oid, matchingRule); 1398 matchingRules.put(oid, matchingRule); 1399 1400 String name = matchingRule.getName(); 1401 if (name != null) 1402 { 1403 name = toLowerCase(name); 1404 equalityMatchingRules.put(name, matchingRule); 1405 matchingRules.put(name, matchingRule); 1406 } 1407 1408 // We'll use an attribute value including the normalized value 1409 // rather than the attribute type because otherwise it would use 1410 // a very expensive matching rule (OID first component match) 1411 // that would kill performance. 1412 String valueString = matchingRule.toString(); 1413 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1414 ByteString normValue = normalizationMatchingRule.normalizeValue( 1415 new ASN1OctetString(valueString)); 1416 matchingRuleSet.add(new AttributeValue(rawValue, normValue)); 1417 } 1418 } 1419 1420 1421 1422 /** 1423 * Deregisters the provided equality matching rule definition with 1424 * this schema. 1425 * 1426 * @param matchingRule The equality matching rule to deregister 1427 * with this schema. 1428 */ 1429 public void deregisterEqualityMatchingRule( 1430 EqualityMatchingRule matchingRule) 1431 { 1432 synchronized (matchingRules) 1433 { 1434 String oid = matchingRule.getOID(); 1435 equalityMatchingRules.remove(oid, matchingRule); 1436 matchingRules.remove(oid, matchingRule); 1437 1438 String name = matchingRule.getName(); 1439 if (name != null) 1440 { 1441 name = toLowerCase(name); 1442 equalityMatchingRules.remove(name, matchingRule); 1443 matchingRules.remove(name, matchingRule); 1444 } 1445 1446 1447 // We'll use an attribute value including the normalized value 1448 // rather than the attribute type because otherwise it would use 1449 // a very expensive matching rule (OID first component match) 1450 // that would kill performance. 1451 try 1452 { 1453 String valueString = matchingRule.toString(); 1454 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1455 ByteString normValue = 1456 normalizationMatchingRule.normalizeValue( 1457 new ASN1OctetString(valueString)); 1458 matchingRuleSet.remove(new AttributeValue(rawValue, 1459 normValue)); 1460 } 1461 catch (Exception e) 1462 { 1463 String valueString = matchingRule.toString(); 1464 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1465 ASN1OctetString normValue = 1466 new ASN1OctetString(toLowerCase(valueString)); 1467 matchingRuleSet.remove(new AttributeValue(rawValue, 1468 normValue)); 1469 } 1470 } 1471 } 1472 1473 1474 1475 /** 1476 * Retrieves the ordering matching rule definitions for this schema, 1477 * as a mapping between the lowercase names and OIDs for the 1478 * matching rule and the matching rule itself. Each matching rule 1479 * may be associated with multiple keys (once for the OID and again 1480 * for each name). The contents of the returned mapping must not be 1481 * altered. 1482 * 1483 * @return The ordering matching rule definitions for this schema. 1484 */ 1485 public ConcurrentHashMap<String,OrderingMatchingRule> 1486 getOrderingMatchingRules() 1487 { 1488 return orderingMatchingRules; 1489 } 1490 1491 1492 1493 /** 1494 * Retrieves the ordering matching rule definition with the 1495 * specified name or OID. 1496 * 1497 * @param lowerName The name or OID of the matching rule to 1498 * retrieve, formatted in all lowercase 1499 * characters. 1500 * 1501 * @return The requested matching rule, or <CODE>null</CODE> if no 1502 * ordering matching rule is registered with the provided 1503 * name or OID. 1504 */ 1505 public OrderingMatchingRule getOrderingMatchingRule( 1506 String lowerName) 1507 { 1508 return orderingMatchingRules.get(lowerName); 1509 } 1510 1511 1512 1513 /** 1514 * Registers the provided ordering matching rule with this schema. 1515 * 1516 * @param matchingRule The ordering matching rule to 1517 * register. 1518 * @param overwriteExisting Indicates whether to overwrite an 1519 * existing mapping if there are any 1520 * conflicts (i.e., another matching rule 1521 * with the same OID or name). 1522 * 1523 * @throws DirectoryException If a conflict is encountered and the 1524 * <CODE>overwriteExisting</CODE> flag 1525 * is set to <CODE>false</CODE> 1526 */ 1527 public void registerOrderingMatchingRule( 1528 OrderingMatchingRule matchingRule, 1529 boolean overwriteExisting) 1530 throws DirectoryException 1531 { 1532 synchronized (matchingRules) 1533 { 1534 if (! overwriteExisting) 1535 { 1536 String oid = toLowerCase(matchingRule.getOID()); 1537 if (matchingRules.containsKey(oid)) 1538 { 1539 MatchingRule conflictingRule = matchingRules.get(oid); 1540 1541 Message message = ERR_SCHEMA_CONFLICTING_MR_OID. 1542 get(matchingRule.getNameOrOID(), oid, 1543 conflictingRule.getNameOrOID()); 1544 throw new DirectoryException( 1545 ResultCode.CONSTRAINT_VIOLATION, message); 1546 } 1547 1548 String name = matchingRule.getName(); 1549 if (name != null) 1550 { 1551 name = toLowerCase(name); 1552 if (matchingRules.containsKey(name)) 1553 { 1554 MatchingRule conflictingRule = matchingRules.get(name); 1555 1556 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME. 1557 get(matchingRule.getOID(), name, 1558 conflictingRule.getOID()); 1559 throw new DirectoryException( 1560 ResultCode.CONSTRAINT_VIOLATION, message); 1561 } 1562 } 1563 } 1564 1565 String oid = toLowerCase(matchingRule.getOID()); 1566 orderingMatchingRules.put(oid, matchingRule); 1567 matchingRules.put(oid, matchingRule); 1568 1569 String name = matchingRule.getName(); 1570 if (name != null) 1571 { 1572 name = toLowerCase(name); 1573 orderingMatchingRules.put(name, matchingRule); 1574 matchingRules.put(name, matchingRule); 1575 } 1576 1577 // We'll use an attribute value including the normalized value 1578 // rather than the attribute type because otherwise it would use 1579 // a very expensive matching rule (OID first component match) 1580 // that would kill performance. 1581 String valueString = matchingRule.toString(); 1582 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1583 ByteString normValue = normalizationMatchingRule.normalizeValue( 1584 new ASN1OctetString(valueString)); 1585 matchingRuleSet.add(new AttributeValue(rawValue, normValue)); 1586 } 1587 } 1588 1589 1590 1591 /** 1592 * Deregisters the provided ordering matching rule definition with 1593 * this schema. 1594 * 1595 * @param matchingRule The ordering matching rule to deregister 1596 * with this schema. 1597 */ 1598 public void deregisterOrderingMatchingRule( 1599 OrderingMatchingRule matchingRule) 1600 { 1601 synchronized (matchingRules) 1602 { 1603 String oid = matchingRule.getOID(); 1604 orderingMatchingRules.remove(oid, matchingRule); 1605 matchingRules.remove(oid, matchingRule); 1606 1607 String name = matchingRule.getName(); 1608 if (name != null) 1609 { 1610 name = toLowerCase(name); 1611 orderingMatchingRules.remove(name, matchingRule); 1612 matchingRules.remove(name, matchingRule); 1613 } 1614 1615 // We'll use an attribute value including the normalized value 1616 // rather than the attribute type because otherwise it would use 1617 // a very expensive matching rule (OID first component match) 1618 // that would kill performance. 1619 try 1620 { 1621 String valueString = matchingRule.toString(); 1622 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1623 ByteString normValue = 1624 normalizationMatchingRule.normalizeValue( 1625 new ASN1OctetString(valueString)); 1626 matchingRuleSet.remove(new AttributeValue(rawValue, 1627 normValue)); 1628 } 1629 catch (Exception e) 1630 { 1631 String valueString = matchingRule.toString(); 1632 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1633 ASN1OctetString normValue = 1634 new ASN1OctetString(toLowerCase(valueString)); 1635 matchingRuleSet.remove(new AttributeValue(rawValue, 1636 normValue)); 1637 } 1638 } 1639 } 1640 1641 1642 1643 /** 1644 * Retrieves the substring matching rule definitions for this 1645 * schema, as a mapping between the lowercase names and OIDs for the 1646 * matching rule and the matching rule itself. Each matching rule 1647 * may be associated with multiple keys (once for the OID and again 1648 * for each name). The contents of the returned mapping must not be 1649 * altered. 1650 * 1651 * @return The substring matching rule definitions for this schema. 1652 */ 1653 public ConcurrentHashMap<String,SubstringMatchingRule> 1654 getSubstringMatchingRules() 1655 { 1656 return substringMatchingRules; 1657 } 1658 1659 1660 1661 /** 1662 * Retrieves the substring matching rule definition with the 1663 * specified name or OID. 1664 * 1665 * @param lowerName The name or OID of the matching rule to 1666 * retrieve, formatted in all lowercase 1667 * characters. 1668 * 1669 * @return The requested matching rule, or <CODE>null</CODE> if no 1670 * substring matching rule is registered with the provided 1671 * name or OID. 1672 */ 1673 public SubstringMatchingRule getSubstringMatchingRule( 1674 String lowerName) 1675 { 1676 return substringMatchingRules.get(lowerName); 1677 } 1678 1679 1680 1681 /** 1682 * Registers the provided substring matching rule with this schema. 1683 * 1684 * @param matchingRule The substring matching rule to 1685 * register. 1686 * @param overwriteExisting Indicates whether to overwrite an 1687 * existing mapping if there are any 1688 * conflicts (i.e., another matching rule 1689 * with the same OID or name). 1690 * 1691 * @throws DirectoryException If a conflict is encountered and the 1692 * <CODE>overwriteExisting</CODE> flag 1693 * is set to <CODE>false</CODE> 1694 */ 1695 public void registerSubstringMatchingRule( 1696 SubstringMatchingRule matchingRule, 1697 boolean overwriteExisting) 1698 throws DirectoryException 1699 { 1700 synchronized (matchingRules) 1701 { 1702 if (! overwriteExisting) 1703 { 1704 String oid = toLowerCase(matchingRule.getOID()); 1705 if (matchingRules.containsKey(oid)) 1706 { 1707 MatchingRule conflictingRule = matchingRules.get(oid); 1708 1709 Message message = ERR_SCHEMA_CONFLICTING_MR_OID. 1710 get(matchingRule.getNameOrOID(), oid, 1711 conflictingRule.getNameOrOID()); 1712 throw new DirectoryException( 1713 ResultCode.CONSTRAINT_VIOLATION, message); 1714 } 1715 1716 String name = matchingRule.getName(); 1717 if (name != null) 1718 { 1719 name = toLowerCase(name); 1720 if (matchingRules.containsKey(name)) 1721 { 1722 MatchingRule conflictingRule = matchingRules.get(name); 1723 1724 Message message = ERR_SCHEMA_CONFLICTING_MR_NAME. 1725 get(matchingRule.getOID(), name, 1726 conflictingRule.getOID()); 1727 throw new DirectoryException( 1728 ResultCode.CONSTRAINT_VIOLATION, message); 1729 } 1730 } 1731 } 1732 1733 String oid = toLowerCase(matchingRule.getOID()); 1734 substringMatchingRules.put(oid, matchingRule); 1735 matchingRules.put(oid, matchingRule); 1736 1737 String name = matchingRule.getName(); 1738 if (name != null) 1739 { 1740 name = toLowerCase(name); 1741 substringMatchingRules.put(name, matchingRule); 1742 matchingRules.put(name, matchingRule); 1743 } 1744 1745 // We'll use an attribute value including the normalized value 1746 // rather than the attribute type because otherwise it would use 1747 // a very expensive matching rule (OID first component match) 1748 // that would kill performance. 1749 String valueString = matchingRule.toString(); 1750 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1751 ByteString normValue = normalizationMatchingRule.normalizeValue( 1752 new ASN1OctetString(valueString)); 1753 matchingRuleSet.add(new AttributeValue(rawValue, normValue)); 1754 } 1755 } 1756 1757 1758 1759 /** 1760 * Deregisters the provided substring matching rule definition with 1761 * this schema. 1762 * 1763 * @param matchingRule The substring matching rule to deregister 1764 * with this schema. 1765 */ 1766 public void deregisterSubstringMatchingRule( 1767 SubstringMatchingRule matchingRule) 1768 { 1769 synchronized (matchingRules) 1770 { 1771 String oid = matchingRule.getOID(); 1772 substringMatchingRules.remove(oid, matchingRule); 1773 matchingRules.remove(oid, matchingRule); 1774 1775 String name = matchingRule.getName(); 1776 if (name != null) 1777 { 1778 name = toLowerCase(name); 1779 substringMatchingRules.remove(name, matchingRule); 1780 matchingRules.remove(name, matchingRule); 1781 } 1782 1783 // We'll use an attribute value including the normalized value 1784 // rather than the attribute type because otherwise it would use 1785 // a very expensive matching rule (OID first component match) 1786 // that would kill performance. 1787 try 1788 { 1789 String valueString = matchingRule.toString(); 1790 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1791 ByteString normValue = 1792 normalizationMatchingRule.normalizeValue( 1793 new ASN1OctetString(valueString)); 1794 matchingRuleSet.remove(new AttributeValue(rawValue, 1795 normValue)); 1796 } 1797 catch (Exception e) 1798 { 1799 String valueString = matchingRule.toString(); 1800 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1801 ASN1OctetString normValue = 1802 new ASN1OctetString(toLowerCase(valueString)); 1803 matchingRuleSet.remove(new AttributeValue(rawValue, 1804 normValue)); 1805 } 1806 } 1807 } 1808 1809 1810 1811 /** 1812 * Retrieves the matching rule use definitions for this schema, as a 1813 * mapping between the matching rule for the matching rule use 1814 * definition and the matching rule use itself. Each matching rule 1815 * use should only be present once, since its only key is its 1816 * matching rule. The contents of the returned mapping must not be 1817 * altered. 1818 * 1819 * @return The matching rule use definitions for this schema. 1820 */ 1821 public ConcurrentHashMap<MatchingRule,MatchingRuleUse> 1822 getMatchingRuleUses() 1823 { 1824 return matchingRuleUses; 1825 } 1826 1827 1828 1829 /** 1830 * Retrieves the set of defined matching rule uses for this schema. 1831 * 1832 * @return The set of defined matching rule uses for this schema. 1833 */ 1834 public LinkedHashSet<AttributeValue> getMatchingRuleUseSet() 1835 { 1836 return matchingRuleUseSet; 1837 } 1838 1839 1840 1841 /** 1842 * Indicates whether this schema definition includes a matching rule 1843 * use for the provided matching rule. 1844 * 1845 * @param matchingRule The matching rule for which to make the 1846 * determination. 1847 * 1848 * @return {@code true} if this schema contains a matching rule use 1849 * for the provided matching rule, or {@code false} if not. 1850 */ 1851 public boolean hasMatchingRuleUse(MatchingRule matchingRule) 1852 { 1853 return matchingRuleUses.containsKey(matchingRule); 1854 } 1855 1856 1857 1858 /** 1859 * Retrieves the matching rule use definition for the specified 1860 * matching rule. 1861 * 1862 * @param matchingRule The matching rule for which to retrieve the 1863 * matching rule use definition. 1864 * 1865 * @return The matching rule use definition, or <CODE>null</CODE> 1866 * if none exists for the specified matching rule. 1867 */ 1868 public MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule) 1869 { 1870 return matchingRuleUses.get(matchingRule); 1871 } 1872 1873 1874 1875 /** 1876 * Registers the provided matching rule use definition with this 1877 * schema. 1878 * 1879 * @param matchingRuleUse The matching rule use definition to 1880 * register. 1881 * @param overwriteExisting Indicates whether to overwrite an 1882 * existing mapping if there are any 1883 * conflicts (i.e., another matching rule 1884 * use with the same matching rule). 1885 * 1886 * @throws DirectoryException If a conflict is encountered and the 1887 * <CODE>overwriteExisting</CODE> flag 1888 * is set to <CODE>false</CODE> 1889 */ 1890 public void registerMatchingRuleUse(MatchingRuleUse matchingRuleUse, 1891 boolean overwriteExisting) 1892 throws DirectoryException 1893 { 1894 synchronized (matchingRuleUses) 1895 { 1896 MatchingRule matchingRule = matchingRuleUse.getMatchingRule(); 1897 1898 if (! overwriteExisting) 1899 { 1900 if (matchingRuleUses.containsKey(matchingRule)) 1901 { 1902 MatchingRuleUse conflictingUse = 1903 matchingRuleUses.get(matchingRule); 1904 1905 Message message = ERR_SCHEMA_CONFLICTING_MATCHING_RULE_USE. 1906 get(matchingRuleUse.getName(), 1907 matchingRule.getNameOrOID(), 1908 conflictingUse.getName()); 1909 throw new DirectoryException( 1910 ResultCode.CONSTRAINT_VIOLATION, message); 1911 } 1912 } 1913 1914 matchingRuleUses.put(matchingRule, matchingRuleUse); 1915 1916 // We'll use an attribute value including the normalized value 1917 // rather than the attribute type because otherwise it would use 1918 // a very expensive matching rule (OID first component match) 1919 // that would kill performance. 1920 String valueString = matchingRuleUse.getDefinition(); 1921 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1922 ByteString normValue = normalizationMatchingRule.normalizeValue( 1923 new ASN1OctetString(valueString)); 1924 matchingRuleUseSet.add(new AttributeValue(rawValue, normValue)); 1925 } 1926 } 1927 1928 1929 1930 /** 1931 * Deregisters the provided matching rule use definition with this 1932 * schema. 1933 * 1934 * @param matchingRuleUse The matching rule use to deregister with 1935 * this schema. 1936 */ 1937 public void deregisterMatchingRuleUse( 1938 MatchingRuleUse matchingRuleUse) 1939 { 1940 synchronized (matchingRuleUses) 1941 { 1942 matchingRuleUses.remove(matchingRuleUse.getMatchingRule(), 1943 matchingRuleUse); 1944 1945 // We'll use an attribute value including the normalized value 1946 // rather than the attribute type because otherwise it would use 1947 // a very expensive matching rule (OID first component match) 1948 // that would kill performance. 1949 try 1950 { 1951 String valueString = matchingRuleUse.getDefinition(); 1952 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1953 ByteString normValue = 1954 normalizationMatchingRule.normalizeValue( 1955 new ASN1OctetString(valueString)); 1956 matchingRuleUseSet.remove(new AttributeValue(rawValue, 1957 normValue)); 1958 } 1959 catch (Exception e) 1960 { 1961 String valueString = matchingRuleUse.getDefinition(); 1962 ASN1OctetString rawValue = new ASN1OctetString(valueString); 1963 ASN1OctetString normValue = 1964 new ASN1OctetString(toLowerCase(valueString)); 1965 matchingRuleUseSet.remove(new AttributeValue(rawValue, 1966 normValue)); 1967 } 1968 } 1969 } 1970 1971 1972 1973 /** 1974 * Retrieves the DIT content rule definitions for this schema, as a 1975 * mapping between the objectclass for the rule and the DIT content 1976 * rule itself. Each DIT content rule should only be present once, 1977 * since its only key is its objectclass. The contents of the 1978 * returned mapping must not be altered. 1979 * 1980 * @return The DIT content rule definitions for this schema. 1981 */ 1982 public ConcurrentHashMap<ObjectClass,DITContentRule> 1983 getDITContentRules() 1984 { 1985 return ditContentRules; 1986 } 1987 1988 1989 1990 /** 1991 * Retrieves the set of defined DIT content rules for this schema. 1992 * 1993 * @return The set of defined DIT content rules for this schema. 1994 */ 1995 public LinkedHashSet<AttributeValue> getDITContentRuleSet() 1996 { 1997 return ditContentRuleSet; 1998 } 1999 2000 2001 2002 /** 2003 * Indicates whether this schema definition includes a DIT content 2004 * rule for the provided objectclass. 2005 * 2006 * @param objectClass The objectclass for which to make the 2007 * determination. 2008 * 2009 * @return {@code true} if this schema contains a DIT content rule 2010 * for the provided objectclass, or {@code false} if not. 2011 */ 2012 public boolean hasDITContentRule(ObjectClass objectClass) 2013 { 2014 return ditContentRules.containsKey(objectClass); 2015 } 2016 2017 2018 2019 /** 2020 * Retrieves the DIT content rule definition for the specified 2021 * objectclass. 2022 * 2023 * @param objectClass The objectclass for the DIT content rule to 2024 * retrieve. 2025 * 2026 * @return The requested DIT content rule, or <CODE>null</CODE> if 2027 * no DIT content rule is registered with the provided 2028 * objectclass. 2029 */ 2030 public DITContentRule getDITContentRule(ObjectClass objectClass) 2031 { 2032 return ditContentRules.get(objectClass); 2033 } 2034 2035 2036 2037 /** 2038 * Registers the provided DIT content rule definition with this 2039 * schema. 2040 * 2041 * @param ditContentRule The DIT content rule to register. 2042 * @param overwriteExisting Indicates whether to overwrite an 2043 * existing mapping if there are any 2044 * conflicts (i.e., another DIT content 2045 * rule with the same objectclass). 2046 * 2047 * @throws DirectoryException If a conflict is encountered and the 2048 * <CODE>overwriteExisting</CODE> flag 2049 * is set to <CODE>false</CODE> 2050 */ 2051 public void registerDITContentRule(DITContentRule ditContentRule, 2052 boolean overwriteExisting) 2053 throws DirectoryException 2054 { 2055 synchronized (ditContentRules) 2056 { 2057 ObjectClass objectClass = ditContentRule.getStructuralClass(); 2058 2059 if (! overwriteExisting) 2060 { 2061 if (ditContentRules.containsKey(objectClass)) 2062 { 2063 DITContentRule conflictingRule = 2064 ditContentRules.get(objectClass); 2065 2066 Message message = ERR_SCHEMA_CONFLICTING_DIT_CONTENT_RULE. 2067 get(ditContentRule.getName(), 2068 objectClass.getNameOrOID(), 2069 conflictingRule.getName()); 2070 throw new DirectoryException( 2071 ResultCode.CONSTRAINT_VIOLATION, message); 2072 } 2073 } 2074 2075 ditContentRules.put(objectClass, ditContentRule); 2076 2077 // We'll use an attribute value including the normalized value 2078 // rather than the attribute type because otherwise it would use 2079 // a very expensive matching rule (OID first component match) 2080 // that would kill performance. 2081 String valueString = ditContentRule.getDefinition(); 2082 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2083 ByteString normValue = normalizationMatchingRule.normalizeValue( 2084 new ASN1OctetString(valueString)); 2085 ditContentRuleSet.add(new AttributeValue(rawValue, normValue)); 2086 } 2087 } 2088 2089 2090 2091 /** 2092 * Deregisters the provided DIT content rule definition with this 2093 * schema. 2094 * 2095 * @param ditContentRule The DIT content rule to deregister with 2096 * this schema. 2097 */ 2098 public void deregisterDITContentRule(DITContentRule ditContentRule) 2099 { 2100 synchronized (ditContentRules) 2101 { 2102 ditContentRules.remove(ditContentRule.getStructuralClass(), 2103 ditContentRule); 2104 2105 // We'll use an attribute value including the normalized value 2106 // rather than the attribute type because otherwise it would use 2107 // a very expensive matching rule (OID first component match) 2108 // that would kill performance. 2109 try 2110 { 2111 String valueString = ditContentRule.getDefinition(); 2112 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2113 ByteString normValue = 2114 normalizationMatchingRule.normalizeValue( 2115 new ASN1OctetString(valueString)); 2116 ditContentRuleSet.remove(new AttributeValue(rawValue, 2117 normValue)); 2118 } 2119 catch (Exception e) 2120 { 2121 String valueString = ditContentRule.getDefinition(); 2122 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2123 ASN1OctetString normValue = 2124 new ASN1OctetString(toLowerCase(valueString)); 2125 ditContentRuleSet.remove(new AttributeValue(rawValue, 2126 normValue)); 2127 } 2128 } 2129 } 2130 2131 2132 2133 /** 2134 * Retrieves the set of defined DIT structure rules for this schema. 2135 * 2136 * @return The set of defined DIT structure rules for this schema. 2137 */ 2138 public LinkedHashSet<AttributeValue> getDITStructureRuleSet() 2139 { 2140 return ditStructureRuleSet; 2141 } 2142 2143 2144 2145 /** 2146 * Retrieves the DIT structure rule definitions for this schema, as 2147 * a mapping between the rule ID for the rule and the DIT structure 2148 * rule itself. Each DIT structure rule should only be present 2149 * once, since its only key is its rule ID. The contents of the 2150 * returned mapping must not be altered. 2151 * 2152 * @return The DIT structure rule definitions for this schema. 2153 */ 2154 public ConcurrentHashMap<Integer,DITStructureRule> 2155 getDITStructureRulesByID() 2156 { 2157 return ditStructureRulesByID; 2158 } 2159 2160 2161 2162 /** 2163 * Retrieves the DIT structure rule definitions for this schema, as 2164 * a mapping between the name form for the rule and the DIT 2165 * structure rule itself. Each DIT structure rule should only be 2166 * present once, since its only key is its name form. The contents 2167 * of the returned mapping must not be altered. 2168 * 2169 * @return The DIT structure rule definitions for this schema. 2170 */ 2171 public ConcurrentHashMap<NameForm,DITStructureRule> 2172 getDITStructureRulesByNameForm() 2173 { 2174 return ditStructureRulesByNameForm; 2175 } 2176 2177 2178 2179 /** 2180 * Indicates whether this schema definition includes a DIT structure 2181 * rule with the provided rule ID. 2182 * 2183 * @param ruleID The rule ID for which to make the determination. 2184 * 2185 * @return {@code true} if this schema contains a DIT structure 2186 * rule with the provided rule ID, or {@code false} if not. 2187 */ 2188 public boolean hasDITStructureRule(int ruleID) 2189 { 2190 return ditStructureRulesByID.containsKey(ruleID); 2191 } 2192 2193 2194 2195 /** 2196 * Indicates whether this schema definition includes a DIT structure 2197 * rule for the provided name form. 2198 * 2199 * @param nameForm The name form for which to make the 2200 * determination. 2201 * 2202 * @return {@code true} if this schema contains a DIT structure 2203 * rule for the provided name form, or {@code false} if 2204 * not. 2205 */ 2206 public boolean hasDITStructureRule(NameForm nameForm) 2207 { 2208 return ditStructureRulesByNameForm.containsKey(nameForm); 2209 } 2210 2211 2212 2213 /** 2214 * Retrieves the DIT structure rule definition with the provided 2215 * rule ID. 2216 * 2217 * @param ruleID The rule ID for the DIT structure rule to 2218 * retrieve. 2219 * 2220 * @return The requested DIT structure rule, or <CODE>null</CODE> 2221 * if no DIT structure rule is registered with the provided 2222 * rule ID. 2223 */ 2224 public DITStructureRule getDITStructureRule(int ruleID) 2225 { 2226 return ditStructureRulesByID.get(ruleID); 2227 } 2228 2229 2230 2231 /** 2232 * Retrieves the DIT structure rule definition for the provided name 2233 * form. 2234 * 2235 * @param nameForm The name form for the DIT structure rule to 2236 * retrieve. 2237 * 2238 * @return The requested DIT structure rule, or <CODE>null</CODE> 2239 * if no DIT structure rule is registered with the provided 2240 * name form. 2241 */ 2242 public DITStructureRule getDITStructureRule(NameForm nameForm) 2243 { 2244 return ditStructureRulesByNameForm.get(nameForm); 2245 } 2246 2247 2248 2249 /** 2250 * Registers the provided DIT structure rule definition with this 2251 * schema. 2252 * 2253 * @param ditStructureRule The DIT structure rule to register. 2254 * @param overwriteExisting Indicates whether to overwrite an 2255 * existing mapping if there are any 2256 * conflicts (i.e., another DIT structure 2257 * rule with the same name form). 2258 * 2259 * @throws DirectoryException If a conflict is encountered and the 2260 * <CODE>overwriteExisting</CODE> flag 2261 * is set to <CODE>false</CODE> 2262 */ 2263 public void registerDITStructureRule( 2264 DITStructureRule ditStructureRule, 2265 boolean overwriteExisting) 2266 throws DirectoryException 2267 { 2268 synchronized (ditStructureRulesByNameForm) 2269 { 2270 NameForm nameForm = ditStructureRule.getNameForm(); 2271 int ruleID = ditStructureRule.getRuleID(); 2272 2273 if (! overwriteExisting) 2274 { 2275 if (ditStructureRulesByNameForm.containsKey(nameForm)) 2276 { 2277 DITStructureRule conflictingRule = 2278 ditStructureRulesByNameForm.get(nameForm); 2279 2280 Message message = 2281 ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_NAME_FORM. 2282 get(ditStructureRule.getNameOrRuleID(), 2283 nameForm.getNameOrOID(), 2284 conflictingRule.getNameOrRuleID()); 2285 throw new DirectoryException( 2286 ResultCode.CONSTRAINT_VIOLATION, message); 2287 } 2288 2289 if (ditStructureRulesByID.containsKey(ruleID)) 2290 { 2291 DITStructureRule conflictingRule = 2292 ditStructureRulesByID.get(ruleID); 2293 2294 Message message = 2295 ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID. 2296 get(ditStructureRule.getNameOrRuleID(), ruleID, 2297 conflictingRule.getNameOrRuleID()); 2298 throw new DirectoryException( 2299 ResultCode.CONSTRAINT_VIOLATION, message); 2300 } 2301 } 2302 2303 ditStructureRulesByNameForm.put(nameForm, ditStructureRule); 2304 ditStructureRulesByID.put(ruleID, ditStructureRule); 2305 2306 // We'll use an attribute value including the normalized value 2307 // rather than the attribute type because otherwise it would use 2308 // a very expensive matching rule (OID first component match) 2309 // that would kill performance. 2310 String valueString = ditStructureRule.getDefinition(); 2311 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2312 ByteString normValue = normalizationMatchingRule.normalizeValue( 2313 new ASN1OctetString(valueString)); 2314 ditStructureRuleSet.add(new AttributeValue(rawValue, 2315 normValue)); 2316 } 2317 } 2318 2319 2320 2321 /** 2322 * Deregisters the provided DIT structure rule definition with this 2323 * schema. 2324 * 2325 * @param ditStructureRule The DIT structure rule to deregister 2326 * with this schema. 2327 */ 2328 public void deregisterDITStructureRule( 2329 DITStructureRule ditStructureRule) 2330 { 2331 synchronized (ditStructureRulesByNameForm) 2332 { 2333 ditStructureRulesByNameForm.remove( 2334 ditStructureRule.getNameForm(), ditStructureRule); 2335 ditStructureRulesByID.remove(ditStructureRule.getRuleID(), 2336 ditStructureRule); 2337 2338 // We'll use an attribute value including the normalized value 2339 // rather than the attribute type because otherwise it would use 2340 // a very expensive matching rule (OID first component match) 2341 // that would kill performance. 2342 try 2343 { 2344 String valueString = ditStructureRule.getDefinition(); 2345 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2346 ByteString normValue = 2347 normalizationMatchingRule.normalizeValue( 2348 new ASN1OctetString(valueString)); 2349 ditStructureRuleSet.remove(new AttributeValue(rawValue, 2350 normValue)); 2351 } 2352 catch (Exception e) 2353 { 2354 String valueString = ditStructureRule.getDefinition(); 2355 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2356 ASN1OctetString normValue = 2357 new ASN1OctetString(toLowerCase(valueString)); 2358 ditStructureRuleSet.remove(new AttributeValue(rawValue, 2359 normValue)); 2360 } 2361 } 2362 } 2363 2364 2365 2366 /** 2367 * Retrieves the set of defined name forms for this schema. 2368 * 2369 * @return The set of defined name forms for this schema. 2370 */ 2371 public LinkedHashSet<AttributeValue> getNameFormSet() 2372 { 2373 return nameFormSet; 2374 } 2375 2376 2377 2378 /** 2379 * Retrieves the name form definitions for this schema, as a mapping 2380 * between the objectclass for the name form and the name form 2381 * itself. Each name form should only be present once, since its 2382 * only key is its objectclass. The contents of the returned 2383 * mapping must not be altered. 2384 * 2385 * @return The name form definitions for this schema. 2386 */ 2387 public ConcurrentHashMap<ObjectClass,NameForm> 2388 getNameFormsByObjectClass() 2389 { 2390 return nameFormsByOC; 2391 } 2392 2393 2394 2395 /** 2396 * Retrieves the name form definitions for this schema, as a mapping 2397 * between the names/OID for the name form and the name form itself. 2398 * Each name form may be present multiple times with different names 2399 * and its OID. The contents of the returned mapping must not be 2400 * altered. 2401 * 2402 * @return The name form definitions for this schema. 2403 */ 2404 public ConcurrentHashMap<String,NameForm> getNameFormsByNameOrOID() 2405 { 2406 return nameFormsByName; 2407 } 2408 2409 2410 2411 /** 2412 * Indicates whether this schema definition includes a name form for 2413 * the specified objectclass. 2414 * 2415 * @param objectClass The objectclass for which to make the 2416 * determination. 2417 * 2418 * @return {@code true} if this schema contains a name form for the 2419 * provided objectclass, or {@code false} if not. 2420 */ 2421 public boolean hasNameForm(ObjectClass objectClass) 2422 { 2423 return nameFormsByOC.containsKey(objectClass); 2424 } 2425 2426 2427 2428 /** 2429 * Indicates whether this schema definition includes a name form 2430 * with the specified name or OID. 2431 * 2432 * @param lowerName The name or OID for which to make the 2433 * determination, formatted in all lowercase 2434 * characters. 2435 * 2436 * @return {@code true} if this schema contains a name form with 2437 * the provided name or OID, or {@code false} if not. 2438 */ 2439 public boolean hasNameForm(String lowerName) 2440 { 2441 return nameFormsByName.containsKey(lowerName); 2442 } 2443 2444 2445 2446 /** 2447 * Retrieves the name form definition for the specified objectclass. 2448 * 2449 * @param objectClass The objectclass for the name form to 2450 * retrieve. 2451 * 2452 * @return The requested name form, or <CODE>null</CODE> if no name 2453 * form is registered with the provided objectClass. 2454 */ 2455 public NameForm getNameForm(ObjectClass objectClass) 2456 { 2457 return nameFormsByOC.get(objectClass); 2458 } 2459 2460 2461 2462 /** 2463 * Retrieves the name form definition with the provided name or OID. 2464 * 2465 * @param lowerName The name or OID of the name form to retrieve, 2466 * formatted in all lowercase characters. 2467 * 2468 * @return The requested name form, or <CODE>null</CODE> if no name 2469 * form is registered with the provided name or OID. 2470 */ 2471 public NameForm getNameForm(String lowerName) 2472 { 2473 return nameFormsByName.get(lowerName); 2474 } 2475 2476 2477 2478 /** 2479 * Registers the provided name form definition with this schema. 2480 * 2481 * @param nameForm The name form definition to register. 2482 * @param overwriteExisting Indicates whether to overwrite an 2483 * existing mapping if there are any 2484 * conflicts (i.e., another name form 2485 * with the same objectclass). 2486 * 2487 * @throws DirectoryException If a conflict is encountered and the 2488 * <CODE>overwriteExisting</CODE> flag 2489 * is set to <CODE>false</CODE> 2490 */ 2491 public void registerNameForm(NameForm nameForm, 2492 boolean overwriteExisting) 2493 throws DirectoryException 2494 { 2495 synchronized (nameFormsByOC) 2496 { 2497 ObjectClass objectClass = nameForm.getStructuralClass(); 2498 2499 if (! overwriteExisting) 2500 { 2501 if (nameFormsByOC.containsKey(objectClass)) 2502 { 2503 NameForm conflictingNameForm = 2504 nameFormsByOC.get(objectClass); 2505 2506 Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OC. 2507 get(nameForm.getNameOrOID(), objectClass.getNameOrOID(), 2508 conflictingNameForm.getNameOrOID()); 2509 throw new DirectoryException( 2510 ResultCode.CONSTRAINT_VIOLATION, message); 2511 } 2512 2513 String oid = toLowerCase(nameForm.getOID()); 2514 if (nameFormsByName.containsKey(oid)) 2515 { 2516 NameForm conflictingNameForm = nameFormsByName.get(oid); 2517 2518 Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID. 2519 get(nameForm.getNameOrOID(), oid, 2520 conflictingNameForm.getNameOrOID()); 2521 throw new DirectoryException( 2522 ResultCode.CONSTRAINT_VIOLATION, message); 2523 } 2524 2525 for (String name : nameForm.getNames().keySet()) 2526 { 2527 if (nameFormsByName.containsKey(name)) 2528 { 2529 NameForm conflictingNameForm = nameFormsByName.get(name); 2530 2531 Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_NAME. 2532 get(nameForm.getNameOrOID(), oid, 2533 conflictingNameForm.getNameOrOID()); 2534 throw new DirectoryException( 2535 ResultCode.CONSTRAINT_VIOLATION, message); 2536 } 2537 } 2538 } 2539 2540 nameFormsByOC.put(objectClass, nameForm); 2541 nameFormsByName.put(toLowerCase(nameForm.getOID()), nameForm); 2542 2543 for (String name : nameForm.getNames().keySet()) 2544 { 2545 nameFormsByName.put(name, nameForm); 2546 } 2547 2548 // We'll use an attribute value including the normalized value 2549 // rather than the attribute type because otherwise it would use 2550 // a very expensive matching rule (OID first component match) 2551 // that would kill performance. 2552 String valueString = nameForm.getDefinition(); 2553 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2554 ByteString normValue = normalizationMatchingRule.normalizeValue( 2555 new ASN1OctetString(valueString)); 2556 nameFormSet.add(new AttributeValue(rawValue, normValue)); 2557 } 2558 } 2559 2560 2561 2562 /** 2563 * Deregisters the provided name form definition with this schema. 2564 * 2565 * @param nameForm The name form definition to deregister. 2566 */ 2567 public void deregisterNameForm(NameForm nameForm) 2568 { 2569 synchronized (nameFormsByOC) 2570 { 2571 nameFormsByOC.remove(nameForm.getStructuralClass(), nameForm); 2572 nameFormsByName.remove(toLowerCase(nameForm.getOID()), 2573 nameForm); 2574 2575 for (String name : nameForm.getNames().keySet()) 2576 { 2577 nameFormsByName.remove(name, nameForm); 2578 } 2579 2580 // We'll use an attribute value including the normalized value 2581 // rather than the attribute type because otherwise it would use 2582 // a very expensive matching rule (OID first component match) 2583 // that would kill performance. 2584 try 2585 { 2586 String valueString = nameForm.getDefinition(); 2587 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2588 ByteString normValue = 2589 normalizationMatchingRule.normalizeValue( 2590 new ASN1OctetString(valueString)); 2591 nameFormSet.remove(new AttributeValue(rawValue, normValue)); 2592 } 2593 catch (Exception e) 2594 { 2595 String valueString = nameForm.getDefinition(); 2596 ASN1OctetString rawValue = new ASN1OctetString(valueString); 2597 ASN1OctetString normValue = 2598 new ASN1OctetString(toLowerCase(valueString)); 2599 nameFormSet.remove(new AttributeValue(rawValue, normValue)); 2600 } 2601 } 2602 } 2603 2604 2605 2606 /** 2607 * Retrieves the modification timestamp for the file in the schema 2608 * configuration directory with the oldest last modified time. 2609 * 2610 * @return The modification timestamp for the file in the schema 2611 * configuration directory with the oldest last modified 2612 * time. 2613 */ 2614 public long getOldestModificationTime() 2615 { 2616 return oldestModificationTime; 2617 } 2618 2619 2620 2621 /** 2622 * Sets the modification timestamp for the oldest file in the schema 2623 * configuration directory. 2624 * 2625 * @param oldestModificationTime The modification timestamp for 2626 * the oldest file in the schema 2627 * configuration directory. 2628 */ 2629 public void setOldestModificationTime(long oldestModificationTime) 2630 { 2631 this.oldestModificationTime = oldestModificationTime; 2632 } 2633 2634 2635 2636 /** 2637 * Retrieves the modification timestamp for the file in the schema 2638 * configuration directory with the youngest last modified time. 2639 * 2640 * @return The modification timestamp for the file in the schema 2641 * configuration directory with the youngest last modified 2642 * time. 2643 */ 2644 public long getYoungestModificationTime() 2645 { 2646 return youngestModificationTime; 2647 } 2648 2649 2650 2651 /** 2652 * Sets the modification timestamp for the youngest file in the 2653 * schema configuration directory. 2654 * 2655 * @param youngestModificationTime The modification timestamp for 2656 * the youngest file in the schema 2657 * configuration directory. 2658 */ 2659 public void setYoungestModificationTime( 2660 long youngestModificationTime) 2661 { 2662 this.youngestModificationTime = youngestModificationTime; 2663 } 2664 2665 2666 2667 /** 2668 * Recursively rebuilds all schema elements that are dependent upon 2669 * the provided element. This must be invoked whenever an existing 2670 * schema element is modified in order to ensure that any elements 2671 * that depend on it should also be recreated to reflect the change. 2672 * <BR><BR> 2673 * The following conditions create dependencies between schema 2674 * elements: 2675 * <UL> 2676 * <LI>If an attribute type references a superior attribute type, 2677 * then it is dependent upon that superior attribute 2678 * type.</LI> 2679 * <LI>If an objectclass requires or allows an attribute type, 2680 * then it is dependent upon that attribute type.</LI> 2681 * <LI>If a name form requires or allows an attribute type in the 2682 * RDN, then it is dependent upon that attribute type.</LI> 2683 * <LI>If a DIT content rule requires, allows, or forbids the use 2684 * of an attribute type, then it is dependent upon that 2685 * attribute type.</LI> 2686 * <LI>If a matching rule use references an attribute type, then 2687 * it is dependent upon that attribute type.</LI> 2688 * <LI>If an objectclass references a superior objectclass, then 2689 * it is dependent upon that superior objectclass.</LI> 2690 * <LI>If a name form references a structural objectclass, then it 2691 * is dependent upon that objectclass.</LI> 2692 * <LI>If a DIT content rule references a structural or auxiliary 2693 * objectclass, then it is dependent upon that 2694 * objectclass.</LI> 2695 * <LI>If a DIT structure rule references a name form, then it is 2696 * dependent upon that name form.</LI> 2697 * <LI>If a DIT structure rule references a superior DIT structure 2698 * rule, then it is dependent upon that superior DIT structure 2699 * rule.</LI> 2700 * </UL> 2701 * 2702 * @param element The element for which to recursively rebuild all 2703 * dependent elements. 2704 * 2705 * @throws DirectoryException If a problem occurs while rebuilding 2706 * any of the schema elements. 2707 */ 2708 public void rebuildDependentElements(SchemaFileElement element) 2709 throws DirectoryException 2710 { 2711 try 2712 { 2713 rebuildDependentElements(element, 0); 2714 } 2715 catch (DirectoryException de) 2716 { 2717 // If we got an error as a result of a circular reference, then 2718 // we want to make sure that the schema element we call out is 2719 // the one that is at the root of the problem. 2720 if (de.getMessageObject().getDescriptor().equals( 2721 ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE)) 2722 { 2723 Message message = ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE. 2724 get(element.getDefinition()); 2725 throw new DirectoryException(de.getResultCode(), message, 2726 de); 2727 } 2728 2729 2730 // It wasn't a circular reference error, so just re-throw the 2731 // exception. 2732 throw de; 2733 } 2734 } 2735 2736 2737 2738 /** 2739 * Recursively rebuilds all schema elements that are dependent upon 2740 * the provided element, increasing the depth for each level of 2741 * recursion to protect against errors due to circular references. 2742 * 2743 * @param element The element for which to recursively rebuild all 2744 * dependent elements. 2745 * @param depth The current recursion depth. 2746 * 2747 * @throws DirectoryException If a problem occurs while rebuilding 2748 * any of the schema elements. 2749 */ 2750 private void rebuildDependentElements(SchemaFileElement element, 2751 int depth) 2752 throws DirectoryException 2753 { 2754 if (depth > 20) 2755 { 2756 // FIXME -- Is this an appropriate maximum depth for detecting 2757 // circular references? 2758 Message message = ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get( 2759 element.getDefinition()); 2760 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 2761 message); 2762 } 2763 2764 2765 // Figure out what type of element we're dealing with and make the 2766 // appropriate determinations for that element. 2767 if (element instanceof AttributeType) 2768 { 2769 AttributeType t = (AttributeType) element; 2770 2771 for (AttributeType at : attributeTypes.values()) 2772 { 2773 if ((at.getSuperiorType() != null) && 2774 at.getSuperiorType().equals(t)) 2775 { 2776 AttributeType newAT = at.recreateFromDefinition(); 2777 deregisterAttributeType(at); 2778 registerAttributeType(newAT, true); 2779 rebuildDependentElements(at, depth+1); 2780 } 2781 } 2782 2783 for (ObjectClass oc : objectClasses.values()) 2784 { 2785 if (oc.getRequiredAttributes().contains(t) || 2786 oc.getOptionalAttributes().contains(t)) 2787 { 2788 ObjectClass newOC = oc.recreateFromDefinition(); 2789 deregisterObjectClass(oc); 2790 registerObjectClass(newOC, true); 2791 rebuildDependentElements(oc, depth+1); 2792 } 2793 } 2794 2795 for (NameForm nf : nameFormsByOC.values()) 2796 { 2797 if (nf.getRequiredAttributes().contains(t) || 2798 nf.getOptionalAttributes().contains(t)) 2799 { 2800 NameForm newNF = nf.recreateFromDefinition(); 2801 deregisterNameForm(nf); 2802 registerNameForm(newNF, true); 2803 rebuildDependentElements(nf, depth+1); 2804 } 2805 } 2806 2807 for (DITContentRule dcr : ditContentRules.values()) 2808 { 2809 if (dcr.getRequiredAttributes().contains(t) || 2810 dcr.getOptionalAttributes().contains(t) || 2811 dcr.getProhibitedAttributes().contains(t)) 2812 { 2813 DITContentRule newDCR = dcr.recreateFromDefinition(); 2814 deregisterDITContentRule(dcr); 2815 registerDITContentRule(newDCR, true); 2816 rebuildDependentElements(dcr, depth+1); 2817 } 2818 } 2819 2820 for (MatchingRuleUse mru : matchingRuleUses.values()) 2821 { 2822 if (mru.getAttributes().contains(t)) 2823 { 2824 MatchingRuleUse newMRU = mru.recreateFromDefinition(); 2825 deregisterMatchingRuleUse(mru); 2826 registerMatchingRuleUse(newMRU, true); 2827 rebuildDependentElements(mru, depth+1); 2828 } 2829 } 2830 } 2831 else if (element instanceof ObjectClass) 2832 { 2833 ObjectClass c = (ObjectClass) element; 2834 2835 for (ObjectClass oc : objectClasses.values()) 2836 { 2837 if ((oc.getSuperiorClass() != null) && 2838 oc.getSuperiorClass().equals(c)) 2839 { 2840 ObjectClass newOC = oc.recreateFromDefinition(); 2841 deregisterObjectClass(oc); 2842 registerObjectClass(newOC, true); 2843 rebuildDependentElements(oc, depth+1); 2844 } 2845 } 2846 2847 NameForm nf = nameFormsByOC.get(c); 2848 if (nf != null) 2849 { 2850 NameForm newNF = nf.recreateFromDefinition(); 2851 deregisterNameForm(nf); 2852 registerNameForm(newNF, true); 2853 rebuildDependentElements(nf, depth+1); 2854 } 2855 2856 for (DITContentRule dcr : ditContentRules.values()) 2857 { 2858 if (dcr.getStructuralClass().equals(c) || 2859 dcr.getAuxiliaryClasses().contains(c)) 2860 { 2861 DITContentRule newDCR = dcr.recreateFromDefinition(); 2862 deregisterDITContentRule(dcr); 2863 registerDITContentRule(newDCR, true); 2864 rebuildDependentElements(dcr, depth+1); 2865 } 2866 } 2867 } 2868 else if (element instanceof NameForm) 2869 { 2870 NameForm n = (NameForm) element; 2871 DITStructureRule dsr = ditStructureRulesByNameForm.get(n); 2872 if (dsr != null) 2873 { 2874 DITStructureRule newDSR = dsr.recreateFromDefinition(); 2875 deregisterDITStructureRule(dsr); 2876 registerDITStructureRule(newDSR, true); 2877 rebuildDependentElements(dsr, depth+1); 2878 } 2879 } 2880 else if (element instanceof DITStructureRule) 2881 { 2882 DITStructureRule d = (DITStructureRule) element; 2883 for (DITStructureRule dsr : ditStructureRulesByID.values()) 2884 { 2885 if (dsr.getSuperiorRules().contains(d)) 2886 { 2887 DITStructureRule newDSR = dsr.recreateFromDefinition(); 2888 deregisterDITStructureRule(dsr); 2889 registerDITStructureRule(newDSR, true); 2890 rebuildDependentElements(dsr, depth+1); 2891 } 2892 } 2893 } 2894 } 2895 2896 2897 2898 /** 2899 * Creates a new <CODE>Schema</CODE> object that is a duplicate of 2900 * this one. It elements may be added and removed from the 2901 * duplicate without impacting this version. 2902 * 2903 * @return A new <CODE>Schema</CODE> object that is a duplicate of 2904 * this one. 2905 */ 2906 public Schema duplicate() 2907 { 2908 Schema dupSchema = new Schema(); 2909 2910 dupSchema.attributeTypes.putAll(attributeTypes); 2911 dupSchema.subordinateTypes.putAll(subordinateTypes); 2912 dupSchema.objectClasses.putAll(objectClasses); 2913 dupSchema.syntaxes.putAll(syntaxes); 2914 dupSchema.matchingRules.putAll(matchingRules); 2915 dupSchema.approximateMatchingRules.putAll( 2916 approximateMatchingRules); 2917 dupSchema.equalityMatchingRules.putAll(equalityMatchingRules); 2918 dupSchema.orderingMatchingRules.putAll(orderingMatchingRules); 2919 dupSchema.substringMatchingRules.putAll(substringMatchingRules); 2920 dupSchema.matchingRuleUses.putAll(matchingRuleUses); 2921 dupSchema.ditContentRules.putAll(ditContentRules); 2922 dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID); 2923 dupSchema.ditStructureRulesByNameForm.putAll( 2924 ditStructureRulesByNameForm); 2925 dupSchema.nameFormsByOC.putAll(nameFormsByOC); 2926 dupSchema.nameFormsByName.putAll(nameFormsByName); 2927 dupSchema.syntaxSet.addAll(syntaxSet); 2928 dupSchema.attributeTypeSet.addAll(attributeTypeSet); 2929 dupSchema.ditContentRuleSet.addAll(ditContentRuleSet); 2930 dupSchema.ditStructureRuleSet.addAll(ditStructureRuleSet); 2931 dupSchema.matchingRuleSet.addAll(matchingRuleSet); 2932 dupSchema.matchingRuleUseSet.addAll(matchingRuleUseSet); 2933 dupSchema.nameFormSet.addAll(nameFormSet); 2934 dupSchema.objectClassSet.addAll(objectClassSet); 2935 dupSchema.oldestModificationTime = oldestModificationTime; 2936 dupSchema.youngestModificationTime = youngestModificationTime; 2937 if (extraAttributes != null) 2938 { 2939 dupSchema.extraAttributes = 2940 new HashMap<String, Attribute>(extraAttributes); 2941 } 2942 2943 return dupSchema; 2944 } 2945 2946 2947 /** 2948 * Get the extraAttributes stored in this schema. 2949 * 2950 * @return The extraAttributes stored in this schema. 2951 */ 2952 public Map<String, Attribute> getExtraAttributes() 2953 { 2954 return extraAttributes; 2955 } 2956 2957 2958 /** 2959 * Add a new extra Attribute for this schema. 2960 * 2961 * @param name The identifier of the extra Attribute. 2962 * 2963 * @param attr The extra attribute that must be added to 2964 * this Schema. 2965 */ 2966 public void addExtraAttribute(String name, Attribute attr) 2967 { 2968 extraAttributes.put(name, attr); 2969 } 2970 2971 2972 /** 2973 * Writes a single file containing all schema element definitions, 2974 * which can be used on startup to determine whether the schema 2975 * files were edited with the server offline. 2976 */ 2977 public static void writeConcatenatedSchema() 2978 { 2979 String concatFilePath = null; 2980 try 2981 { 2982 LinkedHashSet<String> attributeTypes = 2983 new LinkedHashSet<String>(); 2984 LinkedHashSet<String> objectClasses = 2985 new LinkedHashSet<String>(); 2986 LinkedHashSet<String> nameForms = new LinkedHashSet<String>(); 2987 LinkedHashSet<String> ditContentRules = 2988 new LinkedHashSet<String>(); 2989 LinkedHashSet<String> ditStructureRules = 2990 new LinkedHashSet<String>(); 2991 LinkedHashSet<String> matchingRuleUses = 2992 new LinkedHashSet<String>(); 2993 genConcatenatedSchema(attributeTypes, objectClasses, nameForms, 2994 ditContentRules, ditStructureRules, 2995 matchingRuleUses); 2996 2997 2998 File configFile = new File(DirectoryServer.getConfigFile()); 2999 File configDirectory = configFile.getParentFile(); 3000 File upgradeDirectory = new File(configDirectory, "upgrade"); 3001 upgradeDirectory.mkdir(); 3002 File concatFile = new File(upgradeDirectory, 3003 SCHEMA_CONCAT_FILE_NAME); 3004 concatFilePath = concatFile.getAbsolutePath(); 3005 3006 File tempFile = new File(concatFilePath + ".tmp"); 3007 BufferedWriter writer = 3008 new BufferedWriter(new FileWriter(tempFile, false)); 3009 writer.write("dn: " + DirectoryServer.getSchemaDN().toString()); 3010 writer.newLine(); 3011 writer.write("objectClass: top"); 3012 writer.newLine(); 3013 writer.write("objectClass: ldapSubentry"); 3014 writer.newLine(); 3015 writer.write("objectClass: subschema"); 3016 writer.newLine(); 3017 3018 for (String line : attributeTypes) 3019 { 3020 writer.write(ATTR_ATTRIBUTE_TYPES); 3021 writer.write(": "); 3022 writer.write(line); 3023 writer.newLine(); 3024 } 3025 3026 for (String line : objectClasses) 3027 { 3028 writer.write(ATTR_OBJECTCLASSES); 3029 writer.write(": "); 3030 writer.write(line); 3031 writer.newLine(); 3032 } 3033 3034 for (String line : nameForms) 3035 { 3036 writer.write(ATTR_NAME_FORMS); 3037 writer.write(": "); 3038 writer.write(line); 3039 writer.newLine(); 3040 } 3041 3042 for (String line : ditContentRules) 3043 { 3044 writer.write(ATTR_DIT_CONTENT_RULES); 3045 writer.write(": "); 3046 writer.write(line); 3047 writer.newLine(); 3048 } 3049 3050 for (String line : ditStructureRules) 3051 { 3052 writer.write(ATTR_DIT_STRUCTURE_RULES); 3053 writer.write(": "); 3054 writer.write(line); 3055 writer.newLine(); 3056 } 3057 3058 for (String line : matchingRuleUses) 3059 { 3060 writer.write(ATTR_MATCHING_RULE_USE); 3061 writer.write(": "); 3062 writer.write(line); 3063 writer.newLine(); 3064 } 3065 3066 writer.close(); 3067 3068 if (concatFile.exists()) 3069 { 3070 concatFile.delete(); 3071 } 3072 tempFile.renameTo(concatFile); 3073 } 3074 catch (Exception e) 3075 { 3076 if (debugEnabled()) 3077 { 3078 TRACER.debugCaught(DebugLogLevel.ERROR, e); 3079 } 3080 3081 // This is definitely not ideal, but it's not the end of the 3082 // world. The worst that should happen is that the schema 3083 // changes could potentially be sent to the other servers again 3084 // when this server is restarted, which shouldn't hurt anything. 3085 // Still, we should log a warning message. 3086 logError(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE.get( 3087 String.valueOf(concatFilePath), getExceptionMessage(e))); 3088 } 3089 } 3090 3091 3092 3093 /** 3094 * Reads the files contained in the schema directory and generates a 3095 * concatenated view of their contents in the provided sets. 3096 * 3097 * @param attributeTypes The set into which to place the 3098 * attribute types read from the schema 3099 * files. 3100 * @param objectClasses The set into which to place the object 3101 * classes read from the schema files. 3102 * @param nameForms The set into which to place the name 3103 * forms read from the schema files. 3104 * @param ditContentRules The set into which to place the DIT 3105 * content rules read from the schema 3106 * files. 3107 * @param ditStructureRules The set into which to place the DIT 3108 * structure rules read from the schema 3109 * files. 3110 * @param matchingRuleUses The set into which to place the 3111 * matching rule uses read from the 3112 * schema files. 3113 * 3114 * @throws IOException If a problem occurs while reading the 3115 * schema file elements. 3116 */ 3117 public static void genConcatenatedSchema( 3118 LinkedHashSet<String> attributeTypes, 3119 LinkedHashSet<String> objectClasses, 3120 LinkedHashSet<String> nameForms, 3121 LinkedHashSet<String> ditContentRules, 3122 LinkedHashSet<String> ditStructureRules, 3123 LinkedHashSet<String> matchingRuleUses) 3124 throws IOException 3125 { 3126 // Get a sorted list of the files in the schema directory. 3127 String schemaDirectory = 3128 SchemaConfigManager.getSchemaDirectoryPath(); 3129 TreeSet<String> schemaFileNames = new TreeSet<String>(); 3130 for (File f : new File(schemaDirectory).listFiles()) 3131 { 3132 if (f.isFile()) 3133 { 3134 schemaFileNames.add(f.getName()); 3135 } 3136 } 3137 3138 3139 // Open each of the files in order and read the elements that they 3140 // contain, appending them to the appropriate lists. 3141 for (String name : schemaFileNames) 3142 { 3143 // Read the contents of the file into a list with one schema 3144 // element per list element. 3145 LinkedList<StringBuilder> lines = 3146 new LinkedList<StringBuilder>(); 3147 BufferedReader reader = 3148 new BufferedReader(new FileReader( 3149 new File(schemaDirectory, name))); 3150 3151 while (true) 3152 { 3153 String line = reader.readLine(); 3154 if (line == null) 3155 { 3156 break; 3157 } 3158 else if (line.startsWith("#") || (line.length() == 0)) 3159 { 3160 continue; 3161 } 3162 else if (line.startsWith(" ")) 3163 { 3164 lines.getLast().append(line.substring(1)); 3165 } 3166 else 3167 { 3168 lines.add(new StringBuilder(line)); 3169 } 3170 } 3171 3172 reader.close(); 3173 3174 3175 // Iterate through each line in the list. Find the colon and 3176 // get the attribute name at the beginning. If it's someting 3177 // that we don't recognize, then skip it. Otherwise, add the 3178 // X-SCHEMA-FILE extension and add it to the appropriate schema 3179 // element list. 3180 for (StringBuilder buffer : lines) 3181 { 3182 // Get the line and add the X-SCHEMA-FILE extension to the end 3183 // of it. All of them should end with " )" but some might 3184 // have the parenthesis crammed up against the last character 3185 // so deal with that as well. 3186 String line = buffer.toString().trim(); 3187 if (line.endsWith(" )")) 3188 { 3189 line = line.substring(0, line.length()-1) + 3190 SCHEMA_PROPERTY_FILENAME + " '" + name + "' )"; 3191 } 3192 else if (line.endsWith(")")) 3193 { 3194 line = line.substring(0, line.length()-1) + " " + 3195 SCHEMA_PROPERTY_FILENAME + " '" + name + "' )"; 3196 } 3197 else 3198 { 3199 continue; 3200 } 3201 3202 String value; 3203 String lowerLine = toLowerCase(line); 3204 if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) 3205 { 3206 value = line.substring( 3207 ATTR_ATTRIBUTE_TYPES.length()+1).trim(); 3208 attributeTypes.add(value); 3209 } 3210 else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) 3211 { 3212 value = line.substring( 3213 ATTR_OBJECTCLASSES.length()+1).trim(); 3214 objectClasses.add(value); 3215 } 3216 else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) 3217 { 3218 value = line.substring(ATTR_NAME_FORMS.length()+1).trim(); 3219 nameForms.add(value); 3220 } 3221 else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) 3222 { 3223 value = line.substring( 3224 ATTR_DIT_CONTENT_RULES.length()+1).trim(); 3225 ditContentRules.add(value); 3226 } 3227 else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) 3228 { 3229 value = line.substring( 3230 ATTR_DIT_STRUCTURE_RULES.length()+1).trim(); 3231 ditStructureRules.add(value); 3232 } 3233 else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) 3234 { 3235 value = line.substring( 3236 ATTR_MATCHING_RULE_USE.length()+1).trim(); 3237 matchingRuleUses.add(value); 3238 } 3239 } 3240 } 3241 } 3242 3243 3244 3245 /** 3246 * Reads data from the specified concatenated schema file into the 3247 * provided sets. 3248 * 3249 * @param concatSchemaFile The path to the concatenated schema 3250 * file to be read. 3251 * @param attributeTypes The set into which to place the 3252 * attribute types read from the 3253 * concatenated schema file. 3254 * @param objectClasses The set into which to place the object 3255 * classes read from the concatenated 3256 * schema file. 3257 * @param nameForms The set into which to place the name 3258 * forms read from the concatenated 3259 * schema file. 3260 * @param ditContentRules The set into which to place the DIT 3261 * content rules read from the 3262 * concatenated schema file. 3263 * @param ditStructureRules The set into which to place the DIT 3264 * structure rules read from the 3265 * concatenated schema file. 3266 * @param matchingRuleUses The set into which to place the 3267 * matching rule uses read from the 3268 * concatenated schema file. 3269 * 3270 * @throws IOException If a problem occurs while reading the 3271 * schema file elements. 3272 */ 3273 public static void readConcatenatedSchema(String concatSchemaFile, 3274 LinkedHashSet<String> attributeTypes, 3275 LinkedHashSet<String> objectClasses, 3276 LinkedHashSet<String> nameForms, 3277 LinkedHashSet<String> ditContentRules, 3278 LinkedHashSet<String> ditStructureRules, 3279 LinkedHashSet<String> matchingRuleUses) 3280 throws IOException 3281 { 3282 BufferedReader reader = 3283 new BufferedReader(new FileReader(concatSchemaFile)); 3284 while (true) 3285 { 3286 String line = reader.readLine(); 3287 if (line == null) 3288 { 3289 break; 3290 } 3291 3292 String value; 3293 String lowerLine = toLowerCase(line); 3294 if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC)) 3295 { 3296 value = 3297 line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim(); 3298 attributeTypes.add(value); 3299 } 3300 else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC)) 3301 { 3302 value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim(); 3303 objectClasses.add(value); 3304 } 3305 else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC)) 3306 { 3307 value = line.substring(ATTR_NAME_FORMS.length()+1).trim(); 3308 nameForms.add(value); 3309 } 3310 else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC)) 3311 { 3312 value = line.substring( 3313 ATTR_DIT_CONTENT_RULES.length()+1).trim(); 3314 ditContentRules.add(value); 3315 } 3316 else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC)) 3317 { 3318 value = line.substring( 3319 ATTR_DIT_STRUCTURE_RULES.length()+1).trim(); 3320 ditStructureRules.add(value); 3321 } 3322 else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC)) 3323 { 3324 value = line.substring( 3325 ATTR_MATCHING_RULE_USE.length()+1).trim(); 3326 matchingRuleUses.add(value); 3327 } 3328 } 3329 3330 reader.close(); 3331 } 3332 3333 3334 3335 /** 3336 * Compares the provided sets of schema element definitions and 3337 * writes any differences found into the given list of 3338 * modifications. 3339 * 3340 * @param oldElements The set of elements of the specified type 3341 * read from the previous concatenated schema 3342 * files. 3343 * @param newElements The set of elements of the specified type 3344 * read from the server's current schema. 3345 * @param elementType The attribute type associated with the 3346 * schema element being compared. 3347 * @param mods The list of modifications into which any 3348 * identified differences should be written. 3349 */ 3350 public static void compareConcatenatedSchema( 3351 LinkedHashSet<String> oldElements, 3352 LinkedHashSet<String> newElements, 3353 AttributeType elementType, 3354 LinkedList<Modification> mods) 3355 { 3356 AttributeType attributeTypesType = 3357 DirectoryServer.getAttributeType(ATTR_ATTRIBUTE_TYPES_LC, 3358 true); 3359 3360 LinkedHashSet<AttributeValue> values = 3361 new LinkedHashSet<AttributeValue>(); 3362 for (String s : oldElements) 3363 { 3364 if (! newElements.contains(s)) 3365 { 3366 values.add(new AttributeValue(attributeTypesType, s)); 3367 } 3368 } 3369 3370 if (! values.isEmpty()) 3371 { 3372 mods.add(new Modification(ModificationType.DELETE, 3373 new Attribute(elementType, 3374 elementType.getNameOrOID(), 3375 values))); 3376 } 3377 3378 3379 values.clear(); 3380 for (String s : newElements) 3381 { 3382 if (! oldElements.contains(s)) 3383 { 3384 values.add(new AttributeValue(attributeTypesType, s)); 3385 } 3386 } 3387 3388 if (! values.isEmpty()) 3389 { 3390 mods.add(new Modification(ModificationType.ADD, 3391 new Attribute(elementType, 3392 elementType.getNameOrOID(), 3393 values))); 3394 } 3395 } 3396 3397 3398 3399 /** 3400 * Destroys the structures maintained by the schema so that they are 3401 * no longer usable. This should only be called at the end of the 3402 * server shutdown process, and it can help detect inappropriate 3403 * cached references. 3404 */ 3405 @org.opends.server.types.PublicAPI( 3406 stability=org.opends.server.types.StabilityLevel.PRIVATE, 3407 mayInstantiate=false, 3408 mayExtend=false, 3409 mayInvoke=true) 3410 public synchronized void destroy() 3411 { 3412 if (approximateMatchingRules != null) 3413 { 3414 approximateMatchingRules.clear(); 3415 approximateMatchingRules = null; 3416 } 3417 3418 if (attributeTypes != null) 3419 { 3420 attributeTypes.clear(); 3421 attributeTypes = null; 3422 } 3423 3424 if (attributeTypeSet != null) 3425 { 3426 attributeTypeSet.clear(); 3427 attributeTypeSet = null; 3428 } 3429 3430 if (ditContentRules != null) 3431 { 3432 ditContentRules.clear(); 3433 ditContentRules = null; 3434 } 3435 3436 if (ditContentRuleSet != null) 3437 { 3438 ditContentRuleSet.clear(); 3439 ditContentRuleSet = null; 3440 } 3441 3442 if (ditStructureRulesByID != null) 3443 { 3444 ditStructureRulesByID.clear(); 3445 ditStructureRulesByID = null; 3446 } 3447 3448 if (ditStructureRulesByNameForm != null) 3449 { 3450 ditStructureRulesByNameForm.clear(); 3451 ditStructureRulesByNameForm = null; 3452 } 3453 3454 if (ditStructureRuleSet != null) 3455 { 3456 ditStructureRuleSet.clear(); 3457 ditStructureRuleSet = null; 3458 } 3459 3460 if (equalityMatchingRules != null) 3461 { 3462 equalityMatchingRules.clear(); 3463 equalityMatchingRules = null; 3464 } 3465 3466 if (matchingRules != null) 3467 { 3468 matchingRules.clear(); 3469 matchingRules = null; 3470 } 3471 3472 if (matchingRuleSet != null) 3473 { 3474 matchingRuleSet.clear(); 3475 matchingRuleSet = null; 3476 } 3477 3478 if (matchingRuleUses != null) 3479 { 3480 matchingRuleUses.clear(); 3481 matchingRuleUses = null; 3482 } 3483 3484 if (matchingRuleUseSet != null) 3485 { 3486 matchingRuleUseSet.clear(); 3487 matchingRuleUseSet = null; 3488 } 3489 3490 if (nameFormsByName != null) 3491 { 3492 nameFormsByName.clear(); 3493 nameFormsByName = null; 3494 } 3495 3496 if (nameFormsByOC != null) 3497 { 3498 nameFormsByOC.clear(); 3499 nameFormsByOC = null; 3500 } 3501 3502 if (nameFormSet != null) 3503 { 3504 nameFormSet.clear(); 3505 nameFormSet = null; 3506 } 3507 3508 if (objectClasses != null) 3509 { 3510 objectClasses.clear(); 3511 objectClasses = null; 3512 } 3513 3514 if (objectClassSet != null) 3515 { 3516 objectClassSet.clear(); 3517 objectClassSet = null; 3518 } 3519 3520 if (orderingMatchingRules != null) 3521 { 3522 orderingMatchingRules.clear(); 3523 orderingMatchingRules = null; 3524 } 3525 3526 if (subordinateTypes != null) 3527 { 3528 subordinateTypes.clear(); 3529 subordinateTypes = null; 3530 } 3531 3532 if (substringMatchingRules != null) 3533 { 3534 substringMatchingRules.clear(); 3535 substringMatchingRules = null; 3536 } 3537 3538 if (extraAttributes != null) 3539 { 3540 extraAttributes.clear(); 3541 extraAttributes = null; 3542 } 3543 3544 if (syntaxes != null) 3545 { 3546 syntaxes.clear(); 3547 syntaxes = null; 3548 } 3549 3550 if (syntaxSet != null) 3551 { 3552 syntaxSet.clear(); 3553 syntaxSet = null; 3554 } 3555 } 3556 } 3557