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.server.core.api.schema.registries.synchronizers; 021 022 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Locale; 026import java.util.Map; 027import java.util.Set; 028 029import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants; 030import org.apache.directory.api.ldap.model.constants.SchemaConstants; 031import org.apache.directory.api.ldap.model.entry.Attribute; 032import org.apache.directory.api.ldap.model.entry.Entry; 033import org.apache.directory.api.ldap.model.entry.Value; 034import org.apache.directory.api.ldap.model.exception.LdapException; 035import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 036import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException; 037import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 038import org.apache.directory.api.ldap.model.schema.AttributeType; 039import org.apache.directory.api.ldap.model.schema.ObjectClass; 040import org.apache.directory.api.ldap.model.schema.SchemaManager; 041import org.apache.directory.api.ldap.model.schema.registries.ObjectClassRegistry; 042import org.apache.directory.api.util.Strings; 043import org.apache.directory.server.constants.ApacheSchemaConstants; 044import org.apache.directory.server.core.api.entry.ClonedServerEntry; 045import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; 046import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext; 047import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 048import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext; 049import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext; 050import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext; 051import org.apache.directory.server.i18n.I18n; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055 056/** 057 * Central point of control for schemas enforced by the server. The 058 * following duties are presently performed by this class: 059 * 060 * <ul> 061 * <li>Provide central point of access for all registries: global and SAA specific registries</li> 062 * <li>Manage enabling and disabling schemas</li> 063 * <li>Responding to specific schema object changes</li> 064 * </ul> 065 * 066 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 067 */ 068public class RegistrySynchronizerAdaptor 069{ 070 /** A logger for this class */ 071 private static final Logger LOG = LoggerFactory.getLogger( RegistrySynchronizerAdaptor.class ); 072 073 // indices of handlers and object ids into arrays 074 private static final int COMPARATOR_INDEX = 0; 075 private static final int NORMALIZER_INDEX = 1; 076 private static final int SYNTAX_CHECKER_INDEX = 2; 077 private static final int SYNTAX_INDEX = 3; 078 private static final int MATCHING_RULE_INDEX = 4; 079 private static final int ATTRIBUTE_TYPE_INDEX = 5; 080 private static final int OBJECT_CLASS_INDEX = 6; 081 private static final int MATCHING_RULE_USE_INDEX = 7; 082 private static final int DIT_STRUCTURE_RULE_INDEX = 8; 083 private static final int DIT_CONTENT_RULE_INDEX = 9; 084 private static final int NAME_FORM_INDEX = 10; 085 086 private static final Set<String> VALID_OU_VALUES = new HashSet<>(); 087 private static final String[] META_OBJECT_CLASSES = new String[] 088 { 089 MetaSchemaConstants.META_COMPARATOR_OC, 090 MetaSchemaConstants.META_NORMALIZER_OC, 091 MetaSchemaConstants.META_SYNTAX_CHECKER_OC, 092 MetaSchemaConstants.META_SYNTAX_OC, 093 MetaSchemaConstants.META_MATCHING_RULE_OC, 094 MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC, 095 MetaSchemaConstants.META_OBJECT_CLASS_OC, 096 MetaSchemaConstants.META_MATCHING_RULE_USE_OC, 097 MetaSchemaConstants.META_DIT_STRUCTURE_RULE_OC, 098 MetaSchemaConstants.META_DIT_CONTENT_RULE_OC, 099 MetaSchemaConstants.META_NAME_FORM_OC 100 }; 101 102 /** The SchemaManager */ 103 private final SchemaManager schemaManager; 104 105 /** The ObjectClss Attribute */ 106 private final AttributeType objectClassAT; 107 108 private final RegistrySynchronizer[] registrySynchronizers = new RegistrySynchronizer[11]; 109 private final Map<String, RegistrySynchronizer> objectClass2synchronizerMap = new HashMap<>(); 110 private final SchemaSynchronizer schemaSynchronizer; 111 112 static 113 { 114 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.NORMALIZERS_AT ) ); 115 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.COMPARATORS_AT ) ); 116 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.SYNTAX_CHECKERS_AT ) ); 117 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.SYNTAXES ) ); 118 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.MATCHING_RULES_AT ) ); 119 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.MATCHING_RULE_USE_AT ) ); 120 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.ATTRIBUTE_TYPES_AT ) ); 121 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.OBJECT_CLASSES_AT ) ); 122 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.NAME_FORMS_AT ) ); 123 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.DIT_CONTENT_RULES_AT ) ); 124 VALID_OU_VALUES.add( Strings.toLowerCaseAscii( SchemaConstants.DIT_STRUCTURE_RULES_AT ) ); 125 } 126 127 128 public RegistrySynchronizerAdaptor( SchemaManager schemaManager ) throws Exception 129 { 130 this.schemaManager = schemaManager; 131 this.schemaSynchronizer = new SchemaSynchronizer( schemaManager ); 132 this.objectClassAT = schemaManager.lookupAttributeTypeRegistry( SchemaConstants.OBJECT_CLASS_AT ); 133 134 this.registrySynchronizers[COMPARATOR_INDEX] = new ComparatorSynchronizer( schemaManager ); 135 this.registrySynchronizers[NORMALIZER_INDEX] = new NormalizerSynchronizer( schemaManager ); 136 this.registrySynchronizers[SYNTAX_CHECKER_INDEX] = new SyntaxCheckerSynchronizer( schemaManager ); 137 this.registrySynchronizers[SYNTAX_INDEX] = new SyntaxSynchronizer( schemaManager ); 138 this.registrySynchronizers[MATCHING_RULE_INDEX] = new MatchingRuleSynchronizer( schemaManager ); 139 this.registrySynchronizers[ATTRIBUTE_TYPE_INDEX] = new AttributeTypeSynchronizer( schemaManager ); 140 this.registrySynchronizers[OBJECT_CLASS_INDEX] = new ObjectClassSynchronizer( schemaManager ); 141 this.registrySynchronizers[MATCHING_RULE_USE_INDEX] = new MatchingRuleUseSynchronizer( schemaManager ); 142 this.registrySynchronizers[DIT_STRUCTURE_RULE_INDEX] = new DitStructureRuleSynchronizer( schemaManager ); 143 this.registrySynchronizers[DIT_CONTENT_RULE_INDEX] = new DitContentRuleSynchronizer( schemaManager ); 144 this.registrySynchronizers[NAME_FORM_INDEX] = new NameFormSynchronizer( schemaManager ); 145 146 ObjectClassRegistry ocReg = schemaManager.getObjectClassRegistry(); 147 148 for ( int ii = 0; ii < META_OBJECT_CLASSES.length; ii++ ) 149 { 150 ObjectClass oc = ocReg.lookup( META_OBJECT_CLASSES[ii] ); 151 objectClass2synchronizerMap.put( oc.getOid(), registrySynchronizers[ii] ); 152 } 153 } 154 155 156 /** 157 * Add a new SchemaObject or a new Schema in the Schema partition. 158 * 159 * @param addContext The Add context, containing the entry to be added 160 * @throws LdapException If the addition failed 161 */ 162 public void add( AddOperationContext addContext ) throws LdapException 163 { 164 Attribute oc = addContext.getEntry().get( objectClassAT ); 165 166 // First check if we are adding a schemaObject 167 for ( Value value : oc ) 168 { 169 170 String oid = schemaManager.getObjectClassRegistry().getOidByName( value.getString() ); 171 172 if ( objectClass2synchronizerMap.containsKey( oid ) ) 173 { 174 // This is one of the eleven SchemaObject : 175 // AT, C, DCR, DSR, MR, MRU, NF, N, OC, S, SC 176 RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid ); 177 Entry entry = addContext.getEntry(); 178 synchronizer.add( entry ); 179 180 return; 181 } 182 } 183 184 // This is a Schema 185 // e.g. ou=my custom schema,ou=schema 186 if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) ) 187 { 188 Entry entry = addContext.getEntry(); 189 schemaSynchronizer.add( entry ); 190 191 return; 192 } 193 194 // Check if it is a valid container for AT, C, DCR, DSR, MR, MRU, NF, N, OC, S, SC 195 // e.g. ou=attributeTypes,ou=my custom schema,ou=schema 196 if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) ) 197 { 198 if ( addContext.getDn().size() != 3 ) 199 { 200 String msg = I18n.err( I18n.ERR_81 ); 201 LOG.error( msg ); 202 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, msg ); 203 } 204 205 String ouValue = addContext.getDn().getRdn().getValue(); 206 ouValue = Strings.toLowerCaseAscii( Strings.trim( ouValue ) ); 207 208 if ( !VALID_OU_VALUES.contains( ouValue ) ) 209 { 210 String msg = I18n.err( I18n.ERR_82, VALID_OU_VALUES ); 211 LOG.error( msg ); 212 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, msg ); 213 } 214 215 // this is a valid container. 216 return; 217 } 218 219 String msg = I18n.err( I18n.ERR_83, addContext.getDn() ); 220 LOG.error( msg ); 221 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 222 } 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 public void delete( DeleteOperationContext deleteContext, boolean doCascadeDelete ) 229 throws LdapException 230 { 231 Entry entry = deleteContext.getEntry(); 232 233 Attribute oc = entry.get( objectClassAT ); 234 235 for ( Value value : oc ) 236 { 237 String oid = schemaManager.getObjectClassRegistry().getOidByName( value.getString() ); 238 239 if ( objectClass2synchronizerMap.containsKey( oid ) ) 240 { 241 RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid ); 242 synchronizer.delete( entry, doCascadeDelete ); 243 return; 244 } 245 } 246 247 if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) ) 248 { 249 schemaSynchronizer.delete( entry, doCascadeDelete ); 250 return; 251 } 252 253 if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) ) 254 { 255 if ( deleteContext.getDn().size() != 3 ) 256 { 257 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err( I18n.ERR_378 ) ); 258 } 259 260 String ouValue = deleteContext.getDn().getRdn().getValue(); 261 ouValue = Strings.toLowerCaseAscii( Strings.trim( ouValue ) ); 262 263 if ( !VALID_OU_VALUES.contains( ouValue ) ) 264 { 265 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, 266 I18n.err( I18n.ERR_379, VALID_OU_VALUES ) ); 267 } 268 269 return; 270 } 271 272 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM ); 273 } 274 275 276 /** 277 * Modify the schema 278 * 279 * @param modifyContext The context 280 * @param targetEntry The modified entry 281 * @param doCascadeModify Not used 282 * @return <tt>true</tt> if the modify succeded 283 * @throws LdapException If the modification failed 284 */ 285 public boolean modify( ModifyOperationContext modifyContext, Entry targetEntry, boolean doCascadeModify ) 286 throws LdapException 287 { 288 Entry entry = modifyContext.getEntry(); 289 Attribute oc = entry.get( objectClassAT ); 290 291 for ( Value value : oc ) 292 { 293 String oid = schemaManager.getObjectClassRegistry().getOidByName( value.getString() ); 294 295 if ( objectClass2synchronizerMap.containsKey( oid ) ) 296 { 297 RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid ); 298 299 return synchronizer.modify( modifyContext, targetEntry, doCascadeModify ); 300 } 301 } 302 303 if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) ) 304 { 305 return schemaSynchronizer.modify( modifyContext, targetEntry, doCascadeModify ); 306 } 307 308 if ( oc.contains( ApacheSchemaConstants.SCHEMA_MODIFICATION_ATTRIBUTES_OC ) ) 309 { 310 return false; 311 } 312 313 LOG.error( String.format( Locale.ROOT, I18n.err( I18n.ERR_84 ), 314 modifyContext.getDn(), entry, modifyContext.getModItems() ) ); 315 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM ); 316 } 317 318 319 /** 320 * Rename a Schema Object. 321 * 322 * @param renameContext The contect contaoning the rename informations 323 * @param doCascadeModify unused 324 * @throws LdapException If the rename failed 325 */ 326 public void rename( RenameOperationContext renameContext, boolean doCascadeModify ) 327 throws LdapException 328 { 329 Entry originalEntry = ( ( ClonedServerEntry ) renameContext.getEntry() ).getOriginalEntry(); 330 Attribute oc = originalEntry.get( objectClassAT ); 331 332 for ( Value value : oc ) 333 { 334 String oid = schemaManager.getObjectClassRegistry().getOidByName( value.getString() ); 335 336 if ( objectClass2synchronizerMap.containsKey( oid ) ) 337 { 338 RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid ); 339 synchronizer.rename( originalEntry, renameContext.getNewRdn(), doCascadeModify ); 340 return; 341 } 342 } 343 344 if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) ) 345 { 346 schemaSynchronizer.rename( originalEntry, renameContext.getNewRdn(), doCascadeModify ); 347 return; 348 } 349 350 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM ); 351 } 352 353 354 /* (non-Javadoc) 355 * @see org.apache.directory.server.core.schema.SchemaChangeManager#replace(org.apache.directory.server.core.interceptor.context.MoveOperationContext, org.apache.directory.server.core.entry.Entry, boolean) 356 */ 357 public void move( MoveOperationContext moveContext, Entry entry, boolean cascade ) throws LdapException 358 { 359 Attribute oc = entry.get( objectClassAT ); 360 361 for ( Value value : oc ) 362 { 363 String oid = schemaManager.getObjectClassRegistry().getOidByName( value.getString() ); 364 365 if ( objectClass2synchronizerMap.containsKey( oid ) ) 366 { 367 RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid ); 368 synchronizer.move( moveContext.getDn(), moveContext.getNewSuperior(), entry, cascade ); 369 return; 370 } 371 } 372 373 if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) ) 374 { 375 schemaSynchronizer.move( moveContext.getDn(), moveContext.getNewSuperior(), entry, cascade ); 376 return; 377 } 378 379 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM ); 380 } 381 382 383 /* (non-Javadoc) 384 * @see org.apache.directory.server.core.schema.SchemaChangeManager#move(org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext, org.apache.directory.server.core.entry.Entry, boolean) 385 */ 386 public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext, Entry entry, boolean cascade ) 387 throws LdapException 388 { 389 Attribute oc = entry.get( objectClassAT ); 390 391 for ( Value value : oc ) 392 { 393 String oid = schemaManager.getObjectClassRegistry().getOidByName( value.getString() ); 394 395 if ( objectClass2synchronizerMap.containsKey( oid ) ) 396 { 397 RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid ); 398 synchronizer.moveAndRename( moveAndRenameContext.getDn(), moveAndRenameContext.getNewSuperiorDn(), 399 moveAndRenameContext.getNewRdn(), 400 moveAndRenameContext.getDeleteOldRdn(), entry, cascade ); 401 return; 402 } 403 } 404 405 if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) ) 406 { 407 schemaSynchronizer.moveAndRename( moveAndRenameContext.getDn(), moveAndRenameContext.getNewSuperiorDn(), 408 moveAndRenameContext.getNewRdn(), 409 moveAndRenameContext.getDeleteOldRdn(), entry, cascade ); 410 return; 411 } 412 413 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM ); 414 } 415}