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 package org.apache.directory.server.core.schema; 020 021 022 import java.util.HashSet; 023 import java.util.Set; 024 025 import org.apache.directory.server.constants.ApacheSchemaConstants; 026 import org.apache.directory.server.constants.ServerDNConstants; 027 import org.apache.directory.server.core.interceptor.context.LookupOperationContext; 028 import org.apache.directory.shared.ldap.constants.SchemaConstants; 029 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute; 030 import org.apache.directory.shared.ldap.entry.DefaultServerEntry; 031 import org.apache.directory.shared.ldap.entry.EntryAttribute; 032 import org.apache.directory.shared.ldap.entry.ServerEntry; 033 import org.apache.directory.shared.ldap.exception.LdapException; 034 import org.apache.directory.shared.ldap.name.DN; 035 import org.apache.directory.shared.ldap.schema.AttributeType; 036 import org.apache.directory.shared.ldap.schema.DITContentRule; 037 import org.apache.directory.shared.ldap.schema.DITStructureRule; 038 import org.apache.directory.shared.ldap.schema.LdapComparator; 039 import org.apache.directory.shared.ldap.schema.LdapSyntax; 040 import org.apache.directory.shared.ldap.schema.MatchingRule; 041 import org.apache.directory.shared.ldap.schema.MatchingRuleUse; 042 import org.apache.directory.shared.ldap.schema.NameForm; 043 import org.apache.directory.shared.ldap.schema.Normalizer; 044 import org.apache.directory.shared.ldap.schema.ObjectClass; 045 import org.apache.directory.shared.ldap.schema.SchemaManager; 046 import org.apache.directory.shared.ldap.schema.SchemaUtils; 047 import org.apache.directory.shared.ldap.schema.SyntaxChecker; 048 import org.apache.directory.shared.ldap.schema.registries.NormalizerRegistry; 049 050 051 /** 052 * This class manage the Schema's operations. 053 * 054 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 055 * @version $Rev$, $Date$ 056 */ 057 public class DefaultSchemaService implements SchemaService 058 { 059 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 060 061 /** cached version of the schema subentry with all attributes in it */ 062 private ServerEntry schemaSubentry; 063 private final Object lock = new Object(); 064 065 /** a handle on the schema partition */ 066 private SchemaPartition schemaPartition; 067 068 /** the normalized name for the schema modification attributes */ 069 private DN schemaModificationAttributesDN; 070 071 /** A lock to avid concurrent generation of the SubschemaSubentry */ 072 private Object schemaSubentrLock = new Object(); 073 074 075 public DefaultSchemaService() throws Exception 076 { 077 schemaPartition = new SchemaPartition(); 078 } 079 080 081 082 /* (non-Javadoc) 083 * @see org.apache.directory.server.core.schema.SchemaService#isSchemaSubentry(java.lang.String) 084 */ 085 public boolean isSchemaSubentry( String dnString ) throws LdapException 086 { 087 if ( ServerDNConstants.CN_SCHEMA_DN.equalsIgnoreCase( dnString ) || 088 ServerDNConstants.CN_SCHEMA_DN_NORMALIZED.equalsIgnoreCase( dnString ) ) 089 { 090 return true; 091 } 092 093 DN dn = new DN( dnString ).normalize( schemaPartition.getSchemaManager().getNormalizerMapping() ); 094 return dn.getNormName().equals( ServerDNConstants.CN_SCHEMA_DN_NORMALIZED ); 095 } 096 097 098 public final SchemaManager getSchemaManager() 099 { 100 return schemaPartition.getSchemaManager(); 101 } 102 103 104 /* (non-Javadoc) 105 * @see org.apache.directory.server.core.schema.SchemaService#getSchemaPartition() 106 */ 107 public SchemaPartition getSchemaPartition() 108 { 109 return schemaPartition; 110 } 111 112 113 /* (non-Javadoc) 114 * @see org.apache.directory.server.core.schema.SchemaService#setSchemaPartition(org.apache.directory.server.core.schema.SchemaPartition) 115 */ 116 public void setSchemaPartition( SchemaPartition schemaPartition ) 117 { 118 this.schemaPartition = schemaPartition; 119 } 120 121 122 /** 123 * Generate the comparators attribute from the registry 124 */ 125 private EntryAttribute generateComparators() throws LdapException 126 { 127 EntryAttribute attr = new DefaultServerAttribute( 128 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.COMPARATORS_AT ) ); 129 130 for ( LdapComparator<?> comparator : getSchemaManager().getComparatorRegistry() ) 131 { 132 attr.add( SchemaUtils.render( comparator ) ); 133 } 134 135 return attr; 136 } 137 138 139 private EntryAttribute generateNormalizers() throws LdapException 140 { 141 EntryAttribute attr = new DefaultServerAttribute( 142 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.NORMALIZERS_AT ) ); 143 144 NormalizerRegistry nr = getSchemaManager().getNormalizerRegistry(); 145 146 for ( Normalizer normalizer : nr ) 147 { 148 attr.add( SchemaUtils.render( normalizer ) ); 149 } 150 151 return attr; 152 } 153 154 155 private EntryAttribute generateSyntaxCheckers() throws LdapException 156 { 157 EntryAttribute attr = new DefaultServerAttribute( 158 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.SYNTAX_CHECKERS_AT ) ); 159 160 for ( SyntaxChecker syntaxChecker : getSchemaManager().getSyntaxCheckerRegistry() ) 161 { 162 attr.add( SchemaUtils.render( syntaxChecker ) ); 163 } 164 165 return attr; 166 } 167 168 169 private EntryAttribute generateObjectClasses() throws LdapException 170 { 171 EntryAttribute attr = new DefaultServerAttribute( 172 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.OBJECT_CLASSES_AT ) ); 173 174 for ( ObjectClass objectClass : getSchemaManager().getObjectClassRegistry() ) 175 { 176 attr.add( SchemaUtils.render( objectClass ).toString() ); 177 } 178 179 return attr; 180 } 181 182 183 private EntryAttribute generateAttributeTypes() throws LdapException 184 { 185 EntryAttribute attr = new DefaultServerAttribute( 186 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.ATTRIBUTE_TYPES_AT ) ); 187 188 for ( AttributeType attributeType : getSchemaManager().getAttributeTypeRegistry() ) 189 { 190 attr.add( SchemaUtils.render( attributeType ).toString() ); 191 } 192 193 return attr; 194 } 195 196 197 private EntryAttribute generateMatchingRules() throws LdapException 198 { 199 EntryAttribute attr = new DefaultServerAttribute( 200 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.MATCHING_RULES_AT ) ); 201 202 for ( MatchingRule matchingRule : getSchemaManager().getMatchingRuleRegistry() ) 203 { 204 attr.add( SchemaUtils.render( matchingRule ).toString() ); 205 } 206 207 return attr; 208 } 209 210 211 private EntryAttribute generateMatchingRuleUses() throws LdapException 212 { 213 EntryAttribute attr = new DefaultServerAttribute( 214 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.MATCHING_RULE_USE_AT ) ); 215 216 for ( MatchingRuleUse matchingRuleUse : getSchemaManager().getMatchingRuleUseRegistry() ) 217 { 218 attr.add( SchemaUtils.render( matchingRuleUse ).toString() ); 219 } 220 221 return attr; 222 } 223 224 225 private EntryAttribute generateSyntaxes() throws LdapException 226 { 227 EntryAttribute attr = new DefaultServerAttribute( 228 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.LDAP_SYNTAXES_AT ) ); 229 230 for ( LdapSyntax syntax : getSchemaManager().getLdapSyntaxRegistry() ) 231 { 232 attr.add( SchemaUtils.render( syntax ).toString() ); 233 } 234 235 return attr; 236 } 237 238 239 private EntryAttribute generateDitContextRules() throws LdapException 240 { 241 EntryAttribute attr = new DefaultServerAttribute( 242 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.DIT_CONTENT_RULES_AT ) ); 243 244 for ( DITContentRule ditContentRule : getSchemaManager().getDITContentRuleRegistry() ) 245 { 246 attr.add( SchemaUtils.render( ditContentRule ).toString() ); 247 } 248 249 return attr; 250 } 251 252 253 private EntryAttribute generateDitStructureRules() throws LdapException 254 { 255 EntryAttribute attr = new DefaultServerAttribute( 256 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.DIT_STRUCTURE_RULES_AT ) ); 257 258 for ( DITStructureRule ditStructureRule : getSchemaManager().getDITStructureRuleRegistry() ) 259 { 260 attr.add( SchemaUtils.render( ditStructureRule ).toString() ); 261 } 262 263 return attr; 264 } 265 266 267 private EntryAttribute generateNameForms() throws LdapException 268 { 269 EntryAttribute attr = new DefaultServerAttribute( 270 getSchemaManager().lookupAttributeTypeRegistry( SchemaConstants.NAME_FORMS_AT ) ); 271 272 for ( NameForm nameForm : getSchemaManager().getNameFormRegistry() ) 273 { 274 attr.add( SchemaUtils.render( nameForm ).toString() ); 275 } 276 277 return attr; 278 } 279 280 281 /** 282 * Creates the SSSE by extracting all the SchemaObjects from the registries. 283 */ 284 private void generateSchemaSubentry( ServerEntry mods ) throws LdapException 285 { 286 ServerEntry attrs = new DefaultServerEntry( getSchemaManager(), mods.getDn() ); 287 288 // add the objectClass attribute : 'top', 'subschema', 'subentry' and 'apacheSubschema' 289 attrs.put( SchemaConstants.OBJECT_CLASS_AT, 290 SchemaConstants.TOP_OC, 291 SchemaConstants.SUBSCHEMA_OC, 292 SchemaConstants.SUBENTRY_OC, 293 ApacheSchemaConstants.APACHE_SUBSCHEMA_OC 294 ); 295 296 // add the cn attribute as required for the RDN 297 attrs.put( SchemaConstants.CN_AT, "schema" ); 298 299 // generate all the other operational attributes 300 attrs.put( generateComparators() ); 301 attrs.put( generateNormalizers() ); 302 attrs.put( generateSyntaxCheckers() ); 303 attrs.put( generateObjectClasses() ); 304 attrs.put( generateAttributeTypes() ); 305 attrs.put( generateMatchingRules() ); 306 attrs.put( generateMatchingRuleUses() ); 307 attrs.put( generateSyntaxes() ); 308 attrs.put( generateDitContextRules() ); 309 attrs.put( generateDitStructureRules() ); 310 attrs.put( generateNameForms() ); 311 attrs.put( SchemaConstants.SUBTREE_SPECIFICATION_AT, "{}" ); 312 313 // ------------------------------------------------------------------- 314 // set standard operational attributes for the subentry 315 // ------------------------------------------------------------------- 316 317 // Add the createTimestamp 318 EntryAttribute createTimestamp = mods.get( SchemaConstants.CREATE_TIMESTAMP_AT ); 319 attrs.put( SchemaConstants.CREATE_TIMESTAMP_AT, createTimestamp.get() ); 320 321 // Add the creatorsName 322 attrs.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN ); 323 324 // Add the modifyTimestamp 325 EntryAttribute schemaModifyTimestamp = mods.get( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT ); 326 attrs.put( SchemaConstants.MODIFY_TIMESTAMP_AT, schemaModifyTimestamp.get() ); 327 328 // Add the modifiersName 329 EntryAttribute schemaModifiersName = mods.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT ); 330 attrs.put( SchemaConstants.MODIFIERS_NAME_AT, schemaModifiersName.get() ); 331 332 // don't swap out if a request for the subentry is in progress or we 333 // can give back an inconsistent schema back to the client so we block 334 synchronized ( lock ) 335 { 336 schemaSubentry = attrs; 337 } 338 } 339 340 341 private void addAttribute( ServerEntry attrs, String id ) throws LdapException 342 { 343 EntryAttribute attr = schemaSubentry.get( id ); 344 345 if ( attr != null ) 346 { 347 attrs.put( attr ); 348 } 349 } 350 351 352 /** 353 * {@inheritDoc} 354 */ 355 public ServerEntry getSubschemaEntryImmutable() throws Exception 356 { 357 synchronized ( schemaSubentrLock ) 358 { 359 if ( schemaSubentry == null ) 360 { 361 generateSchemaSubentry( schemaPartition.lookup( 362 new LookupOperationContext( null, schemaModificationAttributesDN ) ) ); 363 } 364 365 return ( ServerEntry ) schemaSubentry.clone(); 366 } 367 } 368 369 370 /** 371 * Initializes the SchemaService 372 * 373 * @throws Exception If the initializaion fails 374 */ 375 public void initialize() throws Exception 376 { 377 try 378 { 379 schemaModificationAttributesDN = new DN( ServerDNConstants.SCHEMA_MODIFICATIONS_DN ); 380 schemaModificationAttributesDN.normalize( 381 getSchemaManager().getNormalizerMapping() ); 382 } 383 catch ( LdapException e ) 384 { 385 throw new RuntimeException( e ); 386 } 387 } 388 389 390 /* (non-Javadoc) 391 * @see org.apache.directory.server.core.schema.SchemaService#getSubschemaEntryCloned() 392 */ 393 public ServerEntry getSubschemaEntryCloned() throws Exception 394 { 395 if ( schemaSubentry == null ) 396 { 397 generateSchemaSubentry( schemaPartition.lookup( 398 new LookupOperationContext( null, schemaModificationAttributesDN ) ) ); 399 } 400 401 return ( ServerEntry ) schemaSubentry.clone(); 402 } 403 404 405 /** 406 * {@inheritDoc} 407 */ 408 public ServerEntry getSubschemaEntry( String[] ids ) throws Exception 409 { 410 if ( ids == null ) 411 { 412 ids = EMPTY_STRING_ARRAY; 413 } 414 415 Set<String> setOids = new HashSet<String>(); 416 ServerEntry attrs = new DefaultServerEntry( getSchemaManager(), DN.EMPTY_DN ); 417 boolean returnAllOperationalAttributes = false; 418 419 synchronized( lock ) 420 { 421 // --------------------------------------------------------------- 422 // Check if we need an update by looking at timestamps on disk 423 // --------------------------------------------------------------- 424 425 ServerEntry mods = 426 schemaPartition.lookup( 427 new LookupOperationContext( null, schemaModificationAttributesDN ) ); 428 429 // @todo enable this optimization at some point but for now it 430 // is causing some problems so I will just turn it off 431 // Attribute modifyTimeDisk = mods.get( SchemaConstants.MODIFY_TIMESTAMP_AT ); 432 // 433 // Attribute modifyTimeMemory = null; 434 // 435 // if ( schemaSubentry != null ) 436 // { 437 // modifyTimeMemory = schemaSubentry.get( SchemaConstants.MODIFY_TIMESTAMP_AT ); 438 // if ( modifyTimeDisk == null && modifyTimeMemory == null ) 439 // { 440 // // do nothing! 441 // } 442 // else if ( modifyTimeDisk != null && modifyTimeMemory != null ) 443 // { 444 // Date disk = DateUtils.getDate( ( String ) modifyTimeDisk.get() ); 445 // Date mem = DateUtils.getDate( ( String ) modifyTimeMemory.get() ); 446 // if ( disk.after( mem ) ) 447 // { 448 // generateSchemaSubentry( mods ); 449 // } 450 // } 451 // else 452 // { 453 // generateSchemaSubentry( mods ); 454 // } 455 // } 456 // else 457 // { 458 generateSchemaSubentry( mods ); 459 // } 460 461 // --------------------------------------------------------------- 462 // Prep Work: Transform the attributes to their OID counterpart 463 // --------------------------------------------------------------- 464 465 for ( String id:ids ) 466 { 467 // Check whether the set contains a plus, and use it below to include all 468 // operational attributes. Due to RFC 3673, and issue DIREVE-228 in JIRA 469 if ( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES.equals( id ) ) 470 { 471 returnAllOperationalAttributes = true; 472 } 473 else if ( SchemaConstants.ALL_USER_ATTRIBUTES.equals( id ) ) 474 { 475 setOids.add( id ); 476 } 477 else 478 { 479 setOids.add( getSchemaManager().getAttributeTypeRegistry().getOidByName( id ) ); 480 } 481 } 482 483 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.COMPARATORS_AT_OID ) ) 484 { 485 addAttribute( attrs, SchemaConstants.COMPARATORS_AT ); 486 } 487 488 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NORMALIZERS_AT_OID ) ) 489 { 490 addAttribute( attrs, SchemaConstants.NORMALIZERS_AT ); 491 } 492 493 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SYNTAX_CHECKERS_AT_OID ) ) 494 { 495 addAttribute( attrs, SchemaConstants.SYNTAX_CHECKERS_AT ); 496 } 497 498 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.OBJECT_CLASSES_AT_OID ) ) 499 { 500 addAttribute( attrs, SchemaConstants.OBJECT_CLASSES_AT ); 501 } 502 503 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.ATTRIBUTE_TYPES_AT_OID ) ) 504 { 505 addAttribute( attrs, SchemaConstants.ATTRIBUTE_TYPES_AT ); 506 } 507 508 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULES_AT_OID ) ) 509 { 510 addAttribute( attrs, SchemaConstants.MATCHING_RULES_AT ); 511 } 512 513 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MATCHING_RULE_USE_AT_OID ) ) 514 { 515 addAttribute( attrs, SchemaConstants.MATCHING_RULE_USE_AT ); 516 } 517 518 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.LDAP_SYNTAXES_AT_OID ) ) 519 { 520 addAttribute( attrs, SchemaConstants.LDAP_SYNTAXES_AT ); 521 } 522 523 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_CONTENT_RULES_AT_OID ) ) 524 { 525 addAttribute( attrs, SchemaConstants.DIT_CONTENT_RULES_AT ); 526 } 527 528 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.DIT_STRUCTURE_RULES_AT_OID ) ) 529 { 530 addAttribute( attrs, SchemaConstants.DIT_STRUCTURE_RULES_AT ); 531 } 532 533 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.NAME_FORMS_AT_OID ) ) 534 { 535 addAttribute( attrs, SchemaConstants.NAME_FORMS_AT ); 536 } 537 538 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.SUBTREE_SPECIFICATION_AT_OID ) ) 539 { 540 addAttribute( attrs, SchemaConstants.SUBTREE_SPECIFICATION_AT ); 541 } 542 543 int minSetSize = 0; 544 545 if ( setOids.contains( SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES ) ) 546 { 547 minSetSize++; 548 } 549 550 if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) ) 551 { 552 minSetSize++; 553 } 554 555 if ( setOids.contains( SchemaConstants.REF_AT_OID ) ) 556 { 557 minSetSize++; 558 } 559 560 // add the objectClass attribute 561 if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) || 562 setOids.contains( SchemaConstants.OBJECT_CLASS_AT_OID ) || 563 setOids.size() == minSetSize ) 564 { 565 addAttribute( attrs, SchemaConstants.OBJECT_CLASS_AT ); 566 } 567 568 // add the cn attribute as required for the RDN 569 if ( setOids.contains( SchemaConstants.ALL_USER_ATTRIBUTES ) || 570 setOids.contains( SchemaConstants.CN_AT_OID ) || 571 setOids.size() == minSetSize ) 572 { 573 addAttribute( attrs, SchemaConstants.CN_AT ); 574 } 575 576 // ------------------------------------------------------------------- 577 // set standard operational attributes for the subentry 578 // ------------------------------------------------------------------- 579 580 581 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATE_TIMESTAMP_AT_OID ) ) 582 { 583 addAttribute( attrs, SchemaConstants.CREATE_TIMESTAMP_AT ); 584 } 585 586 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.CREATORS_NAME_AT_OID ) ) 587 { 588 addAttribute( attrs, SchemaConstants.CREATORS_NAME_AT ); 589 } 590 591 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFY_TIMESTAMP_AT_OID ) ) 592 { 593 addAttribute( attrs, SchemaConstants.MODIFY_TIMESTAMP_AT ); 594 } 595 596 if ( returnAllOperationalAttributes || setOids.contains( SchemaConstants.MODIFIERS_NAME_AT_OID ) ) 597 { 598 addAttribute( attrs, SchemaConstants.MODIFIERS_NAME_AT ); 599 } 600 } 601 602 return attrs; 603 } 604 }