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 */ 019package org.apache.directory.server.core.shared; 020 021 022import java.io.IOException; 023 024import org.apache.directory.api.ldap.model.constants.SchemaConstants; 025import org.apache.directory.api.ldap.model.entry.Attribute; 026import org.apache.directory.api.ldap.model.entry.DefaultAttribute; 027import org.apache.directory.api.ldap.model.entry.DefaultEntry; 028import org.apache.directory.api.ldap.model.entry.Entry; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.exception.LdapOtherException; 031import org.apache.directory.api.ldap.model.name.Dn; 032import org.apache.directory.api.ldap.model.schema.AttributeType; 033import org.apache.directory.api.ldap.model.schema.DitContentRule; 034import org.apache.directory.api.ldap.model.schema.DitStructureRule; 035import org.apache.directory.api.ldap.model.schema.LdapComparator; 036import org.apache.directory.api.ldap.model.schema.LdapSyntax; 037import org.apache.directory.api.ldap.model.schema.MatchingRule; 038import org.apache.directory.api.ldap.model.schema.MatchingRuleUse; 039import org.apache.directory.api.ldap.model.schema.NameForm; 040import org.apache.directory.api.ldap.model.schema.Normalizer; 041import org.apache.directory.api.ldap.model.schema.ObjectClass; 042import org.apache.directory.api.ldap.model.schema.SchemaManager; 043import org.apache.directory.api.ldap.model.schema.SchemaObjectRenderer; 044import org.apache.directory.api.ldap.model.schema.SchemaUtils; 045import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 046import org.apache.directory.api.ldap.model.schema.registries.NormalizerRegistry; 047import org.apache.directory.server.constants.ApacheSchemaConstants; 048import org.apache.directory.server.constants.ServerDNConstants; 049import org.apache.directory.server.core.api.DirectoryService; 050import org.apache.directory.server.core.api.entry.ClonedServerEntry; 051import org.apache.directory.server.core.api.interceptor.context.FilteringOperationContext; 052import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext; 053import org.apache.directory.server.core.api.partition.Partition; 054import org.apache.directory.server.core.api.partition.PartitionTxn; 055 056 057/** 058 * This class manage the Schema's operations. 059 * 060 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 061 */ 062public final class SchemaService 063{ 064 /** cached version of the schema subentry with all attributes in it */ 065 private static Entry schemaSubentry; 066 067 /** A lock to avid concurrent generation of the SubschemaSubentry */ 068 private static Object schemaSubentrLock = new Object(); 069 070 071 private SchemaService() 072 { 073 } 074 075 076 /** 077 * Generate the comparators attribute from the registry 078 */ 079 private static Attribute generateComparators( SchemaManager schemaManager ) throws LdapException 080 { 081 Attribute attr = new DefaultAttribute( 082 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.COMPARATORS_AT ) ); 083 084 for ( LdapComparator<?> comparator : schemaManager.getComparatorRegistry() ) 085 { 086 attr.add( SchemaUtils.render( comparator ) ); 087 } 088 089 return attr; 090 } 091 092 093 private static Attribute generateNormalizers( SchemaManager schemaManager ) throws LdapException 094 { 095 Attribute attr = new DefaultAttribute( 096 schemaManager.getAttributeType( SchemaConstants.NORMALIZERS_AT ) ); 097 098 NormalizerRegistry nr = schemaManager.getNormalizerRegistry(); 099 100 for ( Normalizer normalizer : nr ) 101 { 102 attr.add( SchemaUtils.render( normalizer ) ); 103 } 104 105 return attr; 106 } 107 108 109 private static Attribute generateSyntaxCheckers( SchemaManager schemaManager ) throws LdapException 110 { 111 Attribute attr = new DefaultAttribute( 112 schemaManager.getAttributeType( SchemaConstants.SYNTAX_CHECKERS_AT ) ); 113 114 for ( SyntaxChecker syntaxChecker : schemaManager.getSyntaxCheckerRegistry() ) 115 { 116 attr.add( SchemaUtils.render( syntaxChecker ) ); 117 } 118 119 return attr; 120 } 121 122 123 private static Attribute generateObjectClasses( SchemaManager schemaManager ) throws LdapException 124 { 125 Attribute attr = new DefaultAttribute( 126 schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASSES_AT ) ); 127 128 for ( ObjectClass objectClass : schemaManager.getObjectClassRegistry() ) 129 { 130 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( objectClass ) ); 131 } 132 133 return attr; 134 } 135 136 137 private static Attribute generateAttributeTypes( SchemaManager schemaManager ) throws LdapException 138 { 139 Attribute attr = new DefaultAttribute( 140 schemaManager.getAttributeType( SchemaConstants.ATTRIBUTE_TYPES_AT ) ); 141 142 for ( AttributeType attributeType : schemaManager.getAttributeTypeRegistry() ) 143 { 144 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( attributeType ) ); 145 } 146 147 return attr; 148 } 149 150 151 private static Attribute generateMatchingRules( SchemaManager schemaManager ) throws LdapException 152 { 153 Attribute attr = new DefaultAttribute( 154 schemaManager.getAttributeType( SchemaConstants.MATCHING_RULES_AT ) ); 155 156 for ( MatchingRule matchingRule : schemaManager.getMatchingRuleRegistry() ) 157 { 158 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( matchingRule ) ); 159 } 160 161 return attr; 162 } 163 164 165 private static Attribute generateMatchingRuleUses( SchemaManager schemaManager ) throws LdapException 166 { 167 Attribute attr = new DefaultAttribute( 168 schemaManager.getAttributeType( SchemaConstants.MATCHING_RULE_USE_AT ) ); 169 170 for ( MatchingRuleUse matchingRuleUse : schemaManager.getMatchingRuleUseRegistry() ) 171 { 172 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( matchingRuleUse ) ); 173 } 174 175 return attr; 176 } 177 178 179 private static Attribute generateSyntaxes( SchemaManager schemaManager ) throws LdapException 180 { 181 Attribute attr = new DefaultAttribute( 182 schemaManager.getAttributeType( SchemaConstants.LDAP_SYNTAXES_AT ) ); 183 184 for ( LdapSyntax syntax : schemaManager.getLdapSyntaxRegistry() ) 185 { 186 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( syntax ) ); 187 } 188 189 return attr; 190 } 191 192 193 private static Attribute generateDitContextRules( SchemaManager schemaManager ) throws LdapException 194 { 195 Attribute attr = new DefaultAttribute( 196 schemaManager.getAttributeType( SchemaConstants.DIT_CONTENT_RULES_AT ) ); 197 198 for ( DitContentRule ditContentRule : schemaManager.getDITContentRuleRegistry() ) 199 { 200 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( ditContentRule ) ); 201 } 202 203 return attr; 204 } 205 206 207 private static Attribute generateDitStructureRules( SchemaManager schemaManager ) throws LdapException 208 { 209 Attribute attr = new DefaultAttribute( 210 schemaManager.getAttributeType( SchemaConstants.DIT_STRUCTURE_RULES_AT ) ); 211 212 for ( DitStructureRule ditStructureRule : schemaManager.getDITStructureRuleRegistry() ) 213 { 214 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( ditStructureRule ) ); 215 } 216 217 return attr; 218 } 219 220 221 private static Attribute generateNameForms( SchemaManager schemaManager ) throws LdapException 222 { 223 Attribute attr = new DefaultAttribute( 224 schemaManager.getAttributeType( SchemaConstants.NAME_FORMS_AT ) ); 225 226 for ( NameForm nameForm : schemaManager.getNameFormRegistry() ) 227 { 228 attr.add( SchemaObjectRenderer.SUBSCHEMA_SUBENTRY_RENDERER.render( nameForm ) ); 229 } 230 231 return attr; 232 } 233 234 235 /** 236 * Creates the SSSE by extracting all the SchemaObjects from the registries. 237 */ 238 private static void generateSchemaSubentry( SchemaManager schemaManager, Entry mods ) throws LdapException 239 { 240 Entry attrs = new DefaultEntry( schemaManager, mods.getDn() ); 241 242 // add the objectClass attribute : 'top', 'subschema', 'subentry' and 'apacheSubschema' 243 attrs.put( SchemaConstants.OBJECT_CLASS_AT, 244 SchemaConstants.TOP_OC, 245 SchemaConstants.SUBSCHEMA_OC, 246 SchemaConstants.SUBENTRY_OC, 247 ApacheSchemaConstants.APACHE_SUBSCHEMA_OC 248 ); 249 250 // add the cn attribute as required for the Rdn 251 attrs.put( SchemaConstants.CN_AT, "schema" ); 252 253 // generate all the other operational attributes 254 attrs.put( generateComparators( schemaManager ) ); 255 attrs.put( generateNormalizers( schemaManager ) ); 256 attrs.put( generateSyntaxCheckers( schemaManager ) ); 257 attrs.put( generateObjectClasses( schemaManager ) ); 258 attrs.put( generateAttributeTypes( schemaManager ) ); 259 attrs.put( generateMatchingRules( schemaManager ) ); 260 attrs.put( generateMatchingRuleUses( schemaManager ) ); 261 attrs.put( generateSyntaxes( schemaManager ) ); 262 attrs.put( generateDitContextRules( schemaManager ) ); 263 attrs.put( generateDitStructureRules( schemaManager ) ); 264 attrs.put( generateNameForms( schemaManager ) ); 265 attrs.put( SchemaConstants.SUBTREE_SPECIFICATION_AT, "{}" ); 266 267 // ------------------------------------------------------------------- 268 // set standard operational attributes for the subentry 269 // ------------------------------------------------------------------- 270 271 // Add the createTimestamp 272 Attribute createTimestamp = mods.get( SchemaConstants.CREATE_TIMESTAMP_AT ); 273 attrs.put( SchemaConstants.CREATE_TIMESTAMP_AT, createTimestamp.get() ); 274 275 // Add the creatorsName 276 attrs.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN ); 277 278 // Add the modifyTimestamp 279 Attribute schemaModifyTimestamp = mods.get( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT ); 280 attrs.put( SchemaConstants.MODIFY_TIMESTAMP_AT, schemaModifyTimestamp.get() ); 281 282 // Add the modifiersName 283 Attribute schemaModifiersName = mods.get( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT ); 284 attrs.put( SchemaConstants.MODIFIERS_NAME_AT, schemaModifiersName.get() ); 285 286 // don't swap out if a request for the subentry is in progress or we 287 // can give back an inconsistent schema back to the client so we block 288 synchronized ( schemaSubentrLock ) 289 { 290 schemaSubentry = attrs; 291 } 292 } 293 294 295 private static void addAttribute( Entry attrs, String id ) throws LdapException 296 { 297 Attribute attr = schemaSubentry.get( id ); 298 299 if ( ( attr != null ) && ( attr.size() > 0 ) ) 300 { 301 attrs.put( attr ); 302 } 303 } 304 305 306 /** 307 * {@inheritDoc} 308 */ 309 public static Entry getSubschemaEntryImmutable( DirectoryService directoryService ) throws LdapException 310 { 311 synchronized ( schemaSubentrLock ) 312 { 313 if ( schemaSubentry == null ) 314 { 315 Dn schemaModificationAttributesDn = new Dn( directoryService.getSchemaManager(), 316 SchemaConstants.SCHEMA_MODIFICATIONS_DN ); 317 318 Partition partition = directoryService.getSchemaPartition(); 319 320 LookupOperationContext lookupContext = new LookupOperationContext( null, schemaModificationAttributesDn ); 321 lookupContext.setPartition( partition ); 322 323 try ( PartitionTxn partitionTxn = partition.beginReadTransaction() ) 324 { 325 lookupContext.setTransaction( partitionTxn ); 326 327 generateSchemaSubentry( directoryService.getSchemaManager(), 328 directoryService.getSchemaPartition().lookup( lookupContext ) ); 329 } 330 catch ( IOException ioe ) 331 { 332 throw new LdapOtherException( ioe.getMessage(), ioe ); 333 } 334 } 335 336 return schemaSubentry.clone(); 337 } 338 } 339 340 341 /* (non-Javadoc) 342 * @see org.apache.directory.server.core.schema.SchemaService#getSubschemaEntryCloned() 343 */ 344 public static Entry getSubschemaEntryCloned( DirectoryService directoryService ) throws LdapException 345 { 346 if ( schemaSubentry == null ) 347 { 348 Dn schemaModificationAttributesDn = new Dn( directoryService.getSchemaManager(), 349 SchemaConstants.SCHEMA_MODIFICATIONS_DN ); 350 351 Partition partition = directoryService.getSchemaPartition(); 352 353 LookupOperationContext lookupContext = new LookupOperationContext( null, schemaModificationAttributesDn ); 354 lookupContext.setPartition( partition ); 355 356 try ( PartitionTxn partitionTxn = partition.beginReadTransaction() ) 357 { 358 lookupContext.setTransaction( partitionTxn ); 359 360 generateSchemaSubentry( 361 directoryService.getSchemaManager(), 362 directoryService.getSchemaPartition().lookup( lookupContext ) ); 363 } 364 catch ( IOException ioe ) 365 { 366 throw new LdapOtherException( ioe.getMessage(), ioe ); 367 } 368 } 369 370 return schemaSubentry.clone(); 371 } 372 373 374 /** 375 * {@inheritDoc} 376 */ 377 public static Entry getSubschemaEntry( DirectoryService directoryService, FilteringOperationContext operationContext ) 378 throws LdapException 379 { 380 SchemaManager schemaManager = directoryService.getSchemaManager(); 381 382 Entry attrs = new DefaultEntry( schemaManager, Dn.ROOT_DSE ); 383 384 synchronized ( schemaSubentrLock ) 385 { 386 // --------------------------------------------------------------- 387 // Check if we need an update by looking at timestamps on disk 388 // --------------------------------------------------------------- 389 Dn schemaModificationAttributesDn = new Dn( directoryService.getSchemaManager(), 390 SchemaConstants.SCHEMA_MODIFICATIONS_DN ); 391 392 Partition partition = directoryService.getSchemaPartition(); 393 394 LookupOperationContext lookupContext = new LookupOperationContext( null, schemaModificationAttributesDn, SchemaConstants.ALL_ATTRIBUTES_ARRAY ); 395 lookupContext.setPartition( partition ); 396 397 try ( PartitionTxn partitionTxn = partition.beginReadTransaction() ) 398 { 399 lookupContext.setTransaction( partitionTxn ); 400 401 Entry mods = 402 directoryService.getSchemaPartition().lookup( lookupContext ); 403 // TODO enable this optimization at some point but for now it 404 // is causing some problems so I will just turn it off 405 // Attribute modifyTimeDisk = mods.get( SchemaConstants.MODIFY_TIMESTAMP_AT ); 406 // 407 // Attribute modifyTimeMemory = null; 408 // 409 // if ( schemaSubentry != null ) 410 // { 411 // modifyTimeMemory = schemaSubentry.get( SchemaConstants.MODIFY_TIMESTAMP_AT ); 412 // if ( modifyTimeDisk == null && modifyTimeMemory == null ) 413 // { 414 // // do nothing! 415 // } 416 // else if ( modifyTimeDisk != null && modifyTimeMemory != null ) 417 // { 418 // Date disk = DateUtils.getDate( ( String ) modifyTimeDisk.get() ); 419 // Date mem = DateUtils.getDate( ( String ) modifyTimeMemory.get() ); 420 // if ( disk.after( mem ) ) 421 // { 422 // generateSchemaSubentry( mods ); 423 // } 424 // } 425 // else 426 // { 427 // generateSchemaSubentry( mods ); 428 // } 429 // } 430 // else 431 // { 432 generateSchemaSubentry( schemaManager, mods ); 433 } 434 catch ( IOException ioe ) 435 { 436 throw new LdapOtherException( ioe.getMessage(), ioe ); 437 } 438 439 // --------------------------------------------------------------- 440 // Prep Work: Transform the attributes to their OID counterpart 441 // --------------------------------------------------------------- 442 addAttribute( attrs, SchemaConstants.COMPARATORS_AT ); 443 addAttribute( attrs, SchemaConstants.NORMALIZERS_AT ); 444 addAttribute( attrs, SchemaConstants.SYNTAX_CHECKERS_AT ); 445 addAttribute( attrs, SchemaConstants.OBJECT_CLASSES_AT ); 446 addAttribute( attrs, SchemaConstants.ATTRIBUTE_TYPES_AT ); 447 addAttribute( attrs, SchemaConstants.MATCHING_RULES_AT ); 448 addAttribute( attrs, SchemaConstants.MATCHING_RULE_USE_AT ); 449 addAttribute( attrs, SchemaConstants.LDAP_SYNTAXES_AT ); 450 addAttribute( attrs, SchemaConstants.DIT_CONTENT_RULES_AT ); 451 addAttribute( attrs, SchemaConstants.DIT_STRUCTURE_RULES_AT ); 452 addAttribute( attrs, SchemaConstants.NAME_FORMS_AT ); 453 addAttribute( attrs, SchemaConstants.SUBTREE_SPECIFICATION_AT ); 454 455 // add the objectClass attribute if needed 456 addAttribute( attrs, SchemaConstants.OBJECT_CLASS_AT ); 457 458 // add the cn attribute as required for the Rdn 459 addAttribute( attrs, SchemaConstants.CN_AT ); 460 461 // ------------------------------------------------------------------- 462 // set standard operational attributes for the subentry 463 // ------------------------------------------------------------------- 464 addAttribute( attrs, SchemaConstants.CREATE_TIMESTAMP_AT ); 465 addAttribute( attrs, SchemaConstants.CREATORS_NAME_AT ); 466 addAttribute( attrs, SchemaConstants.MODIFY_TIMESTAMP_AT ); 467 addAttribute( attrs, SchemaConstants.MODIFIERS_NAME_AT ); 468 addAttribute( attrs, SchemaConstants.ENTRY_UUID_AT ); 469 addAttribute( attrs, SchemaConstants.ENTRY_DN_AT ); 470 } 471 472 return new ClonedServerEntry( attrs ); 473 } 474}