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.ArrayList; 024import java.util.List; 025 026import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants; 027import org.apache.directory.api.ldap.model.constants.SchemaConstants; 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.LdapInvalidDnException; 031import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException; 032import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 033import org.apache.directory.api.ldap.model.name.Dn; 034import org.apache.directory.api.ldap.model.name.Rdn; 035import org.apache.directory.api.ldap.model.schema.AttributeType; 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.SchemaManager; 039import org.apache.directory.api.ldap.model.schema.SchemaObject; 040import org.apache.directory.api.ldap.model.schema.registries.Schema; 041import org.apache.directory.api.util.Strings; 042import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 043import org.apache.directory.server.i18n.I18n; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047 048/** 049 * A syntax specific registry synchronizer which responds to syntax entry 050 * changes in the DIT to update the syntax registry. 051 * 052 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 053 */ 054public class SyntaxSynchronizer extends AbstractRegistrySynchronizer 055{ 056 /** A logger for this class */ 057 private static final Logger LOG = LoggerFactory.getLogger( SyntaxSynchronizer.class ); 058 059 060 /** 061 * Creates a new instance of SyntaxSynchronizer. 062 * 063 * @param schemaManager The global schemaManager 064 * @throws Exception If the initialization failed 065 */ 066 public SyntaxSynchronizer( SchemaManager schemaManager ) throws Exception 067 { 068 super( schemaManager ); 069 } 070 071 072 /** 073 * {@inheritDoc} 074 */ 075 @Override 076 public boolean modify( ModifyOperationContext modifyContext, Entry targetEntry, boolean cascade ) 077 throws LdapException 078 { 079 Dn name = modifyContext.getDn(); 080 Entry entry = modifyContext.getEntry(); 081 String oid = getOid( entry ); 082 LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(), 083 getSchemaName( name ) ); 084 String schemaName = getSchemaName( entry.getDn() ); 085 086 if ( isSchemaEnabled( schemaName ) ) 087 { 088 schemaManager.unregisterLdapSyntax( oid ); 089 schemaManager.add( syntax ); 090 091 return SCHEMA_MODIFIED; 092 } 093 094 return SCHEMA_UNCHANGED; 095 } 096 097 098 /** 099 * {@inheritDoc} 100 */ 101 @Override 102 public void add( Entry entry ) throws LdapException 103 { 104 Dn dn = entry.getDn(); 105 Dn parentDn = dn.getParent(); 106 107 // The parent Dn must be ou=syntaxes,cn=<schemaName>,ou=schema 108 checkParent( parentDn, schemaManager, SchemaConstants.SYNTAX ); 109 110 // The new schemaObject's OID must not already exist 111 checkOidIsUnique( entry ); 112 113 // Build the new Syntax from the given entry 114 String schemaName = getSchemaName( dn ); 115 116 LdapSyntax syntax = factory.getSyntax( schemaManager, entry, schemaManager.getRegistries(), schemaName ); 117 118 // At this point, the constructed Syntax has not been checked against the 119 // existing Registries. It may be broken (missing SUP, or such), it will be checked 120 // there, if the schema and the Syntax are both enabled. 121 Schema schema = schemaManager.getLoadedSchema( schemaName ); 122 123 if ( schema.isEnabled() && syntax.isEnabled() ) 124 { 125 if ( schemaManager.add( syntax ) ) 126 { 127 LOG.debug( "Added {} into the enabled schema {}", dn.getName(), schemaName ); 128 } 129 else 130 { 131 // We have some error : reject the addition and get out 132 String msg = I18n.err( I18n.ERR_399, entry.getDn().getName(), 133 Strings.listToString( schemaManager.getErrors() ) ); 134 LOG.info( msg ); 135 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 136 } 137 } 138 else 139 { 140 LOG.debug( "The Syntax {} cannot be added in the disabled schema {}", dn.getName(), schemaName ); 141 } 142 } 143 144 145 /** 146 * Check if a syntax is used by an AT or a MR 147 */ 148 private List<SchemaObject> checkInUse( String oid ) 149 { 150 List<SchemaObject> dependees = new ArrayList<>(); 151 152 for ( AttributeType attributeType : schemaManager.getAttributeTypeRegistry() ) 153 { 154 if ( oid.equals( attributeType.getSyntax().getOid() ) ) 155 { 156 dependees.add( attributeType ); 157 } 158 } 159 160 for ( MatchingRule matchingRule : schemaManager.getMatchingRuleRegistry() ) 161 { 162 if ( oid.equals( matchingRule.getSyntax().getOid() ) ) 163 { 164 dependees.add( matchingRule ); 165 } 166 } 167 168 return dependees; 169 } 170 171 172 /** 173 * Get the list of SchemaObject's name using a given syntax 174 */ 175 private String getNames( List<SchemaObject> schemaObjects ) 176 { 177 StringBuilder sb = new StringBuilder(); 178 boolean isFirst = true; 179 180 for ( SchemaObject schemaObject : schemaObjects ) 181 { 182 if ( isFirst ) 183 { 184 isFirst = false; 185 } 186 else 187 { 188 sb.append( ", " ); 189 } 190 191 sb.append( schemaObject.getName() ); 192 } 193 194 return sb.toString(); 195 } 196 197 198 /** 199 * {@inheritDoc} 200 */ 201 @Override 202 public void delete( Entry entry, boolean cascade ) throws LdapException 203 { 204 Dn dn = entry.getDn(); 205 Dn parentDn = dn.getParent(); 206 207 // The parent Dn must be ou=syntaxes,cn=<schemaName>,ou=schema 208 checkParent( parentDn, schemaManager, SchemaConstants.SYNTAX ); 209 210 // Get the Syntax from the given entry ( it has been grabbed from the server earlier) 211 String schemaName = getSchemaName( entry.getDn() ); 212 213 // Get the schema 214 Schema schema = schemaManager.getLoadedSchema( schemaName ); 215 216 if ( schema.isDisabled() ) 217 { 218 // The schema is disabled, nothing to do. 219 LOG.debug( "The Syntax {} cannot be removed from the disabled schema {}.", 220 dn.getName(), schemaName ); 221 222 return; 223 } 224 225 // Test that the Oid exists 226 LdapSyntax syntax = ( LdapSyntax ) checkOidExists( entry ); 227 228 List<Throwable> errors = new ArrayList<>(); 229 230 if ( schema.isEnabled() && syntax.isEnabled() ) 231 { 232 if ( schemaManager.delete( syntax ) ) 233 { 234 LOG.debug( "Removed {} from the schema {}", syntax, schemaName ); 235 } 236 else 237 { 238 // We have some error : reject the deletion and get out 239 String msg = I18n.err( I18n.ERR_400, entry.getDn().getName(), 240 Strings.listToString( errors ) ); 241 LOG.info( msg ); 242 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg ); 243 } 244 } 245 else 246 { 247 LOG.debug( "Removed {} from the disabled schema {}", syntax, schemaName ); 248 } 249 } 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 @Override 256 public void rename( Entry entry, Rdn newRdn, boolean cascade ) throws LdapException 257 { 258 String oldOid = getOid( entry ); 259 String schemaName = getSchemaName( entry.getDn() ); 260 261 // Check that this syntax is not used by an AttributeType 262 List<SchemaObject> dependees = checkInUse( oldOid ); 263 264 if ( !dependees.isEmpty() ) 265 { 266 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err( I18n.ERR_401, 267 oldOid, 268 getNames( dependees ) ) ); 269 } 270 271 Entry targetEntry = entry.clone(); 272 String newOid = newRdn.getValue(); 273 checkOidIsUnique( newOid ); 274 275 targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid ); 276 LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(), 277 getSchemaName( entry.getDn() ) ); 278 279 if ( isSchemaEnabled( schemaName ) ) 280 { 281 schemaManager.unregisterLdapSyntax( oldOid ); 282 schemaManager.add( syntax ); 283 } 284 else 285 { 286 // always remove old OIDs that are not in schema anymore 287 unregisterOids( syntax ); 288 // even for disabled schemas add OIDs 289 registerOids( syntax ); 290 } 291 } 292 293 294 /** 295 * {@inheritDoc} 296 */ 297 @Override 298 public void moveAndRename( Dn oriChildName, Dn newParentName, Rdn newRn, boolean deleteOldRn, 299 Entry entry, boolean cascade ) throws LdapException 300 { 301 checkNewParent( newParentName ); 302 String oldOid = getOid( entry ); 303 String oldSchemaName = getSchemaName( oriChildName ); 304 String newSchemaName = getSchemaName( newParentName ); 305 306 // Check that this syntax is not used by an AttributeType 307 List<SchemaObject> dependees = checkInUse( oldOid ); 308 309 if ( !dependees.isEmpty() ) 310 { 311 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, 312 I18n.err( I18n.ERR_401, oldOid, getNames( dependees ) ) ); 313 } 314 315 Entry targetEntry = entry.clone(); 316 String newOid = newRn.getValue(); 317 checkOidIsUnique( newOid ); 318 319 targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid ); 320 LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(), 321 getSchemaName( newParentName ) ); 322 323 if ( isSchemaEnabled( oldSchemaName ) ) 324 { 325 schemaManager.unregisterLdapSyntax( oldOid ); 326 } 327 else 328 { 329 unregisterOids( syntax ); 330 } 331 332 if ( isSchemaEnabled( newSchemaName ) ) 333 { 334 schemaManager.add( syntax ); 335 } 336 else 337 { 338 // register new syntax OIDs even if schema is disabled 339 registerOids( syntax ); 340 } 341 } 342 343 344 /** 345 * {@inheritDoc} 346 */ 347 @Override 348 public void move( Dn oriChildName, Dn newParentName, Entry entry, boolean cascade ) throws LdapException 349 { 350 checkNewParent( newParentName ); 351 String oid = getOid( entry ); 352 String oldSchemaName = getSchemaName( oriChildName ); 353 String newSchemaName = getSchemaName( newParentName ); 354 355 LdapSyntax syntax = factory.getSyntax( schemaManager, entry, schemaManager.getRegistries(), 356 getSchemaName( newParentName ) ); 357 358 if ( isSchemaEnabled( oldSchemaName ) ) 359 { 360 schemaManager.unregisterLdapSyntax( oid ); 361 } 362 else 363 { 364 unregisterOids( syntax ); 365 } 366 367 if ( isSchemaEnabled( newSchemaName ) ) 368 { 369 schemaManager.add( syntax ); 370 } 371 else 372 { 373 registerOids( syntax ); 374 } 375 } 376 377 378 private void checkNewParent( Dn newParent ) throws LdapException 379 { 380 if ( newParent.size() != 3 ) 381 { 382 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, 383 I18n.err( I18n.ERR_402 ) ); 384 } 385 386 Rdn rdn = newParent.getRdn(); 387 if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals( 388 SchemaConstants.OU_AT_OID ) ) 389 { 390 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_403 ) ); 391 } 392 393 if ( !rdn.getValue().equalsIgnoreCase( SchemaConstants.SYNTAXES ) ) 394 { 395 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_363 ) ); 396 } 397 } 398}