001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.schema.registries.helper; 021 022import java.util.HashSet; 023import java.util.List; 024import java.util.Set; 025 026import org.apache.directory.api.i18n.I18n; 027import org.apache.directory.api.ldap.model.exception.LdapException; 028import org.apache.directory.api.ldap.model.exception.LdapSchemaException; 029import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes; 030import org.apache.directory.api.ldap.model.schema.AttributeType; 031import org.apache.directory.api.ldap.model.schema.LdapSyntax; 032import org.apache.directory.api.ldap.model.schema.MatchingRule; 033import org.apache.directory.api.ldap.model.schema.MutableAttributeType; 034import org.apache.directory.api.ldap.model.schema.UsageEnum; 035import org.apache.directory.api.ldap.model.schema.registries.AttributeTypeRegistry; 036import org.apache.directory.api.ldap.model.schema.registries.Registries; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040/** 041 * An helper class used to store all the methods associated with an AttributeType 042 * in relation with the Registries and SchemaManager. 043 * 044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 045 */ 046public final class AttributeTypeHelper 047{ 048 /** A logger for this class */ 049 private static final Logger LOG = LoggerFactory.getLogger( AttributeTypeHelper.class ); 050 051 private AttributeTypeHelper() 052 { 053 } 054 055 /** 056 * Inject the AttributeType into the Registries, updating the references to 057 * other SchemaObject 058 * 059 * If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX), 060 * an exception is thrown. 061 * 062 * @param attributeType The AttributeType to add to the Registries 063 * @param errors The errors we got while adding the AttributeType to the Registries 064 * @param registries The Registries 065 * @throws LdapException If the AttributeType is not valid 066 */ 067 public static void addToRegistries( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) throws LdapException 068 { 069 if ( registries != null ) 070 { 071 try 072 { 073 attributeType.unlock(); 074 AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry(); 075 076 // The superior 077 if ( !buildSuperior( attributeType, errors, registries ) ) 078 { 079 // We have had errors, let's stop here as we need a correct superior to continue 080 return; 081 } 082 083 // The Syntax 084 buildSyntax( attributeType, errors, registries ); 085 086 // The EQUALITY matching rule 087 buildEquality( attributeType, errors, registries ); 088 089 // The ORDERING matching rule 090 buildOrdering( attributeType, errors, registries ); 091 092 // The SUBSTR matching rule 093 buildSubstring( attributeType, errors, registries ); 094 095 // Check the USAGE 096 checkUsage( attributeType, errors ); 097 098 // Check the COLLECTIVE element 099 checkCollective( attributeType, errors ); 100 101 // Inject the attributeType into the oid/normalizer map 102 attributeTypeRegistry.addMappingFor( attributeType ); 103 104 // Register this AttributeType into the Descendant map 105 attributeTypeRegistry.registerDescendants( attributeType, attributeType.getSuperior() ); 106 107 /** 108 * Add the AT references (using and usedBy) : 109 * AT -> MR (for EQUALITY, ORDERING and SUBSTR) 110 * AT -> S 111 * AT -> AT 112 */ 113 if ( attributeType.getEquality() != null ) 114 { 115 registries.addReference( attributeType, attributeType.getEquality() ); 116 } 117 118 if ( attributeType.getOrdering() != null ) 119 { 120 registries.addReference( attributeType, attributeType.getOrdering() ); 121 } 122 123 if ( attributeType.getSubstring() != null ) 124 { 125 registries.addReference( attributeType, attributeType.getSubstring() ); 126 } 127 128 if ( attributeType.getSyntax() != null ) 129 { 130 registries.addReference( attributeType, attributeType.getSyntax() ); 131 } 132 133 if ( attributeType.getSuperior() != null ) 134 { 135 registries.addReference( attributeType, attributeType.getSuperior() ); 136 } 137 } 138 finally 139 { 140 attributeType.lock(); 141 } 142 } 143 } 144 145 146 /** 147 * Build the Superior AttributeType reference for an AttributeType 148 */ 149 private static boolean buildSuperior( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) 150 { 151 MutableAttributeType currentSuperior; 152 AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry(); 153 154 String superiorOid = attributeType.getSuperiorOid(); 155 156 if ( superiorOid != null ) 157 { 158 // This AT has a superior 159 try 160 { 161 currentSuperior = ( MutableAttributeType ) attributeTypeRegistry.lookup( superiorOid ); 162 } 163 catch ( Exception e ) 164 { 165 // Not allowed. 166 String msg = I18n.err( I18n.ERR_04303, superiorOid, attributeType.getName() ); 167 168 LdapSchemaException ldapSchemaException = new LdapSchemaException( 169 LdapSchemaExceptionCodes.AT_NONEXISTENT_SUPERIOR, msg, e ); 170 ldapSchemaException.setSourceObject( attributeType ); 171 ldapSchemaException.setRelatedId( superiorOid ); 172 errors.add( ldapSchemaException ); 173 LOG.info( msg ); 174 175 // Get out now 176 return false; 177 } 178 179 if ( currentSuperior != null ) 180 { 181 // a special case : if the superior is collective, this is an error 182 if ( currentSuperior.isCollective() ) 183 { 184 String msg = I18n.err( I18n.ERR_04482_CANNOT_SUBTYPE_COLLECTIVE, 185 currentSuperior, attributeType.getName() ); 186 187 LdapSchemaException ldapSchemaException = new LdapSchemaException( 188 LdapSchemaExceptionCodes.AT_CANNOT_SUBTYPE_COLLECTIVE_AT, msg ); 189 ldapSchemaException.setSourceObject( attributeType ); 190 errors.add( ldapSchemaException ); 191 LOG.info( msg ); 192 193 return false; 194 } 195 196 attributeType.setSuperior( currentSuperior ); 197 198 // Recursively update the superior if not already done. We don't recurse 199 // if the superior's superior is not null, as it means it has already been 200 // handled. 201 if ( currentSuperior.getSuperior() == null ) 202 { 203 registries.buildReference( errors, currentSuperior ); 204 } 205 206 // Update the descendant MAP 207 try 208 { 209 attributeTypeRegistry.registerDescendants( attributeType, currentSuperior ); 210 } 211 catch ( LdapException ne ) 212 { 213 errors.add( ne ); 214 LOG.info( ne.getMessage() ); 215 216 return false; 217 } 218 219 // Check for cycles now 220 Set<String> superiors = new HashSet<>(); 221 superiors.add( attributeType.getOid() ); 222 AttributeType tmp = currentSuperior; 223 boolean isOk = true; 224 225 while ( tmp != null ) 226 { 227 if ( superiors.contains( tmp.getOid() ) ) 228 { 229 // There is a cycle : bad bad bad ! 230 // Not allowed. 231 String msg = I18n.err( I18n.ERR_04304, attributeType.getName() ); 232 233 LdapSchemaException ldapSchemaException = new LdapSchemaException( 234 LdapSchemaExceptionCodes.AT_CYCLE_TYPE_HIERARCHY, msg ); 235 ldapSchemaException.setSourceObject( attributeType ); 236 errors.add( ldapSchemaException ); 237 LOG.info( msg ); 238 isOk = false; 239 240 break; 241 } 242 else 243 { 244 superiors.add( tmp.getOid() ); 245 tmp = tmp.getSuperior(); 246 } 247 } 248 249 superiors.clear(); 250 251 return isOk; 252 } 253 else 254 { 255 // Not allowed. 256 String msg = I18n.err( I18n.ERR_04305, superiorOid, attributeType.getName() ); 257 258 LdapSchemaException ldapSchemaException = new LdapSchemaException( 259 LdapSchemaExceptionCodes.AT_NONEXISTENT_SUPERIOR, msg ); 260 ldapSchemaException.setSourceObject( attributeType ); 261 ldapSchemaException.setRelatedId( superiorOid ); 262 errors.add( ldapSchemaException ); 263 LOG.info( msg ); 264 265 // Get out now 266 return false; 267 } 268 } 269 else 270 { 271 // No superior, just return 272 return true; 273 } 274 } 275 276 277 /** 278 * Build the SYNTAX reference for an AttributeType 279 */ 280 private static void buildSyntax( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) 281 { 282 String syntaxOid = attributeType.getSyntaxOid(); 283 284 if ( syntaxOid != null ) 285 { 286 LdapSyntax currentSyntax = null; 287 288 try 289 { 290 currentSyntax = registries.getLdapSyntaxRegistry().lookup( syntaxOid ); 291 } 292 catch ( LdapException ne ) 293 { 294 // Not allowed. 295 String msg = I18n.err( I18n.ERR_04306, syntaxOid, attributeType.getName() ); 296 297 LdapSchemaException ldapSchemaException = new LdapSchemaException( 298 LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg, ne ); 299 ldapSchemaException.setSourceObject( attributeType ); 300 ldapSchemaException.setRelatedId( syntaxOid ); 301 errors.add( ldapSchemaException ); 302 LOG.info( msg ); 303 304 return; 305 } 306 307 if ( currentSyntax != null ) 308 { 309 // Update the Syntax reference 310 attributeType.setSyntax( currentSyntax ); 311 } 312 else 313 { 314 // Not allowed. 315 String msg = I18n.err( I18n.ERR_04306, syntaxOid, attributeType.getName() ); 316 317 LdapSchemaException ldapSchemaException = new LdapSchemaException( 318 LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg ); 319 ldapSchemaException.setSourceObject( attributeType ); 320 ldapSchemaException.setRelatedId( syntaxOid ); 321 errors.add( ldapSchemaException ); 322 LOG.info( msg ); 323 324 return; 325 } 326 } 327 else 328 { 329 // We inherit from the superior's syntax, if any 330 if ( attributeType.getSuperior() != null ) 331 { 332 if ( attributeType.getSuperior().getSyntax() != null ) 333 { 334 attributeType.setSyntax( attributeType.getSuperior().getSyntax() ); 335 } 336 else 337 { 338 String msg = I18n.err( I18n.ERR_04306, syntaxOid, attributeType.getName() ); 339 340 LdapSchemaException ldapSchemaException = new LdapSchemaException( 341 LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg ); 342 ldapSchemaException.setSourceObject( attributeType ); 343 ldapSchemaException.setRelatedId( syntaxOid ); 344 errors.add( ldapSchemaException ); 345 LOG.info( msg ); 346 347 return; 348 } 349 } 350 else 351 { 352 // Not allowed. 353 String msg = I18n.err( I18n.ERR_04307, attributeType.getName() ); 354 355 LdapSchemaException ldapSchemaException = new LdapSchemaException( 356 LdapSchemaExceptionCodes.AT_SYNTAX_OR_SUPERIOR_REQUIRED, msg ); 357 ldapSchemaException.setSourceObject( attributeType ); 358 errors.add( ldapSchemaException ); 359 LOG.info( msg ); 360 361 return; 362 } 363 } 364 } 365 366 367 /** 368 * Build the EQUALITY MR reference for an AttributeType 369 */ 370 private static void buildEquality( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) 371 { 372 String equalityOid = attributeType.getEqualityOid(); 373 374 // The equality MR. It can be null 375 if ( equalityOid != null ) 376 { 377 MatchingRule currentEquality = null; 378 379 try 380 { 381 currentEquality = registries.getMatchingRuleRegistry().lookup( equalityOid ); 382 } 383 catch ( LdapException ne ) 384 { 385 // Not allowed. 386 String msg = I18n.err( I18n.ERR_04308, equalityOid, attributeType.getName() ); 387 388 LdapSchemaException ldapSchemaException = new LdapSchemaException( 389 LdapSchemaExceptionCodes.AT_NONEXISTENT_EQUALITY_MATCHING_RULE, msg, ne ); 390 ldapSchemaException.setSourceObject( attributeType ); 391 ldapSchemaException.setRelatedId( equalityOid ); 392 errors.add( ldapSchemaException ); 393 LOG.info( msg ); 394 395 return; 396 } 397 398 if ( currentEquality != null ) 399 { 400 attributeType.setEquality( currentEquality ); 401 402 // Restore the old equality OID to preserve the user's provided value 403 attributeType.setEqualityOid( equalityOid ); 404 } 405 else 406 { 407 // Not allowed. 408 String msg = I18n.err( I18n.ERR_04309, equalityOid, attributeType.getName() ); 409 410 LdapSchemaException ldapSchemaException = new LdapSchemaException( 411 LdapSchemaExceptionCodes.AT_NONEXISTENT_EQUALITY_MATCHING_RULE, msg ); 412 ldapSchemaException.setSourceObject( attributeType ); 413 ldapSchemaException.setRelatedId( equalityOid ); 414 errors.add( ldapSchemaException ); 415 LOG.info( msg ); 416 } 417 } 418 else 419 { 420 AttributeType superior = attributeType.getSuperior(); 421 422 // If the AT has a superior, take its Equality MR if any 423 if ( ( superior != null ) && ( superior.getEquality() != null ) ) 424 { 425 attributeType.setEquality( superior.getEquality() ); 426 } 427 } 428 } 429 430 431 /** 432 * Build the SUBSTR MR reference for an AttributeType 433 */ 434 private static void buildSubstring( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) 435 { 436 String substringOid = attributeType.getSubstringOid(); 437 438 // The Substring MR. It can be null 439 if ( substringOid != null ) 440 { 441 MatchingRule currentSubstring = null; 442 443 try 444 { 445 currentSubstring = registries.getMatchingRuleRegistry().lookup( substringOid ); 446 } 447 catch ( LdapException ne ) 448 { 449 // Not allowed. 450 String msg = I18n.err( I18n.ERR_04312, substringOid, attributeType.getName() ); 451 452 LdapSchemaException ldapSchemaException = new LdapSchemaException( 453 LdapSchemaExceptionCodes.AT_NONEXISTENT_SUBSTRING_MATCHING_RULE, msg, ne ); 454 ldapSchemaException.setSourceObject( attributeType ); 455 ldapSchemaException.setRelatedId( substringOid ); 456 errors.add( ldapSchemaException ); 457 LOG.info( msg ); 458 459 return; 460 } 461 462 if ( currentSubstring != null ) 463 { 464 attributeType.setSubstring( currentSubstring ); 465 } 466 else 467 { 468 // Not allowed. 469 String msg = I18n.err( I18n.ERR_04313, substringOid, attributeType.getName() ); 470 471 LdapSchemaException ldapSchemaException = new LdapSchemaException( 472 LdapSchemaExceptionCodes.AT_NONEXISTENT_SUBSTRING_MATCHING_RULE, msg ); 473 ldapSchemaException.setSourceObject( attributeType ); 474 ldapSchemaException.setRelatedId( substringOid ); 475 errors.add( ldapSchemaException ); 476 LOG.info( msg ); 477 478 return; 479 } 480 } 481 else 482 { 483 AttributeType superior = attributeType.getSuperior(); 484 485 // If the AT has a superior, take its Substring MR if any 486 if ( ( superior != null ) && ( superior.getSubstring() != null ) ) 487 { 488 attributeType.setSubstring( superior.getSubstring() ); 489 } 490 } 491 } 492 493 494 495 496 497 498 /** 499 * Build the ORDERING MR reference for an AttributeType 500 */ 501 private static void buildOrdering( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) 502 { 503 String orderingOid = attributeType.getOrderingOid(); 504 505 if ( orderingOid != null ) 506 { 507 MatchingRule currentOrdering = null; 508 509 try 510 { 511 currentOrdering = registries.getMatchingRuleRegistry().lookup( orderingOid ); 512 } 513 catch ( LdapException ne ) 514 { 515 // Not allowed. 516 String msg = I18n.err( I18n.ERR_04310, orderingOid, attributeType.getName() ); 517 518 LdapSchemaException ldapSchemaException = new LdapSchemaException( 519 LdapSchemaExceptionCodes.AT_NONEXISTENT_ORDERING_MATCHING_RULE, msg, ne ); 520 ldapSchemaException.setSourceObject( attributeType ); 521 ldapSchemaException.setRelatedId( orderingOid ); 522 errors.add( ldapSchemaException ); 523 LOG.info( msg ); 524 525 return; 526 } 527 528 if ( currentOrdering != null ) 529 { 530 attributeType.setOrdering( currentOrdering ); 531 } 532 else 533 { 534 // Not allowed. 535 String msg = I18n.err( I18n.ERR_04311, orderingOid, attributeType.getName() ); 536 537 LdapSchemaException ldapSchemaException = new LdapSchemaException( 538 LdapSchemaExceptionCodes.AT_NONEXISTENT_ORDERING_MATCHING_RULE, msg ); 539 ldapSchemaException.setSourceObject( attributeType ); 540 ldapSchemaException.setRelatedId( orderingOid ); 541 errors.add( ldapSchemaException ); 542 LOG.info( msg ); 543 } 544 } 545 else 546 { 547 AttributeType superior = attributeType.getSuperior(); 548 549 // If the AT has a superior, take its Ordering MR if any 550 if ( ( superior != null ) && ( superior.getOrdering() != null ) ) 551 { 552 attributeType.setOrdering( superior.getOrdering() ); 553 } 554 } 555 } 556 557 558 /** 559 * Check the constraints for the Usage field. 560 */ 561 private static void checkUsage( AttributeType attributeType, List<Throwable> errors ) 562 { 563 AttributeType superior = attributeType.getSuperior(); 564 565 // Check that the AT usage is the same that its superior 566 if ( ( superior != null ) && ( attributeType.getUsage() != superior.getUsage() ) ) 567 { 568 // This is an error 569 String msg = I18n.err( I18n.ERR_04314, attributeType.getName() ); 570 571 LdapSchemaException ldapSchemaException = new LdapSchemaException( 572 LdapSchemaExceptionCodes.AT_MUST_HAVE_SAME_USAGE_THAN_SUPERIOR, msg ); 573 ldapSchemaException.setSourceObject( attributeType ); 574 errors.add( ldapSchemaException ); 575 LOG.info( msg ); 576 577 return; 578 } 579 580 // Now, check that the AttributeType's USAGE does not conflict 581 if ( !attributeType.isUserModifiable() && ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS ) ) 582 { 583 // Cannot have a not user modifiable AT which is not an operational AT 584 String msg = I18n.err( I18n.ERR_04315, attributeType.getName() ); 585 586 LdapSchemaException ldapSchemaException = new LdapSchemaException( 587 LdapSchemaExceptionCodes.AT_USER_APPLICATIONS_USAGE_MUST_BE_USER_MODIFIABLE, msg ); 588 ldapSchemaException.setSourceObject( attributeType ); 589 errors.add( ldapSchemaException ); 590 LOG.info( msg ); 591 } 592 } 593 594 595 /** 596 * Check the constraints for the Collective field. 597 */ 598 private static void checkCollective( MutableAttributeType attributeType, List<Throwable> errors ) 599 { 600 AttributeType superior = attributeType.getSuperior(); 601 602 if ( ( superior != null ) && superior.isCollective() ) 603 { 604 // An AttributeType will be collective if its superior is collective 605 attributeType.setCollective( true ); 606 } 607 608 if ( attributeType.isCollective() && ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS ) ) 609 { 610 // An AttributeType which is collective must be a USER attributeType 611 String msg = I18n.err( I18n.ERR_04316, attributeType.getName() ); 612 613 LdapSchemaException ldapSchemaException = new LdapSchemaException( 614 LdapSchemaExceptionCodes.AT_COLLECTIVE_MUST_HAVE_USER_APPLICATIONS_USAGE, msg ); 615 ldapSchemaException.setSourceObject( attributeType ); 616 errors.add( ldapSchemaException ); 617 LOG.info( msg ); 618 } 619 620 if ( attributeType.isCollective() && attributeType.isSingleValued() ) 621 { 622 // A collective attribute must be multi-valued 623 String msg = I18n.err( I18n.ERR_04483_COLLECTIVE_NOT_MULTI_VALUED, attributeType.getName() ); 624 625 LdapSchemaException ldapSchemaException = new LdapSchemaException( 626 LdapSchemaExceptionCodes.AT_COLLECTIVE_CANNOT_BE_SINGLE_VALUED, msg ); 627 ldapSchemaException.setSourceObject( attributeType ); 628 errors.add( ldapSchemaException ); 629 LOG.info( msg ); 630 } 631 } 632 633 634 /** 635 * Remove the AttributeType from the registries, updating the references to 636 * other SchemaObject. 637 * 638 * If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX), 639 * an exception is thrown. 640 * 641 * @param attributeType The AttributeType to remove from the Registries 642 * @param errors The errors we got while removing the AttributeType from the Registries 643 * @param registries The Registries 644 * @throws LdapException If the AttributeType is not valid 645 */ 646 public static void removeFromRegistries( AttributeType attributeType, List<Throwable> errors, Registries registries ) throws LdapException 647 { 648 if ( registries != null ) 649 { 650 AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry(); 651 652 // Remove the attributeType from the oid/normalizer map 653 attributeTypeRegistry.removeMappingFor( attributeType ); 654 655 // Unregister this AttributeType into the Descendant map 656 attributeTypeRegistry.unregisterDescendants( attributeType, attributeType.getSuperior() ); 657 658 /** 659 * Remove the AT references (using and usedBy) : 660 * AT -> MR (for EQUALITY, ORDERING and SUBSTR) 661 * AT -> S 662 * AT -> AT 663 */ 664 if ( attributeType.getEquality() != null ) 665 { 666 registries.delReference( attributeType, attributeType.getEquality() ); 667 } 668 669 if ( attributeType.getOrdering() != null ) 670 { 671 registries.delReference( attributeType, attributeType.getOrdering() ); 672 } 673 674 if ( attributeType.getSubstring() != null ) 675 { 676 registries.delReference( attributeType, attributeType.getSubstring() ); 677 } 678 679 if ( attributeType.getSyntax() != null ) 680 { 681 registries.delReference( attributeType, attributeType.getSyntax() ); 682 } 683 684 if ( attributeType.getSuperior() != null ) 685 { 686 registries.delReference( attributeType, attributeType.getSuperior() ); 687 } 688 } 689 } 690}