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 */ 020 package org.apache.directory.server.core.schema.registries.synchronizers; 021 022 023 import java.util.ArrayList; 024 import java.util.List; 025 026 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext; 027 import org.apache.directory.server.i18n.I18n; 028 import org.apache.directory.shared.ldap.constants.MetaSchemaConstants; 029 import org.apache.directory.shared.ldap.constants.SchemaConstants; 030 import org.apache.directory.shared.ldap.entry.ServerEntry; 031 import org.apache.directory.shared.ldap.exception.LdapException; 032 import org.apache.directory.shared.ldap.exception.LdapInvalidDnException; 033 import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException; 034 import org.apache.directory.shared.ldap.message.ResultCodeEnum; 035 import org.apache.directory.shared.ldap.name.DN; 036 import org.apache.directory.shared.ldap.name.RDN; 037 import org.apache.directory.shared.ldap.schema.AttributeType; 038 import org.apache.directory.shared.ldap.schema.LdapSyntax; 039 import org.apache.directory.shared.ldap.schema.MatchingRule; 040 import org.apache.directory.shared.ldap.schema.SchemaManager; 041 import org.apache.directory.shared.ldap.schema.SchemaObject; 042 import org.apache.directory.shared.ldap.schema.registries.Schema; 043 import org.apache.directory.shared.ldap.util.StringTools; 044 import org.slf4j.Logger; 045 import 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 * @version $Rev$, $Date$ 054 */ 055 public class SyntaxSynchronizer extends AbstractRegistrySynchronizer 056 { 057 /** A logger for this class */ 058 private static final Logger LOG = LoggerFactory.getLogger( SyntaxSynchronizer.class ); 059 060 061 /** 062 * Creates a new instance of SyntaxSynchronizer. 063 * 064 * @param schemaManager The global schemaManager 065 * @throws Exception If the initialization failed 066 */ 067 public SyntaxSynchronizer( SchemaManager schemaManager ) throws Exception 068 { 069 super( schemaManager ); 070 } 071 072 073 /** 074 * {@inheritDoc} 075 */ 076 public boolean modify( ModifyOperationContext opContext, ServerEntry targetEntry, boolean cascade ) 077 throws Exception 078 { 079 DN name = opContext.getDn(); 080 ServerEntry entry = opContext.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 public void add( ServerEntry entry ) throws Exception 102 { 103 DN dn = entry.getDn(); 104 DN parentDn = ( DN ) dn.clone(); 105 parentDn.remove( parentDn.size() - 1 ); 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 StringTools.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<SchemaObject>(); 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 public void delete( ServerEntry entry, boolean cascade ) throws Exception 202 { 203 DN dn = entry.getDn(); 204 DN parentDn = ( DN ) dn.clone(); 205 parentDn.remove( parentDn.size() - 1 ); 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<Throwable>(); 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 StringTools.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 public void rename( ServerEntry entry, RDN newRdn, boolean cascade ) throws Exception 256 { 257 String oldOid = getOid( entry ); 258 String schemaName = getSchemaName( entry.getDn() ); 259 260 // Check that this syntax is not used by an AttributeType 261 List<SchemaObject> dependees = checkInUse( oldOid ); 262 263 if ( dependees.size() != 0 ) 264 { 265 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err( I18n.ERR_401, oldOid, 266 getNames( dependees ) ) ); 267 } 268 269 ServerEntry targetEntry = ( ServerEntry ) entry.clone(); 270 String newOid = ( String ) newRdn.getNormValue(); 271 checkOidIsUnique( newOid ); 272 273 targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid ); 274 LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(), 275 getSchemaName( entry.getDn() ) ); 276 277 if ( isSchemaEnabled( schemaName ) ) 278 { 279 schemaManager.unregisterLdapSyntax( oldOid ); 280 schemaManager.add( syntax ); 281 } 282 else 283 { 284 // always remove old OIDs that are not in schema anymore 285 unregisterOids( syntax ); 286 // even for disabled schemas add OIDs 287 registerOids( syntax ); 288 } 289 } 290 291 292 public void moveAndRename( DN oriChildName, DN newParentName, RDN newRn, boolean deleteOldRn, 293 ServerEntry entry, boolean cascade ) throws Exception 294 { 295 checkNewParent( newParentName ); 296 String oldOid = getOid( entry ); 297 String oldSchemaName = getSchemaName( oriChildName ); 298 String newSchemaName = getSchemaName( newParentName ); 299 300 // Check that this syntax is not used by an AttributeType 301 List<SchemaObject> dependees = checkInUse( oldOid ); 302 303 if ( dependees.size() != 0 ) 304 { 305 throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, 306 I18n.err( I18n.ERR_401, oldOid, getNames( dependees ) ) ); 307 } 308 309 ServerEntry targetEntry = ( ServerEntry ) entry.clone(); 310 String newOid = ( String ) newRn.getNormValue(); 311 checkOidIsUnique( newOid ); 312 313 targetEntry.put( MetaSchemaConstants.M_OID_AT, newOid ); 314 LdapSyntax syntax = factory.getSyntax( schemaManager, targetEntry, schemaManager.getRegistries(), 315 getSchemaName( newParentName ) ); 316 317 if ( isSchemaEnabled( oldSchemaName ) ) 318 { 319 schemaManager.unregisterLdapSyntax( oldOid ); 320 } 321 else 322 { 323 unregisterOids( syntax ); 324 } 325 326 if ( isSchemaEnabled( newSchemaName ) ) 327 { 328 schemaManager.add( syntax ); 329 } 330 else 331 { 332 // register new syntax OIDs even if schema is disabled 333 registerOids( syntax ); 334 } 335 } 336 337 338 public void move( DN oriChildName, DN newParentName, ServerEntry entry, boolean cascade ) throws Exception 339 { 340 checkNewParent( newParentName ); 341 String oid = getOid( entry ); 342 String oldSchemaName = getSchemaName( oriChildName ); 343 String newSchemaName = getSchemaName( newParentName ); 344 345 // schema dep check before delete to be handled by the SchemaPartition 346 // 347 // Set<ServerEntry> dependees = dao.listSyntaxDependents( oid ); 348 // 349 // if ( dependees != null && dependees.size() > 0 ) 350 // { 351 // throw new LdapUnwillingToPerformException( "The syntax with OID " + oid 352 // + " cannot be deleted until all entities" 353 // + " using this syntax have also been deleted. The following dependees exist: " 354 // + getOids( dependees ), 355 // ResultCodeEnum.UNWILLING_TO_PERFORM ); 356 // } 357 358 LdapSyntax syntax = factory.getSyntax( schemaManager, entry, schemaManager.getRegistries(), 359 getSchemaName( newParentName ) ); 360 361 if ( isSchemaEnabled( oldSchemaName ) ) 362 { 363 schemaManager.unregisterLdapSyntax( oid ); 364 } 365 else 366 { 367 unregisterOids( syntax ); 368 } 369 370 if ( isSchemaEnabled( newSchemaName ) ) 371 { 372 schemaManager.add( syntax ); 373 } 374 else 375 { 376 registerOids( syntax ); 377 } 378 } 379 380 381 private void checkNewParent( DN newParent ) throws LdapException 382 { 383 if ( newParent.size() != 3 ) 384 { 385 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, 386 I18n.err( I18n.ERR_402 ) ); 387 } 388 389 RDN rdn = newParent.getRdn(); 390 if ( !schemaManager.getAttributeTypeRegistry().getOidByName( rdn.getNormType() ).equals( 391 SchemaConstants.OU_AT_OID ) ) 392 { 393 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_403 ) ); 394 } 395 396 if ( !( ( String ) rdn.getNormValue() ).equalsIgnoreCase( "syntaxes" ) ) 397 { 398 throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_363 ) ); 399 } 400 } 401 }