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.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import org.apache.directory.server.core.CoreSession;
030    import org.apache.directory.server.core.OperationManager;
031    import org.apache.directory.server.core.entry.ServerEntryUtils;
032    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
033    import org.apache.directory.server.core.partition.ByPassConstants;
034    import org.apache.directory.server.i18n.I18n;
035    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
036    import org.apache.directory.shared.ldap.constants.SchemaConstants;
037    import org.apache.directory.shared.ldap.entry.DefaultServerAttribute;
038    import org.apache.directory.shared.ldap.entry.EntryAttribute;
039    import org.apache.directory.shared.ldap.entry.Modification;
040    import org.apache.directory.shared.ldap.entry.ModificationOperation;
041    import org.apache.directory.shared.ldap.entry.ServerEntry;
042    import org.apache.directory.shared.ldap.entry.ServerModification;
043    import org.apache.directory.shared.ldap.entry.Value;
044    import org.apache.directory.shared.ldap.exception.LdapException;
045    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
046    import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
047    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
048    import org.apache.directory.shared.ldap.name.DN;
049    import org.apache.directory.shared.ldap.name.RDN;
050    import org.apache.directory.shared.ldap.schema.AttributeType;
051    import org.apache.directory.shared.ldap.schema.SchemaManager;
052    import org.apache.directory.shared.ldap.schema.SchemaObject;
053    import org.apache.directory.shared.ldap.schema.SchemaObjectType;
054    import org.apache.directory.shared.ldap.schema.SchemaObjectWrapper;
055    import org.apache.directory.shared.ldap.schema.loader.ldif.SchemaEntityFactory;
056    import org.apache.directory.shared.ldap.schema.registries.AttributeTypeRegistry;
057    import org.apache.directory.shared.ldap.schema.registries.DefaultSchemaObjectRegistry;
058    import org.apache.directory.shared.ldap.schema.registries.Registries;
059    import org.apache.directory.shared.ldap.schema.registries.Schema;
060    import org.apache.directory.shared.ldap.util.DateUtils;
061    import org.apache.directory.shared.ldap.util.StringTools;
062    import org.slf4j.Logger;
063    import org.slf4j.LoggerFactory;
064    
065    
066    /**
067     * This class handle modifications made on a global schema. Modifications made
068     * on SchemaObjects are handled by the specific shcemaObject synchronizers.
069     * 
070     * @TODO poorly implemented - revisit the SchemaChangeHandler for this puppy
071     * and do it right.
072     *
073     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
074     * @version $Rev$, $Date$
075     */
076    public class SchemaSynchronizer implements RegistrySynchronizer
077    {
078        /** A logger for this class */
079        private static final Logger LOG = LoggerFactory.getLogger( SchemaSynchronizer.class );
080    
081        private final SchemaEntityFactory factory;
082        //private final PartitionSchemaLoader loader;
083        
084        private final SchemaManager schemaManager;
085        
086        /** The global registries */
087        private final Registries registries;
088        
089        /** The m-disable AttributeType */
090        private final AttributeType disabledAT;
091        
092        /** The CN attributeType */
093        private final AttributeType cnAT;
094        
095        /** The m-dependencies AttributeType */
096        private final AttributeType dependenciesAT;
097        
098        /** The modifiersName AttributeType */
099        private final AttributeType modifiersNameAT;
100        
101        /** The modifyTimestamp AttributeType */
102        private final AttributeType modifyTimestampAT;
103        
104        /** A static DN referencing ou=schema */
105        private final DN ouSchemaDN;
106    
107        /**
108         * Creates and initializes a new instance of Schema synchronizer
109         *
110         * @param registries The Registries
111         * @param loader The schema loader
112         * @throws Exception If something went wrong
113         */
114        public SchemaSynchronizer( SchemaManager schemaManager ) throws Exception
115        {
116            this.registries = schemaManager.getRegistries();
117            this.schemaManager = schemaManager;
118            disabledAT = registries.getAttributeTypeRegistry().lookup( MetaSchemaConstants.M_DISABLED_AT );
119            factory = new SchemaEntityFactory();
120            cnAT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.CN_AT );
121            dependenciesAT = registries.getAttributeTypeRegistry()
122                .lookup( MetaSchemaConstants.M_DEPENDENCIES_AT );
123            modifiersNameAT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.MODIFIERS_NAME_AT );
124            modifyTimestampAT = registries.getAttributeTypeRegistry().lookup( SchemaConstants.MODIFY_TIMESTAMP_AT );
125            
126            ouSchemaDN = new DN( SchemaConstants.OU_SCHEMA );
127            ouSchemaDN.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
128        }
129    
130    
131        /**
132         * The only modification done on a schema element is on the m-disabled 
133         * attributeType
134         * 
135         * Depending in the existence of this attribute in the previous entry, we will
136         * have to update the entry or not.
137         */
138        public boolean modify( ModifyOperationContext opContext, ServerEntry targetEntry, boolean cascade ) throws Exception
139        {
140            ServerEntry entry = opContext.getEntry();
141            List<Modification> mods = opContext.getModItems(); 
142            boolean hasModification = SCHEMA_UNCHANGED;
143            
144            // Check if the entry has a m-disabled attribute 
145            EntryAttribute disabledInEntry = entry.get( disabledAT );
146            Modification disabledModification = ServerEntryUtils.getModificationItem( mods, disabledAT );
147            
148            // The attribute might be present, but that does not mean we will change it.
149            // If it's absent, and if we have it in the previous entry, that mean we want
150            // to enable the schema
151            if ( disabledModification != null )
152            {
153                // We are trying to modify the m-disabled attribute. 
154                ModificationOperation modification = disabledModification.getOperation();
155                EntryAttribute attribute = disabledModification.getAttribute();
156                
157                hasModification = modifyDisable( opContext, modification, attribute, disabledInEntry );
158            }
159            else if ( disabledInEntry != null )
160            {
161                hasModification = modifyDisable( opContext, ModificationOperation.REMOVE_ATTRIBUTE, null, disabledInEntry );
162            }
163                
164            
165            return hasModification;
166        }
167    
168    
169        public void moveAndRename( DN oriChildName, DN newParentName, RDN newRn, boolean deleteOldRn, ServerEntry entry, boolean cascaded ) throws LdapException
170        {
171    
172        }
173    
174    
175        /**
176         * Handles the addition of a metaSchema object to the schema partition.
177         * 
178         * @param name the dn of the new metaSchema object
179         * @param entry the attributes of the new metaSchema object
180         */
181        public void add( ServerEntry entry ) throws Exception
182        {
183            DN dn = entry.getDn();
184            DN parentDn = ( DN ) dn.clone();
185            parentDn.remove( parentDn.size() - 1 );
186            parentDn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
187    
188            if ( !parentDn.equals( ouSchemaDN ) )
189            {
190                throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, I18n.err( I18n.ERR_380, ouSchemaDN.getName(),
191                        parentDn.getNormName() ) );
192            }
193    
194            // check if the new schema is enabled or disabled
195            boolean isEnabled = false;
196            EntryAttribute disabled = entry.get( disabledAT );
197            
198            if ( disabled == null )
199            {
200                // If the attribute is absent, then the schema is enabled by default
201                isEnabled = true;
202            }
203            else if ( ! disabled.contains( "TRUE" ) )
204            {
205                isEnabled = true;
206            }
207            
208            // check to see that all dependencies are resolved and loaded if this
209            // schema is enabled, otherwise check that the dependency schemas exist
210            checkForDependencies( isEnabled, entry );
211            
212            /*
213             * There's a slight problem that may result when adding a metaSchema
214             * object if the addition of the physical entry fails.  If the schema
215             * is enabled when added in the condition tested below, that schema
216             * is added to the global registries.  We need to add this so subsequent
217             * schema entity additions are loaded into the registries as they are
218             * added to the schema partition.  However if the metaSchema object 
219             * addition fails then we're left with this schema object looking like
220             * it is enabled in the registries object's schema hash.  The effects
221             * of this are unpredictable.
222             * 
223             * This whole problem is due to the inability of these handlers to 
224             * react to a failed operation.  To fix this we would need some way
225             * for these handlers to respond to failed operations and revert their
226             * effects on the registries.
227             * 
228             * TODO: might want to add a set of failedOnXXX methods to the adapter
229             * where on failure the schema service calls the schema manager and it
230             * calls the appropriate methods on the respective handler.  This way
231             * the schema manager can rollback registry changes when LDAP operations
232             * fail.
233             */
234    
235            if ( isEnabled )
236            {
237                Schema schema = factory.getSchema( entry );
238                schemaManager.load( schema );
239            }
240        }
241    
242    
243        /**
244         * Called to react to the deletion of a metaSchema object.  This method
245         * simply removes the schema from the loaded schema map of the global 
246         * registries.  
247         * 
248         * @param name the dn of the metaSchema object being deleted
249         * @param entry the attributes of the metaSchema object 
250         */
251        public void delete( ServerEntry entry, boolean cascade ) throws Exception
252        {
253            EntryAttribute cn = entry.get( cnAT );
254            String schemaName = cn.getString();
255    
256            // Before allowing a schema object to be deleted we must check
257            // to make sure it's not depended upon by another schema
258            Set<String> dependents = schemaManager.listDependentSchemaNames( schemaName );
259            
260            if ( ( dependents != null ) && ! dependents.isEmpty() )
261            {
262                String msg = I18n.err( I18n.ERR_381, dependents ); 
263                LOG.warn( msg );
264                throw new LdapUnwillingToPerformException(
265                    ResultCodeEnum.UNWILLING_TO_PERFORM,
266                    msg );
267            }
268            
269            // no need to check if schema is enabled or disabled here
270            // if not in the loaded set there will be no negative effect
271            schemaManager.unload( schemaName );
272        }
273    
274    
275    
276        /**
277         * Responds to the rdn (commonName) of the metaSchema object being 
278         * changed.  Changes all the schema entities associated with the 
279         * renamed schema so they now map to a new schema name.
280         * 
281         * @param name the dn of the metaSchema object renamed
282         * @param entry the entry of the metaSchema object before the rename
283         * @param newRdn the new commonName of the metaSchema object
284         */
285        public void rename( ServerEntry entry, RDN newRdn, boolean cascade ) throws Exception
286        {
287            String rdnAttribute = newRdn.getUpType();
288            String rdnAttributeOid = registries.getAttributeTypeRegistry().getOidByName( rdnAttribute );
289    
290            if ( ! rdnAttributeOid.equals( cnAT.getOid() ) )
291            {
292                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
293                    I18n.err( I18n.ERR_382, rdnAttribute ) );
294            }
295    
296            /*
297             * This operation has to do the following:
298             * 
299             * [1] check and make sure there are no dependent schemas on the 
300             *     one being renamed - if so an exception should result
301             *      
302             * [2] make non-schema object registries modify the mapping 
303             *     for their entities: non-schema object registries contain
304             *     objects that are not SchemaObjects and hence do not carry
305             *     their schema within the object as a property
306             *     
307             * [3] make schema object registries do the same but the way
308             *     they do them will be different since these objects will
309             *     need to be replaced or will require a setter for the 
310             *     schema name
311             */
312            
313            // step [1]
314            /*
315            String schemaName = getSchemaName( entry.getDn() );
316            Set<String> dependents = schemaManager.listDependentSchemaNames( schemaName );
317            if ( ! dependents.isEmpty() )
318            {
319                throw new LdapUnwillingToPerformException( 
320                    "Cannot allow a rename on " + schemaName + " schema while it has depentents.",
321                    ResultCodeEnum.UNWILLING_TO_PERFORM );
322            }
323    
324            // check if the new schema is enabled or disabled
325            boolean isEnabled = false;
326            EntryAttribute disabled = entry.get( disabledAT );
327            
328            if ( disabled == null )
329            {
330                isEnabled = true;
331            }
332            else if ( ! disabled.get().equals( "TRUE" ) )
333            {
334                isEnabled = true;
335            }
336    
337            if ( ! isEnabled )
338            {
339                return;
340            }
341    
342            // do steps 2 and 3 if the schema has been enabled and is loaded
343            
344            // step [2] 
345            String newSchemaName = ( String ) newRdn.getUpValue();
346            registries.getComparatorRegistry().renameSchema( schemaName, newSchemaName );
347            registries.getNormalizerRegistry().renameSchema( schemaName, newSchemaName );
348            registries.getSyntaxCheckerRegistry().renameSchema( schemaName, newSchemaName );
349            
350            // step [3]
351            renameSchema( registries.getAttributeTypeRegistry(), schemaName, newSchemaName );
352            renameSchema( registries.getDitContentRuleRegistry(), schemaName, newSchemaName );
353            renameSchema( registries.getDitStructureRuleRegistry(), schemaName, newSchemaName );
354            renameSchema( registries.getMatchingRuleRegistry(), schemaName, newSchemaName );
355            renameSchema( registries.getMatchingRuleUseRegistry(), schemaName, newSchemaName );
356            renameSchema( registries.getNameFormRegistry(), schemaName, newSchemaName );
357            renameSchema( registries.getObjectClassRegistry(), schemaName, newSchemaName );
358            renameSchema( registries.getLdapSyntaxRegistry(), schemaName, newSchemaName );
359            */
360        }
361        
362    
363        /**
364         * Moves are not allowed for metaSchema objects so this always throws an
365         * UNWILLING_TO_PERFORM LdapException.
366         */
367        public void moveAndRename( DN oriChildName, DN newParentName, String newRn, boolean deleteOldRn, 
368            ServerEntry entry, boolean cascade ) throws LdapUnwillingToPerformException
369        {
370            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
371                I18n.err( I18n.ERR_383 ) );
372        }
373    
374    
375        /**
376         * Moves are not allowed for metaSchema objects so this always throws an
377         * UNWILLING_TO_PERFORM LdapException.
378         */
379        public void move( DN oriChildName, DN newParentName, 
380            ServerEntry entry, boolean cascade ) throws LdapUnwillingToPerformException
381        {
382            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
383                I18n.err( I18n.ERR_383 ) );
384        }
385    
386        
387        // -----------------------------------------------------------------------
388        // private utility methods
389        // -----------------------------------------------------------------------
390    
391        
392        /**
393         * Modify the Disable flag (the flag can be set to true or false).
394         * 
395         * We can ADD, REMOVE or MODIFY this flag. The following matrix expose what will be the consequences
396         * of this operation, depending on the current state
397         * 
398         * <pre>
399         *                 +-------------------+--------------------+--------------------+
400         *     op/state    |       TRUE        |       FALSE        |       ABSENT       |
401         * +-------+-------+----------------------------------------+--------------------+
402         * | ADD   | TRUE  | do nothing        | do nothing         | disable the schema |
403         * |       +-------+-------------------+--------------------+--------------------+
404         * |       | FALSE | do nothing        | do nothing         | do nothing         |
405         * +-------+-------+-------------------+--------------------+--------------------+
406         * |REMOVE | N/A   | enable the schema | do nothing         | do nothing         |
407         * +-------+-------+-------------------+--------------------+--------------------+
408         * |MODIFY | TRUE  | do nothing        | disable the schema | disable the schema |
409         * |       +-------+-------------------+--------------------+--------------------+
410         * |       | FALSE | enable the schema | do nothing         |  do nothing        |
411         * +-------+-------+-------------------+--------------------+--------------------+
412         * </pre>
413         */
414        private boolean modifyDisable( ModifyOperationContext opContext, ModificationOperation modOp, 
415            EntryAttribute disabledInMods, EntryAttribute disabledInEntry ) throws Exception
416        {
417            DN name = opContext.getDn();
418            
419            switch ( modOp )
420            {
421                /*
422                 * If the user is adding a new m-disabled attribute to an enabled schema, 
423                 * we check that the value is "TRUE" and disable that schema if so.
424                 */
425                case ADD_ATTRIBUTE :
426                    if ( disabledInEntry == null )
427                    {
428                        if ( "TRUE".equalsIgnoreCase( disabledInMods.getString() ) )
429                        {
430                            return disableSchema( opContext.getSession(), getSchemaName( name ) );
431                        }
432                    }
433                    
434                    break;
435    
436                /*
437                 * If the user is removing the m-disabled attribute we check if the schema is currently 
438                 * disabled.  If so we enable the schema.
439                 */
440                case REMOVE_ATTRIBUTE :
441                    if ( ( disabledInEntry != null ) && ( "TRUE".equalsIgnoreCase( disabledInEntry.getString() ) ) )
442                    {
443                        return enableSchema( getSchemaName( name ) );
444                    }
445                    
446                    break;
447    
448                /*
449                 * If the user is replacing the m-disabled attribute we check if the schema is 
450                 * currently disabled and enable it if the new state has it as enabled.  If the
451                 * schema is not disabled we disable it if the mods set m-disabled to true.
452                 */
453                case REPLACE_ATTRIBUTE :
454                    
455                    boolean isCurrentlyDisabled = false;
456                    
457                    if ( disabledInEntry != null )
458                    {
459                        isCurrentlyDisabled = "TRUE".equalsIgnoreCase( disabledInEntry.getString() );
460                    }
461                    
462                    boolean isNewStateDisabled = false;
463                   
464                    if ( disabledInMods != null )
465                    {
466                        Value<?> val = disabledInMods.get();
467                        
468                        if ( val == null )
469                        {
470                            isNewStateDisabled = false;
471                        }
472                        else
473                        {
474                            isNewStateDisabled = "TRUE".equalsIgnoreCase( val.getString() );
475                        }
476                    }
477    
478                    if ( isCurrentlyDisabled && !isNewStateDisabled )
479                    {
480                        return enableSchema( getSchemaName( name ) );
481                    }
482    
483                    if ( !isCurrentlyDisabled && isNewStateDisabled )
484                    {
485                        return disableSchema( opContext.getSession(), getSchemaName( name ) );
486                    }
487                    
488                    break;
489                    
490                default:
491                    throw new IllegalArgumentException( I18n.err( I18n.ERR_384, modOp ) );
492            }
493            
494            return SCHEMA_UNCHANGED;
495        }
496    
497    
498        private String getSchemaName( DN schema )
499        {
500            return ( String ) schema.getRdn().getNormValue();
501        }
502    
503        
504        /**
505         * Build the DN to access a schemaObject path for a specific schema 
506         */
507        private DN buildDn( SchemaObjectType schemaObjectType, String schemaName ) throws LdapInvalidDnException
508        {
509            
510            DN path = new DN( 
511                SchemaConstants.OU_SCHEMA,
512                "cn=" + schemaName,
513                schemaObjectType.getRdn()
514                );
515            
516            return path;
517        }
518        
519        
520        /**
521         * Disable a schema and update all of its schemaObject 
522         */
523        private void disable( SchemaObject schemaObject, CoreSession session, Registries registries )
524            throws Exception
525        {
526            Schema schema = registries.getLoadedSchema( schemaObject.getSchemaName() );
527            List<Modification> modifications = new ArrayList<Modification>();
528            
529            // The m-disabled AT
530            EntryAttribute disabledAttr = new DefaultServerAttribute( disabledAT, "FALSE" );
531            Modification disabledMod = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, disabledAttr );
532            
533            modifications.add( disabledMod );
534            
535            // The modifiersName AT
536            EntryAttribute modifiersNameAttr = 
537                new DefaultServerAttribute( modifiersNameAT, session.getEffectivePrincipal().getName() );
538            Modification modifiersNameMod = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, modifiersNameAttr );
539            
540            modifications.add( modifiersNameMod );
541            
542            // The modifyTimestamp AT
543            EntryAttribute modifyTimestampAttr = 
544                new DefaultServerAttribute( modifyTimestampAT, DateUtils.getGeneralizedTime() );
545            Modification modifyTimestampMod = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, modifyTimestampAttr );
546            
547            modifications.add( modifyTimestampMod );
548            
549            // Call the modify operation
550            DN dn = buildDn( schemaObject.getObjectType(), schemaObject.getName() );
551            
552            ModifyOperationContext modifyContext = new ModifyOperationContext( session, dn, modifications );
553            modifyContext.setByPassed( ByPassConstants.BYPASS_ALL_COLLECTION );
554    
555            OperationManager operationManager = 
556                session.getDirectoryService().getOperationManager();
557            
558            operationManager.modify( modifyContext );
559            
560            // Now iterate on all the schemaObject under this schema
561            for ( SchemaObjectWrapper schemaObjectWrapper : schema.getContent() )
562            {
563                
564            }
565        }
566    
567        private boolean disableSchema( CoreSession session, String schemaName ) throws Exception
568        {
569            Schema schema = registries.getLoadedSchema( schemaName );
570    
571            if ( schema == null )
572            {
573                // This is not possible. We can't enable a schema which is not loaded.
574                String msg = I18n.err( I18n.ERR_85, schemaName );
575                LOG.error( msg );
576                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
577            }
578            
579            return schemaManager.disable( schemaName );
580    
581            /*
582            // First check that the schema is not already disabled
583            Map<String, Schema> schemas = registries.getLoadedSchemas();
584            
585            Schema schema = schemas.get( schemaName );
586            
587            if ( ( schema == null ) || schema.isDisabled() )
588            {
589                // The schema is disabled, do nothing
590                return SCHEMA_UNCHANGED;
591            }
592            
593            Set<String> dependents = schemaManager.listEnabledDependentSchemaNames( schemaName );
594            
595            if ( ! dependents.isEmpty() )
596            {
597                throw new LdapUnwillingToPerformException(
598                    "Cannot disable schema with enabled dependents: " + dependents,
599                    ResultCodeEnum.UNWILLING_TO_PERFORM );
600            }
601            
602            schema.disable();
603            
604            // Use brute force right now : iterate through all the schemaObjects
605            // searching for those associated with the disabled schema
606            disableAT( session, schemaName );
607            
608            Set<SchemaObjectWrapper> content = registries.getLoadedSchema( schemaName ).getContent(); 
609    
610            for ( SchemaObjectWrapper schemaWrapper : content )
611            {
612                SchemaObject schemaObject = schemaWrapper.get();
613                
614                System.out.println( "Disabling " + schemaObject.getName() );
615            }
616            
617            return SCHEMA_MODIFIED;
618            */
619        }
620        
621        
622        private void disableAT( CoreSession session, String schemaName )
623        {
624            AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
625            
626            for ( AttributeType attributeType : atRegistry )
627            {
628                if ( schemaName.equalsIgnoreCase( attributeType.getSchemaName() ) )
629                {
630                    if ( attributeType.isDisabled() )
631                    {
632                        continue;
633                    }
634                    
635                    EntryAttribute disable = new DefaultServerAttribute( disabledAT, "TRUE"  );
636                    Modification modification = 
637                        new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, disable );
638                    
639                    //session.modify( dn, mods, ignoreReferral, log )
640                }
641            }
642        }
643    
644    
645        /**
646         * Enabling a schema consist on switching all of its schema element to enable.
647         * We have to do it on a temporary registries.
648         */
649        private boolean enableSchema( String schemaName ) throws Exception
650        {
651            Schema schema = registries.getLoadedSchema( schemaName );
652    
653            if ( schema == null )
654            {
655                // We have to load the schema before enabling it.
656                schemaManager.loadDisabled( schemaName );
657            }
658            
659            return schemaManager.enable( schemaName );
660        }
661    
662    
663        /**
664         * Checks to make sure the dependencies either exist for disabled metaSchemas,
665         * or exist and are loaded (enabled) for enabled metaSchemas.
666         * 
667         * @param isEnabled whether or not the new metaSchema is enabled
668         * @param entry the Attributes for the new metaSchema object
669         * @throws NamingException if the dependencies do not resolve or are not
670         * loaded (enabled)
671         */
672        private void checkForDependencies( boolean isEnabled, ServerEntry entry ) throws Exception
673        {
674            EntryAttribute dependencies = entry.get( this.dependenciesAT );
675    
676            if ( dependencies == null )
677            {
678                return;
679            }
680            
681            if ( isEnabled )
682            {
683                // check to make sure all the dependencies are also enabled
684                Map<String,Schema> loaded = registries.getLoadedSchemas();
685                
686                for ( Value<?> value:dependencies )
687                {
688                    String dependency = value.getString();
689                    
690                    if ( ! loaded.containsKey( dependency ) )
691                    {
692                        throw new LdapUnwillingToPerformException( 
693                            ResultCodeEnum.UNWILLING_TO_PERFORM, "Unwilling to perform operation on enabled schema with disabled or missing dependencies: " 
694                            + dependency );
695                    }
696                }
697            }
698            else
699            {
700                for ( Value<?> value:dependencies )
701                {
702                    String dependency = value.getString();
703                    
704                    if ( schemaManager.getLoadedSchema( StringTools.toLowerCase( dependency ) ) == null )
705                    {
706                        throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, 
707                            I18n.err( I18n.ERR_385, dependency ) );
708                    }
709                }
710            }
711        }
712    
713        
714        /**
715         * Used to iterate through SchemaObjects in a DefaultSchemaObjectRegistry and rename
716         * their schema property to a new schema name.
717         * 
718         * @param registry the registry whose objects are changed
719         * @param originalSchemaName the original schema name
720         * @param newSchemaName the new schema name
721         */
722        private void renameSchema( DefaultSchemaObjectRegistry<? extends SchemaObject> registry, String originalSchemaName, String newSchemaName ) 
723        {
724            Iterator<? extends SchemaObject> list = registry.iterator();
725            while ( list.hasNext() )
726            {
727                SchemaObject obj = list.next();
728                if ( obj.getSchemaName().equalsIgnoreCase( originalSchemaName ) )
729                {
730                    obj.setSchemaName( newSchemaName );
731                }
732            }
733        }
734    }