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.shared.ldap.schemamanager.impl;
021
022
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031import org.apache.directory.shared.i18n.I18n;
032import org.apache.directory.shared.ldap.model.constants.MetaSchemaConstants;
033import org.apache.directory.shared.ldap.model.constants.SchemaConstants;
034import org.apache.directory.shared.ldap.model.entry.Entry;
035import org.apache.directory.shared.ldap.model.exception.LdapException;
036import org.apache.directory.shared.ldap.model.exception.LdapInvalidDnException;
037import org.apache.directory.shared.ldap.model.exception.LdapOtherException;
038import org.apache.directory.shared.ldap.model.exception.LdapProtocolErrorException;
039import org.apache.directory.shared.ldap.model.exception.LdapSchemaException;
040import org.apache.directory.shared.ldap.model.exception.LdapSchemaExceptionCodes;
041import org.apache.directory.shared.ldap.model.exception.LdapUnwillingToPerformException;
042import org.apache.directory.shared.ldap.model.message.ResultCodeEnum;
043import org.apache.directory.shared.ldap.model.name.Dn;
044import org.apache.directory.shared.ldap.model.schema.AttributeType;
045import org.apache.directory.shared.ldap.model.schema.EntityFactory;
046import org.apache.directory.shared.ldap.model.schema.LdapComparator;
047import org.apache.directory.shared.ldap.model.schema.LdapSyntax;
048import org.apache.directory.shared.ldap.model.schema.LoadableSchemaObject;
049import org.apache.directory.shared.ldap.model.schema.MatchingRule;
050import org.apache.directory.shared.ldap.model.schema.Normalizer;
051import org.apache.directory.shared.ldap.model.schema.ObjectClass;
052import org.apache.directory.shared.ldap.model.schema.SchemaManager;
053import org.apache.directory.shared.ldap.model.schema.SchemaObject;
054import org.apache.directory.shared.ldap.model.schema.SchemaObjectWrapper;
055import org.apache.directory.shared.ldap.model.schema.SyntaxChecker;
056import org.apache.directory.shared.ldap.model.schema.normalizers.OidNormalizer;
057import org.apache.directory.shared.ldap.model.schema.registries.AttributeTypeRegistry;
058import org.apache.directory.shared.ldap.model.schema.registries.ComparatorRegistry;
059import org.apache.directory.shared.ldap.model.schema.registries.DITContentRuleRegistry;
060import org.apache.directory.shared.ldap.model.schema.registries.DITStructureRuleRegistry;
061import org.apache.directory.shared.ldap.model.schema.registries.ImmutableAttributeTypeRegistry;
062import org.apache.directory.shared.ldap.model.schema.registries.ImmutableComparatorRegistry;
063import org.apache.directory.shared.ldap.model.schema.registries.ImmutableDITContentRuleRegistry;
064import org.apache.directory.shared.ldap.model.schema.registries.ImmutableDITStructureRuleRegistry;
065import org.apache.directory.shared.ldap.model.schema.registries.ImmutableLdapSyntaxRegistry;
066import org.apache.directory.shared.ldap.model.schema.registries.ImmutableMatchingRuleRegistry;
067import org.apache.directory.shared.ldap.model.schema.registries.ImmutableMatchingRuleUseRegistry;
068import org.apache.directory.shared.ldap.model.schema.registries.ImmutableNameFormRegistry;
069import org.apache.directory.shared.ldap.model.schema.registries.ImmutableNormalizerRegistry;
070import org.apache.directory.shared.ldap.model.schema.registries.ImmutableObjectClassRegistry;
071import org.apache.directory.shared.ldap.model.schema.registries.ImmutableSyntaxCheckerRegistry;
072import org.apache.directory.shared.ldap.model.schema.registries.LdapSyntaxRegistry;
073import org.apache.directory.shared.ldap.model.schema.registries.MatchingRuleRegistry;
074import org.apache.directory.shared.ldap.model.schema.registries.MatchingRuleUseRegistry;
075import org.apache.directory.shared.ldap.model.schema.registries.NameFormRegistry;
076import org.apache.directory.shared.ldap.model.schema.registries.NormalizerRegistry;
077import org.apache.directory.shared.ldap.model.schema.registries.ObjectClassRegistry;
078import org.apache.directory.shared.ldap.model.schema.registries.OidRegistry;
079import org.apache.directory.shared.ldap.model.schema.registries.Registries;
080import org.apache.directory.shared.ldap.model.schema.registries.Schema;
081import org.apache.directory.shared.ldap.model.schema.registries.SchemaLoader;
082import org.apache.directory.shared.ldap.model.schema.registries.SyntaxCheckerRegistry;
083import org.apache.directory.shared.ldap.schemaloader.JarLdifSchemaLoader;
084import org.apache.directory.shared.ldap.schemaloader.SchemaEntityFactory;
085import org.apache.directory.shared.util.Strings;
086import org.apache.directory.shared.util.exception.NotImplementedException;
087import org.slf4j.Logger;
088import org.slf4j.LoggerFactory;
089
090
091/**
092 * The SchemaManager class : it handles all the schema operations (addition, removal,
093 * modification).
094 *
095 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
096 */
097public class DefaultSchemaManager implements SchemaManager
098{
099    /** static class logger */
100    private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaManager.class );
101
102    /** The NamingContext this SchemaManager is associated with */
103    private Dn namingContext;
104
105    /** The global registries for this namingContext */
106    private volatile Registries registries;
107
108    /** The list of errors produced when loading some schema elements */
109    private List<Throwable> errors;
110
111    /** The Schema schemaLoader used by this SchemaManager */
112    private SchemaLoader schemaLoader;
113
114    /** the factory that generates respective SchemaObjects from LDIF entries */
115    private final EntityFactory factory;
116
117    /** the normalized name for the schema modification attributes */
118    private Dn schemaModificationAttributesDn;
119
120    /** A Map containing all the schema being dependent from a schema */
121    private Map<String, Set<String>> schemaDependences = new HashMap<String, Set<String>>();
122
123    /** A flag indicating that the SchemaManager is relaxed or not */
124    private boolean isRelaxed = STRICT;
125
126    /** Two flags for RELAXED and STRICT, this is STRICT */
127    public static final boolean STRICT = false;
128
129    /** Two flags for RELAXED and STRICT, this is RELAXED */
130    public static final boolean RELAXED = true;
131
132
133    /**
134     * Creates a new instance of DefaultSchemaManager with the default schema schemaLoader
135     *
136     * @param loader The schema loader to use
137     */
138    public DefaultSchemaManager() throws Exception
139    {
140        // Default to the the root (one schemaManager for all the entries
141        namingContext = Dn.ROOT_DSE;
142        this.schemaLoader = new JarLdifSchemaLoader();
143        errors = new ArrayList<Throwable>();
144        registries = new Registries( this );
145        factory = new SchemaEntityFactory();
146        isRelaxed = STRICT;
147        loadAllEnabled();
148    }
149
150
151    /**
152     * Creates a new instance of DefaultSchemaManager with the default schema schemaLoader
153     *
154     * @param loader The schema loader to use
155     */
156    public DefaultSchemaManager( SchemaLoader loader )
157    {
158        // Default to the the root (one schemaManager for all the entries
159        namingContext = Dn.ROOT_DSE;
160        this.schemaLoader = loader;
161        errors = new ArrayList<Throwable>();
162        registries = new Registries( this );
163        factory = new SchemaEntityFactory();
164        isRelaxed = STRICT;
165    }
166
167
168    /**
169     * Creates a new instance of DefaultSchemaManager, for a specific
170     * naming context
171     *
172     * @param loader The schema loader to use
173     * @param namingContext The associated NamingContext
174     */
175    public DefaultSchemaManager( SchemaLoader loader, Dn namingContext )
176    {
177        this.namingContext = namingContext;
178        this.schemaLoader = loader;
179        errors = new ArrayList<Throwable>();
180        registries = new Registries( this );
181        factory = new SchemaEntityFactory();
182        isRelaxed = STRICT;
183    }
184
185
186    //-----------------------------------------------------------------------
187    // Helper methods
188    //-----------------------------------------------------------------------
189    /**
190     * Clone the registries before doing any modification on it. Relax it
191     * too so that we can update it.
192     */
193    private Registries cloneRegistries() throws LdapException
194    {
195        try
196        {
197            // Relax the controls at first
198            errors = new ArrayList<Throwable>();
199
200            // Clone the Registries
201            Registries clonedRegistries = registries.clone();
202
203            // And update references. We may have errors, that may be fixed
204            // by the new loaded schemas.
205            errors = clonedRegistries.checkRefInteg();
206
207            // Now, relax the cloned Registries if there is no error
208            clonedRegistries.setRelaxed();
209
210            return clonedRegistries;
211        }
212        catch ( CloneNotSupportedException cnse )
213        {
214            throw new LdapOtherException( cnse.getMessage() );
215        }
216    }
217
218
219    /**
220     * Transform a String[] array of schema to a Schema[]
221     */
222    private Schema[] toArray( String... schemas ) throws LdapException
223    {
224        Schema[] schemaArray = new Schema[schemas.length];
225        int n = 0;
226
227        for ( String schemaName : schemas )
228        {
229            Schema schema = schemaLoader.getSchema( schemaName );
230
231            if ( schema != null )
232            {
233                schemaArray[n++] = schema;
234            }
235            else
236            {
237                throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err(
238                    I18n.ERR_11001, schemaName ) );
239            }
240        }
241
242        return schemaArray;
243    }
244
245
246    private void addSchemaObjects( Schema schema, Registries registries ) throws LdapException
247    {
248        // Create a content container for this schema
249        registries.addSchema( schema.getSchemaName() );
250
251        // And inject any existing SchemaObject into the registries
252        try
253        {
254            addComparators( schema, registries );
255            addNormalizers( schema, registries );
256            addSyntaxCheckers( schema, registries );
257            addSyntaxes( schema, registries );
258            addMatchingRules( schema, registries );
259            addAttributeTypes( schema, registries );
260            addObjectClasses( schema, registries );
261            addMatchingRuleUses( schema, registries );
262            addDitContentRules( schema, registries );
263            addNameForms( schema, registries );
264            addDitStructureRules( schema, registries );
265        }
266        catch ( IOException ioe )
267        {
268            throw new LdapOtherException( ioe.getMessage() );
269        }
270
271        // TODO Add some listener handling at this point
272        //notifyListenerOrRegistries( schema, registries );
273    }
274
275
276    /**
277     * Delete all the schemaObjects for a given schema from the registries
278     */
279    private void deleteSchemaObjects( Schema schema, Registries registries ) throws LdapException
280    {
281        Map<String, Set<SchemaObjectWrapper>> schemaObjects = registries.getObjectBySchemaName();
282        Set<SchemaObjectWrapper> content = schemaObjects.get( Strings.toLowerCase( schema.getSchemaName() ) );
283
284        List<SchemaObject> toBeDeleted = new ArrayList<SchemaObject>();
285
286        // Buid an intermediate list to avoid concurrent modifications
287        for ( SchemaObjectWrapper schemaObjectWrapper : content )
288        {
289            toBeDeleted.add( schemaObjectWrapper.get() );
290        }
291
292        for ( SchemaObject schemaObject : toBeDeleted )
293        {
294            registries.delete( errors, schemaObject );
295        }
296    }
297
298
299    //-----------------------------------------------------------------------
300    // API methods
301    //-----------------------------------------------------------------------
302    /**
303     * {@inheritDoc}
304     */
305    public boolean disable( Schema... schemas ) throws LdapException
306    {
307        boolean disabled = false;
308
309        // Reset the errors if not null
310        if ( errors != null )
311        {
312            errors.clear();
313        }
314
315        // Work on a cloned and relaxed registries
316        Registries clonedRegistries = cloneRegistries();
317        clonedRegistries.setRelaxed();
318
319        for ( Schema schema : schemas )
320        {
321            unload( clonedRegistries, schema );
322        }
323
324        // Build the cross references
325        errors = clonedRegistries.buildReferences();
326
327        // Destroy the clonedRegistry
328        clonedRegistries.clear();
329
330        if ( errors.isEmpty() )
331        {
332            // Ok no errors. Check the registries now
333            errors = clonedRegistries.checkRefInteg();
334
335            if ( errors.isEmpty() )
336            {
337                // We are golden : let's apply the schemas in the real registries
338                for ( Schema schema : schemas )
339                {
340                    unload( registries, schema );
341                    schema.disable();
342                }
343
344                // Build the cross references
345                errors = registries.buildReferences();
346                registries.setStrict();
347
348                disabled = true;
349            }
350        }
351
352        // clear the cloned registries
353        clonedRegistries.clear();
354
355        return disabled;
356    }
357
358
359    /**
360     * {@inheritDoc}
361     */
362    public boolean disable( String... schemaNames ) throws LdapException
363    {
364        Schema[] schemas = toArray( schemaNames );
365
366        return disable( schemas );
367    }
368
369
370    /**
371     * {@inheritDoc}
372     */
373    public boolean disabledRelaxed( Schema... schemas )
374    {
375        // TODO Auto-generated method stub
376        return false;
377    }
378
379
380    /**
381     * {@inheritDoc}
382     */
383    public boolean disabledRelaxed( String... schemas )
384    {
385        // TODO Auto-generated method stub
386        return false;
387    }
388
389
390    /**
391     * {@inheritDoc}
392     */
393    public List<Schema> getDisabled()
394    {
395        List<Schema> disabled = new ArrayList<Schema>();
396
397        for ( Schema schema : registries.getLoadedSchemas().values() )
398        {
399            if ( schema.isDisabled() )
400            {
401                disabled.add( schema );
402            }
403        }
404
405        return disabled;
406    }
407
408
409    /**
410     * {@inheritDoc}
411     */
412    public boolean enable( Schema... schemas ) throws LdapException
413    {
414        boolean enabled = false;
415
416        // Reset the errors if not null
417        if ( errors != null )
418        {
419            errors.clear();
420        }
421
422        // Work on a cloned and relaxed registries
423        Registries clonedRegistries = cloneRegistries();
424        clonedRegistries.setRelaxed();
425
426        Set<Schema> disabledSchemas = new HashSet<Schema>();
427
428        for ( Schema schema : schemas )
429        {
430            if ( schema.getDependencies() != null )
431            {
432                for ( String dependency : schema.getDependencies() )
433                {
434                    Schema dependencySchema = schemaLoader.getSchema( dependency );
435
436                    if ( dependencySchema.isDisabled() )
437                    {
438                        disabledSchemas.add( dependencySchema );
439                    }
440                }
441            }
442
443            schema.enable();
444            load( clonedRegistries, schema );
445        }
446
447        // Revert back the disabled schema to disabled
448        for ( Schema disabledSchema : disabledSchemas )
449        {
450            if ( disabledSchema.isEnabled() )
451            {
452                disabledSchema.disable();
453            }
454        }
455
456        // Build the cross references
457        errors = clonedRegistries.buildReferences();
458
459        // Destroy the clonedRegistry
460        clonedRegistries.clear();
461
462        if ( errors.isEmpty() )
463        {
464            // Ok no errors. Check the registries now
465            errors = clonedRegistries.checkRefInteg();
466
467            if ( errors.isEmpty() )
468            {
469                // We are golden : let's apply the schemas in the real registries
470                for ( Schema schema : schemas )
471                {
472                    schema.enable();
473                    load( registries, schema );
474                }
475
476                // Build the cross references
477                errors = registries.buildReferences();
478                registries.setStrict();
479
480                enabled = true;
481            }
482        }
483
484        // clear the cloned registries
485        clonedRegistries.clear();
486
487        return enabled;
488    }
489
490
491    /**
492     * {@inheritDoc}
493     */
494    public boolean enable( String... schemaNames ) throws LdapException
495    {
496        Schema[] schemas = toArray( schemaNames );
497        return enable( schemas );
498    }
499
500
501    /**
502     * {@inheritDoc}
503     */
504    public boolean enableRelaxed( Schema... schemas )
505    {
506        // TODO Auto-generated method stub
507        return false;
508    }
509
510
511    /**
512     * {@inheritDoc}
513     */
514    public boolean enableRelaxed( String... schemas )
515    {
516        // TODO Auto-generated method stub
517        return false;
518    }
519
520
521    /**
522     * {@inheritDoc}
523     */
524    public List<Schema> getEnabled()
525    {
526        List<Schema> enabled = new ArrayList<Schema>();
527
528        for ( Schema schema : registries.getLoadedSchemas().values() )
529        {
530            if ( schema.isEnabled() )
531            {
532                enabled.add( schema );
533            }
534        }
535
536        return enabled;
537    }
538
539
540    /**
541     * {@inheritDoc}
542     */
543    public List<Throwable> getErrors()
544    {
545        return errors;
546    }
547
548
549    /**
550     * {@inheritDoc}
551     */
552    public Registries getRegistries()
553    {
554        return registries;
555    }
556
557
558    /**
559     * {@inheritDoc}
560     */
561    public boolean isDisabledAccepted()
562    {
563        // TODO Auto-generated method stub
564        return false;
565    }
566
567
568    /**
569     * {@inheritDoc}
570     */
571    public boolean load( Schema... schemas ) throws LdapException
572    {
573        if ( schemas.length == 0 )
574        {
575            return true;
576        }
577
578        boolean loaded = false;
579
580        // Reset the errors if not null
581        if ( errors != null )
582        {
583            errors.clear();
584        }
585
586        // Work on a cloned and relaxed registries
587        Registries clonedRegistries = cloneRegistries();
588        clonedRegistries.setRelaxed();
589
590        // Load the schemas
591        for ( Schema schema : schemas )
592        {
593            boolean singleSchemaLoaded = load( clonedRegistries, schema );
594
595            // return false if the schema was not loaded in the first place
596            if ( !singleSchemaLoaded )
597            {
598                return false;
599            }
600        }
601
602        // Build the cross references
603        errors = clonedRegistries.buildReferences();
604
605        if ( errors.isEmpty() )
606        {
607            // Ok no errors. Check the registries now
608            errors = clonedRegistries.checkRefInteg();
609
610            if ( errors.isEmpty() )
611            {
612                // We are golden : let's apply the schema in the real registries
613                registries.setRelaxed();
614
615                // Load the schemas
616                for ( Schema schema : schemas )
617                {
618                    load( registries, schema );
619
620                    // Update the schema dependences if needed
621
622                    if ( schema.getDependencies() != null )
623                    {
624                        for ( String dep : schema.getDependencies() )
625                        {
626                            Set<String> deps = schemaDependences.get( dep );
627
628                            if ( deps == null )
629                            {
630                                deps = new HashSet<String>();
631                                deps.add( schema.getSchemaName() );
632                            }
633
634                            // Replace the dependences
635                            schemaDependences.put( dep, deps );
636                        }
637                    }
638
639                    // add the schema to the schemaLoader
640                    schemaLoader.addSchema( schema );
641                }
642
643                // Build the cross references
644                errors = registries.buildReferences();
645                registries.setStrict();
646
647                loaded = true;
648            }
649        }
650
651        // clear the cloned registries
652        clonedRegistries.clear();
653
654        return loaded;
655    }
656
657
658    /**
659     * {@inheritDoc}
660     */
661    public boolean load( String... schemaNames ) throws Exception
662    {
663        if ( schemaNames.length == 0 )
664        {
665            return true;
666        }
667
668        Schema[] schemas = toArray( schemaNames );
669
670        return load( schemas );
671    }
672
673
674    /**
675     * Load the schema in the registries. We will load everything accordingly to the two flags :
676     * - isRelaxed
677     * - disabledAccepted
678     */
679    private boolean load( Registries registries, Schema schema ) throws LdapException
680    {
681        if ( schema == null )
682        {
683            LOG.info( "The schema is null" );
684            return false;
685        }
686
687        // First avoid loading twice the same schema
688        if ( registries.isSchemaLoaded( schema.getSchemaName() ) )
689        {
690            return true;
691        }
692
693        if ( schema.isDisabled() )
694        {
695            if ( registries.isDisabledAccepted() )
696            {
697                LOG.info( "Loading {} disabled schema: \n{}", schema.getSchemaName(), schema );
698
699                registries.schemaLoaded( schema );
700                addSchemaObjects( schema, registries );
701            }
702            else
703            {
704                return false;
705            }
706        }
707        else
708        {
709            LOG.info( "Loading {} enabled schema: \n{}", schema.getSchemaName(), schema );
710
711            // Check that the dependencies, if any, are correct
712            if ( schema.getDependencies() != null )
713            {
714                for ( String dependency : schema.getDependencies() )
715                {
716                    Schema dependencySchema = schemaLoader.getSchema( dependency );
717
718                    if ( dependencySchema == null )
719                    {
720                        // The dependency has not been loaded.
721                        String msg = I18n.err( I18n.ERR_11002, schema.getSchemaName() );
722                        LOG.info( msg );
723                        Throwable error = new LdapProtocolErrorException( msg );
724                        errors.add( error );
725                        return false;
726                    }
727
728                    // If the dependency is disabled, then enable it
729                    if ( dependencySchema.isDisabled() )
730                    {
731                        dependencySchema.enable();
732
733                        if ( load( registries, dependencySchema ) == false )
734                        {
735                            dependencySchema.disable();
736
737                            return false;
738                        }
739                    }
740                }
741            }
742
743            registries.schemaLoaded( schema );
744            addSchemaObjects( schema, registries );
745        }
746
747        return true;
748    }
749
750
751    /**
752     * Unload the schema from the registries. We will unload everything accordingly to the two flags :
753     * - isRelaxed
754     * - disabledAccepted
755     */
756    // False positive
757    @SuppressWarnings("PMD.UnusedPrivateMethod")
758    private boolean unload( Registries registries, Schema schema ) throws LdapException
759    {
760        if ( schema == null )
761        {
762            LOG.info( "The schema is null" );
763            return false;
764        }
765
766        // First avoid unloading twice the same schema
767        if ( !registries.isSchemaLoaded( schema.getSchemaName() ) )
768        {
769            return true;
770        }
771
772        if ( schema.isEnabled() )
773        {
774            LOG.info( "Unloading {} schema: \n{}", schema.getSchemaName(), schema );
775
776            deleteSchemaObjects( schema, registries );
777            registries.schemaUnloaded( schema );
778        }
779
780        return true;
781    }
782
783
784    /**
785     * Add all the Schema's AttributeTypes
786     */
787    private void addAttributeTypes( Schema schema, Registries registries ) throws LdapException, IOException
788    {
789        for ( Entry entry : schemaLoader.loadAttributeTypes( schema ) )
790        {
791            AttributeType attributeType = factory.getAttributeType( this, entry, registries, schema.getSchemaName() );
792
793            addSchemaObject( registries, attributeType, schema );
794        }
795    }
796
797
798    /**
799     * Add all the Schema's comparators
800     */
801    private void addComparators( Schema schema, Registries registries ) throws LdapException, IOException
802    {
803        for ( Entry entry : schemaLoader.loadComparators( schema ) )
804        {
805            LdapComparator<?> comparator = factory.getLdapComparator( this, entry, registries, schema.getSchemaName() );
806
807            addSchemaObject( registries, comparator, schema );
808        }
809    }
810
811
812    /**
813     * Add all the Schema's DitContentRules
814     */
815    // Not yet implemented, but may be used
816    @SuppressWarnings("PMD.UnusedFormalParameter")
817    private void addDitContentRules( Schema schema, Registries registries ) throws LdapException, IOException
818    {
819        if ( !schemaLoader.loadDitContentRules( schema ).isEmpty() )
820        {
821            throw new NotImplementedException( I18n.err( I18n.ERR_11003 ) );
822        }
823    }
824
825
826    /**
827     * Add all the Schema's DitStructureRules
828     */
829    // Not yet implemented, but may be used
830    @SuppressWarnings("PMD.UnusedFormalParameter")
831    private void addDitStructureRules( Schema schema, Registries registries ) throws LdapException, IOException
832    {
833        if ( !schemaLoader.loadDitStructureRules( schema ).isEmpty() )
834        {
835            throw new NotImplementedException( I18n.err( I18n.ERR_11004 ) );
836        }
837    }
838
839
840    /**
841     * Add all the Schema's MatchingRules
842     */
843    private void addMatchingRules( Schema schema, Registries registries ) throws LdapException, IOException
844    {
845        for ( Entry entry : schemaLoader.loadMatchingRules( schema ) )
846        {
847            MatchingRule matchingRule = factory.getMatchingRule( this, entry, registries, schema.getSchemaName() );
848
849            addSchemaObject( registries, matchingRule, schema );
850        }
851    }
852
853
854    /**
855     * Add all the Schema's MatchingRuleUses
856     */
857    // Not yet implemented, but may be used
858    @SuppressWarnings("PMD.UnusedFormalParameter")
859    private void addMatchingRuleUses( Schema schema, Registries registries ) throws LdapException, IOException
860    {
861        if ( !schemaLoader.loadMatchingRuleUses( schema ).isEmpty() )
862        {
863            throw new NotImplementedException( I18n.err( I18n.ERR_11005 ) );
864        }
865        // for ( Entry entry : schemaLoader.loadMatchingRuleUses( schema ) )
866        // {
867        //     throw new NotImplementedException( I18n.err( I18n.ERR_11005 ) );
868        // }
869    }
870
871
872    /**
873     * Add all the Schema's NameForms
874     */
875    // Not yet implemented, but may be used
876    @SuppressWarnings("PMD.UnusedFormalParameter")
877    private void addNameForms( Schema schema, Registries registries ) throws LdapException, IOException
878    {
879        if ( !schemaLoader.loadNameForms( schema ).isEmpty() )
880        {
881            throw new NotImplementedException( I18n.err( I18n.ERR_11006 ) );
882        }
883    }
884
885
886    /**
887     * Add all the Schema's Normalizers
888     */
889    private void addNormalizers( Schema schema, Registries registries ) throws LdapException, IOException
890    {
891        for ( Entry entry : schemaLoader.loadNormalizers( schema ) )
892        {
893            Normalizer normalizer = factory.getNormalizer( this, entry, registries, schema.getSchemaName() );
894
895            addSchemaObject( registries, normalizer, schema );
896        }
897    }
898
899
900    /**
901     * Add all the Schema's ObjectClasses
902     */
903    private void addObjectClasses( Schema schema, Registries registries ) throws LdapException, IOException
904    {
905        for ( Entry entry : schemaLoader.loadObjectClasses( schema ) )
906        {
907            ObjectClass objectClass = factory.getObjectClass( this, entry, registries, schema.getSchemaName() );
908
909            addSchemaObject( registries, objectClass, schema );
910        }
911    }
912
913
914    /**
915     * Add all the Schema's Syntaxes
916     */
917    private void addSyntaxes( Schema schema, Registries registries ) throws LdapException, IOException
918    {
919        for ( Entry entry : schemaLoader.loadSyntaxes( schema ) )
920        {
921            LdapSyntax syntax = factory.getSyntax( this, entry, registries, schema.getSchemaName() );
922
923            addSchemaObject( registries, syntax, schema );
924        }
925    }
926
927
928    /**Add
929     * Register all the Schema's SyntaxCheckers
930     */
931    private void addSyntaxCheckers( Schema schema, Registries registries ) throws LdapException, IOException
932    {
933        for ( Entry entry : schemaLoader.loadSyntaxCheckers( schema ) )
934        {
935            SyntaxChecker syntaxChecker = factory.getSyntaxChecker( this, entry, registries, schema.getSchemaName() );
936
937            addSchemaObject( registries, syntaxChecker, schema );
938        }
939    }
940
941
942    /**
943     * Add the schemaObject into the registries.
944     *
945     * @param registries The Registries
946     * @param schemaObject The SchemaObject containing the SchemaObject description
947     * @param schema The associated schema
948     * @return the created schemaObject instance
949     * @throws LdapException If the registering failed
950     */
951    private SchemaObject addSchemaObject( Registries registries, SchemaObject schemaObject, Schema schema )
952        throws LdapException
953    {
954        if ( registries.isRelaxed() )
955        {
956            if ( registries.isDisabledAccepted() || ( schema.isEnabled() && schemaObject.isEnabled() ) )
957            {
958                registries.add( errors, schemaObject );
959            }
960            else
961            {
962                errors.add( new Throwable() );
963            }
964        }
965        else
966        {
967            if ( schema.isEnabled() && schemaObject.isEnabled() )
968            {
969                registries.add( errors, schemaObject );
970            }
971            else
972            {
973                errors.add( new Throwable() );
974            }
975        }
976
977        return schemaObject;
978    }
979
980
981    /**
982     * {@inheritDoc}
983     */
984    public boolean loadAllEnabled() throws Exception
985    {
986        Schema[] schemas = schemaLoader.getAllEnabled().toArray( new Schema[0] );
987
988        return loadWithDeps( schemas );
989    }
990
991
992    /**
993     * {@inheritDoc}
994     */
995    public boolean loadAllEnabledRelaxed() throws Exception
996    {
997        Schema[] schemas = schemaLoader.getAllEnabled().toArray( new Schema[0] );
998
999        return loadWithDepsRelaxed( schemas );
1000    }
1001
1002
1003    /**
1004     * {@inheritDoc}
1005     */
1006    public boolean loadDisabled( Schema... schemas ) throws LdapException
1007    {
1008        // Work on a cloned and relaxed registries
1009        Registries clonedRegistries = cloneRegistries();
1010
1011        // Accept the disabled schemas
1012        clonedRegistries.setDisabledAccepted( true );
1013
1014        // Load the schemas
1015        for ( Schema schema : schemas )
1016        {
1017            // Enable the Schema object before loading it
1018            schema.enable();
1019            load( clonedRegistries, schema );
1020        }
1021
1022        clonedRegistries.clear();
1023
1024        // Apply the change to the correct registries if no errors
1025        if ( errors.size() == 0 )
1026        {
1027            // No error, we can enable the schema in the real registries
1028            for ( Schema schema : schemas )
1029            {
1030                load( registries, schema );
1031            }
1032
1033            return true;
1034        }
1035        else
1036        {
1037            for ( Schema schema : schemas )
1038            {
1039                schema.disable();
1040            }
1041
1042            return false;
1043        }
1044    }
1045
1046
1047    /**
1048     * {@inheritDoc}
1049     */
1050    public boolean loadDisabled( String... schemaNames ) throws LdapException
1051    {
1052        Schema[] schemas = toArray( schemaNames );
1053
1054        return loadDisabled( schemas );
1055    }
1056
1057
1058    /**
1059     * {@inheritDoc}
1060     */
1061    public boolean loadRelaxed( Schema... schemas ) throws Exception
1062    {
1063        // TODO Auto-generated method stub
1064        return false;
1065    }
1066
1067
1068    /**
1069     * {@inheritDoc}
1070     */
1071    public boolean loadRelaxed( String... schemaNames ) throws Exception
1072    {
1073        Schema[] schemas = toArray( schemaNames );
1074        return loadRelaxed( schemas );
1075    }
1076
1077
1078    /**
1079     * {@inheritDoc}
1080     */
1081    public boolean loadWithDeps( Schema... schemas ) throws Exception
1082    {
1083        boolean loaded = false;
1084
1085        // Reset the errors if not null
1086        if ( errors != null )
1087        {
1088            errors.clear();
1089        }
1090
1091        // Work on a cloned and relaxed registries
1092        Registries clonedRegistries = cloneRegistries();
1093        clonedRegistries.setRelaxed();
1094
1095        // Load the schemas
1096        for ( Schema schema : schemas )
1097        {
1098            loadDepsFirst( clonedRegistries, schema );
1099        }
1100
1101        // Build the cross references
1102        errors = clonedRegistries.buildReferences();
1103
1104        if ( errors.isEmpty() )
1105        {
1106            // Ok no errors. Check the registries now
1107            errors = clonedRegistries.checkRefInteg();
1108
1109            if ( errors.isEmpty() )
1110            {
1111                // We are golden : let's apply the schema in the real registries
1112                registries = clonedRegistries;
1113                registries.setStrict();
1114                loaded = true;
1115            }
1116        }
1117        else
1118        {
1119            // clear the cloned registries
1120            clonedRegistries.clear();
1121        }
1122
1123        return loaded;
1124    }
1125
1126
1127    /**
1128     * {@inheritDoc}
1129     */
1130    public boolean loadWithDeps( String... schemas ) throws Exception
1131    {
1132        return loadWithDeps( toArray( schemas ) );
1133    }
1134
1135
1136    /**
1137     * Recursive method which loads schema's with their dependent schemas first
1138     * and tracks what schemas it has seen so the recursion does not go out of
1139     * control with dependency cycle detection.
1140     *
1141     * @param registries The Registries in which the schemas will be loaded
1142     * @param schema the current schema we are attempting to load
1143     * @throws Exception if there is a cycle detected and/or another
1144     * failure results while loading, producing and or registering schema objects
1145     */
1146    private void loadDepsFirst( Registries registries, Schema schema ) throws Exception
1147    {
1148        if ( schema == null )
1149        {
1150            LOG.info( "The schema is null" );
1151            return;
1152        }
1153
1154        if ( schema.isDisabled() && !registries.isDisabledAccepted() )
1155        {
1156            LOG.info( "The schema is disabled and the registries does not accepted disabled schema" );
1157            return;
1158        }
1159
1160        String schemaName = schema.getSchemaName();
1161
1162        if ( registries.isSchemaLoaded( schemaName ) )
1163        {
1164            LOG.info( "{} schema has already been loaded" + schema.getSchemaName() );
1165            return;
1166        }
1167
1168        String[] deps = schema.getDependencies();
1169
1170        // if no deps then load this guy and return
1171        if ( ( deps == null ) || ( deps.length == 0 ) )
1172        {
1173            load( registries, schema );
1174
1175            return;
1176        }
1177
1178        /*
1179         * We got deps and need to load them before this schema.  We go through
1180         * all deps loading them with their deps first if they have not been
1181         * loaded.
1182         */
1183        for ( String depName : deps )
1184        {
1185            if ( registries.isSchemaLoaded( schemaName ) )
1186            {
1187                // The schema is already loaded. Loop on the next schema
1188                continue;
1189            }
1190            else
1191            {
1192                // Call recursively this method
1193                Schema schemaDep = schemaLoader.getSchema( depName );
1194                loadDepsFirst( registries, schemaDep );
1195            }
1196        }
1197
1198        // Now load the current schema
1199        load( registries, schema );
1200    }
1201
1202
1203    /**
1204     * {@inheritDoc}
1205     */
1206    public boolean loadWithDepsRelaxed( Schema... schemas ) throws Exception
1207    {
1208        registries.setRelaxed();
1209
1210        // Load the schemas
1211        for ( Schema schema : schemas )
1212        {
1213            loadDepsFirstRelaxed( schema );
1214        }
1215
1216        // Build the cross references
1217        errors = registries.buildReferences();
1218
1219        // Check the registries now
1220        errors = registries.checkRefInteg();
1221
1222        return true;
1223    }
1224
1225
1226    /**
1227     * {@inheritDoc}
1228     */
1229    public boolean loadWithDepsRelaxed( String... schemas ) throws Exception
1230    {
1231        return loadWithDepsRelaxed( toArray( schemas ) );
1232    }
1233
1234
1235    /**
1236     * Recursive method which loads schema's with their dependent schemas first
1237     * and tracks what schemas it has seen so the recursion does not go out of
1238     * control with dependency cycle detection.
1239     *
1240     * @param schema the current schema we are attempting to load
1241     * @throws Exception if there is a cycle detected and/or another
1242     * failure results while loading, producing and or registering schema objects
1243     */
1244    private void loadDepsFirstRelaxed( Schema schema ) throws Exception
1245    {
1246        if ( schema == null )
1247        {
1248            LOG.info( "The schema is null" );
1249            return;
1250        }
1251
1252        if ( schema.isDisabled() && !registries.isDisabledAccepted() )
1253        {
1254            LOG.info( "The schema is disabled and the registries does not accepted disabled schema" );
1255            return;
1256        }
1257
1258        String schemaName = schema.getSchemaName();
1259
1260        if ( registries.isSchemaLoaded( schemaName ) )
1261        {
1262            LOG.info( "{} schema has already been loaded" + schema.getSchemaName() );
1263            return;
1264        }
1265
1266        String[] deps = schema.getDependencies();
1267
1268        // if no deps then load this guy and return
1269        if ( ( deps == null ) || ( deps.length == 0 ) )
1270        {
1271            load( registries, schema );
1272
1273            return;
1274        }
1275
1276        /*
1277         * We got deps and need to load them before this schema.  We go through
1278         * all deps loading them with their deps first if they have not been
1279         * loaded.
1280         */
1281        for ( String depName : deps )
1282        {
1283            if ( registries.isSchemaLoaded( schemaName ) )
1284            {
1285                // The schema is already loaded. Loop on the next schema
1286                continue;
1287            }
1288            else
1289            {
1290                // Call recursively this method
1291                Schema schemaDep = schemaLoader.getSchema( depName );
1292                loadDepsFirstRelaxed( schemaDep );
1293            }
1294        }
1295
1296        // Now load the current schema
1297        load( registries, schema );
1298    }
1299
1300
1301    /**
1302     * {@inheritDoc}
1303     */
1304    public void setRegistries( Registries registries )
1305    {
1306        // TODO Auto-generated method stub
1307
1308    }
1309
1310
1311    /**
1312     * {@inheritDoc}
1313     */
1314    public boolean unload( Schema... schemas ) throws LdapException
1315    {
1316        boolean unloaded = false;
1317
1318        // Reset the errors if not null
1319        if ( errors != null )
1320        {
1321            errors.clear();
1322        }
1323
1324        // Work on a cloned and relaxed registries
1325        Registries clonedRegistries = cloneRegistries();
1326        clonedRegistries.setRelaxed();
1327
1328        // Load the schemas
1329        for ( Schema schema : schemas )
1330        {
1331            unload( clonedRegistries, schema );
1332        }
1333
1334        // Build the cross references
1335        errors = clonedRegistries.buildReferences();
1336
1337        if ( errors.isEmpty() )
1338        {
1339            // Ok no errors. Check the registries now
1340            errors = clonedRegistries.checkRefInteg();
1341
1342            if ( errors.isEmpty() )
1343            {
1344                // We are golden : let's apply the schema in the real registries
1345                registries.setRelaxed();
1346
1347                // Load the schemas
1348                for ( Schema schema : schemas )
1349                {
1350                    unload( registries, schema );
1351
1352                    // Update the schema dependences
1353                    for ( String dep : schema.getDependencies() )
1354                    {
1355                        Set<String> deps = schemaDependences.get( dep );
1356
1357                        if ( deps != null )
1358                        {
1359                            deps.remove( schema.getSchemaName() );
1360                        }
1361                    }
1362
1363                    schemaLoader.removeSchema( schema );
1364                }
1365
1366                // Build the cross references
1367                errors = registries.buildReferences();
1368                registries.setStrict();
1369
1370                unloaded = true;
1371            }
1372        }
1373
1374        // clear the cloned registries
1375        clonedRegistries.clear();
1376
1377        return unloaded;
1378    }
1379
1380
1381    /**
1382     * {@inheritDoc}
1383     */
1384    public boolean unload( String... schemaNames ) throws LdapException
1385    {
1386        Schema[] schemas = toArray( schemaNames );
1387
1388        return unload( schemas );
1389    }
1390
1391
1392    /**
1393     * {@inheritDoc}
1394     */
1395    public boolean verify( Schema... schemas ) throws Exception
1396    {
1397        // Work on a cloned registries
1398        Registries clonedRegistries = cloneRegistries();
1399
1400        // Loop on all the schemas
1401        for ( Schema schema : schemas )
1402        {
1403            try
1404            {
1405                // Inject the schema
1406                boolean loaded = load( clonedRegistries, schema );
1407
1408                if ( !loaded )
1409                {
1410                    // We got an error : exit
1411                    clonedRegistries.clear();
1412                    return false;
1413                }
1414
1415                // Now, check the registries
1416                List<Throwable> errors = clonedRegistries.checkRefInteg();
1417
1418                if ( errors.size() != 0 )
1419                {
1420                    // We got an error : exit
1421                    clonedRegistries.clear();
1422                    return false;
1423                }
1424            }
1425            catch ( Exception e )
1426            {
1427                // We got an error : exit
1428                clonedRegistries.clear();
1429                return false;
1430            }
1431        }
1432
1433        // We can now delete the cloned registries before exiting
1434        clonedRegistries.clear();
1435
1436        return true;
1437    }
1438
1439
1440    /**
1441     * {@inheritDoc}
1442     */
1443    public boolean verify( String... schemas ) throws Exception
1444    {
1445        return verify( toArray( schemas ) );
1446    }
1447
1448
1449    /**
1450     * {@inheritDoc}
1451     */
1452    public void setSchemaLoader( SchemaLoader schemaLoader )
1453    {
1454        this.schemaLoader = schemaLoader;
1455    }
1456
1457
1458    /**
1459     * @return the namingContext
1460     */
1461    public Dn getNamingContext()
1462    {
1463        return namingContext;
1464    }
1465
1466
1467    /**
1468     * Initializes the SchemaService
1469     *
1470     * @throws Exception If the initialization fails
1471     */
1472    public void initialize() throws Exception
1473    {
1474        try
1475        {
1476            schemaModificationAttributesDn = new Dn( SchemaConstants.SCHEMA_MODIFICATIONS_DN );
1477            schemaModificationAttributesDn.apply( new DefaultSchemaManager() );
1478        }
1479        catch ( LdapInvalidDnException e )
1480        {
1481            throw new RuntimeException( e );
1482        }
1483    }
1484
1485
1486    /**
1487     * {@inheritDoc}
1488     */
1489    public SchemaLoader getLoader()
1490    {
1491        return schemaLoader;
1492    }
1493
1494
1495    //-----------------------------------------------------------------------------------
1496    // Immutable accessors
1497    //-----------------------------------------------------------------------------------
1498    /**
1499     * {@inheritDoc}
1500     */
1501    public AttributeTypeRegistry getAttributeTypeRegistry()
1502    {
1503        return new ImmutableAttributeTypeRegistry( registries.getAttributeTypeRegistry() );
1504    }
1505
1506
1507    /**
1508     * {@inheritDoc}
1509     */
1510    public ComparatorRegistry getComparatorRegistry()
1511    {
1512        return new ImmutableComparatorRegistry( registries.getComparatorRegistry() );
1513    }
1514
1515
1516    /**
1517     * {@inheritDoc}
1518     */
1519    public DITContentRuleRegistry getDITContentRuleRegistry()
1520    {
1521        return new ImmutableDITContentRuleRegistry( registries.getDitContentRuleRegistry() );
1522    }
1523
1524
1525    /**
1526     * {@inheritDoc}
1527     */
1528    public DITStructureRuleRegistry getDITStructureRuleRegistry()
1529    {
1530        return new ImmutableDITStructureRuleRegistry( registries.getDitStructureRuleRegistry() );
1531    }
1532
1533
1534    /**
1535     * {@inheritDoc}
1536     */
1537    public MatchingRuleRegistry getMatchingRuleRegistry()
1538    {
1539        return new ImmutableMatchingRuleRegistry( registries.getMatchingRuleRegistry() );
1540    }
1541
1542
1543    /**
1544     * {@inheritDoc}
1545     */
1546    public MatchingRuleUseRegistry getMatchingRuleUseRegistry()
1547    {
1548        return new ImmutableMatchingRuleUseRegistry( registries.getMatchingRuleUseRegistry() );
1549    }
1550
1551
1552    /**
1553     * {@inheritDoc}
1554     */
1555    public NameFormRegistry getNameFormRegistry()
1556    {
1557        return new ImmutableNameFormRegistry( registries.getNameFormRegistry() );
1558    }
1559
1560
1561    /**
1562     * {@inheritDoc}
1563     */
1564    public NormalizerRegistry getNormalizerRegistry()
1565    {
1566        return new ImmutableNormalizerRegistry( registries.getNormalizerRegistry() );
1567    }
1568
1569
1570    /**
1571     * {@inheritDoc}
1572     */
1573    public ObjectClassRegistry getObjectClassRegistry()
1574    {
1575        return new ImmutableObjectClassRegistry( registries.getObjectClassRegistry() );
1576    }
1577
1578
1579    /**
1580     * {@inheritDoc}
1581     */
1582    public LdapSyntaxRegistry getLdapSyntaxRegistry()
1583    {
1584        return new ImmutableLdapSyntaxRegistry( registries.getLdapSyntaxRegistry() );
1585    }
1586
1587
1588    /**
1589     * {@inheritDoc}
1590     */
1591    public SyntaxCheckerRegistry getSyntaxCheckerRegistry()
1592    {
1593        return new ImmutableSyntaxCheckerRegistry( registries.getSyntaxCheckerRegistry() );
1594    }
1595
1596
1597    /**
1598     * {@inheritDoc}
1599     */
1600    public AttributeType lookupAttributeTypeRegistry( String oid ) throws LdapException
1601    {
1602        return registries.getAttributeTypeRegistry().lookup( Strings.toLowerCase( oid ).trim() );
1603    }
1604
1605
1606    /**
1607     * {@inheritDoc}
1608     */
1609    public AttributeType getAttributeType( String oid )
1610    {
1611        try
1612        {
1613            return registries.getAttributeTypeRegistry().lookup( Strings.toLowerCase( oid ).trim() );
1614        }
1615        catch ( LdapException lnsae )
1616        {
1617            return null;
1618        }
1619    }
1620
1621
1622    /**
1623     * {@inheritDoc}
1624     */
1625    public LdapComparator<?> lookupComparatorRegistry( String oid ) throws LdapException
1626    {
1627        return registries.getComparatorRegistry().lookup( oid );
1628    }
1629
1630
1631    /**
1632     * {@inheritDoc}
1633     */
1634    public MatchingRule lookupMatchingRuleRegistry( String oid ) throws LdapException
1635    {
1636        return registries.getMatchingRuleRegistry().lookup( Strings.toLowerCase( oid ).trim() );
1637    }
1638
1639
1640    /**
1641     * {@inheritDoc}
1642     */
1643    public Normalizer lookupNormalizerRegistry( String oid ) throws LdapException
1644    {
1645        return registries.getNormalizerRegistry().lookup( oid );
1646    }
1647
1648
1649    /**
1650     * {@inheritDoc}
1651     */
1652    public ObjectClass lookupObjectClassRegistry( String oid ) throws LdapException
1653    {
1654        return registries.getObjectClassRegistry().lookup( Strings.toLowerCase( oid ).trim() );
1655    }
1656
1657
1658    /**
1659     * {@inheritDoc}
1660     */
1661    public LdapSyntax lookupLdapSyntaxRegistry( String oid ) throws LdapException
1662    {
1663        return registries.getLdapSyntaxRegistry().lookup( Strings.toLowerCase( oid ).trim() );
1664    }
1665
1666
1667    /**
1668     * {@inheritDoc}
1669     */
1670    public SyntaxChecker lookupSyntaxCheckerRegistry( String oid ) throws LdapException
1671    {
1672        return registries.getSyntaxCheckerRegistry().lookup( oid );
1673    }
1674
1675
1676    /**
1677     * Check that the given OID exists in the globalOidRegistry.
1678     */
1679    private boolean checkOidExist( SchemaObject schemaObject )
1680    {
1681        if ( !( schemaObject instanceof LoadableSchemaObject ) )
1682        {
1683            return registries.getGlobalOidRegistry().contains( schemaObject.getOid() );
1684        }
1685
1686        if ( schemaObject instanceof LdapComparator<?> )
1687        {
1688            return registries.getComparatorRegistry().contains( schemaObject.getOid() );
1689        }
1690
1691        if ( schemaObject instanceof SyntaxChecker )
1692        {
1693            return registries.getSyntaxCheckerRegistry().contains( schemaObject.getOid() );
1694        }
1695
1696        if ( schemaObject instanceof Normalizer )
1697        {
1698            return registries.getNormalizerRegistry().contains( schemaObject.getOid() );
1699        }
1700
1701        return false;
1702    }
1703
1704
1705    /**
1706     * Get the inner SchemaObject if it's not a C/N/SC
1707     */
1708    private SchemaObject getSchemaObject( SchemaObject schemaObject ) throws LdapException
1709    {
1710        if ( schemaObject instanceof LoadableSchemaObject )
1711        {
1712            return schemaObject;
1713        }
1714        else
1715        {
1716            return registries.getGlobalOidRegistry().getSchemaObject( schemaObject.getOid() );
1717        }
1718    }
1719
1720
1721    /**
1722     * Retrieve the schema name for a specific SchemaObject, or return "other" if none is found.
1723     */
1724    private String getSchemaName( SchemaObject schemaObject )
1725    {
1726        String schemaName = Strings.toLowerCase( schemaObject.getSchemaName() );
1727
1728        if ( Strings.isEmpty( schemaName ) )
1729        {
1730            return MetaSchemaConstants.SCHEMA_OTHER;
1731        }
1732
1733        if ( schemaLoader.getSchema( schemaName ) == null )
1734        {
1735            return null;
1736        }
1737        else
1738        {
1739            return schemaName;
1740        }
1741    }
1742
1743
1744    private SchemaObject copy( SchemaObject schemaObject )
1745    {
1746        SchemaObject copy = null;
1747
1748        if ( !( schemaObject instanceof LoadableSchemaObject ) )
1749        {
1750            copy = schemaObject.copy();
1751        }
1752        else
1753        {
1754            // Check the schemaObject here.
1755            if ( ( ( LoadableSchemaObject ) schemaObject ).isValid() )
1756            {
1757                copy = schemaObject;
1758            }
1759            else
1760            {
1761                // We have an invalid SchemaObject, no need to go any further
1762                Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err(
1763                    I18n.ERR_11007, schemaObject.getOid() ) );
1764                errors.add( error );
1765            }
1766        }
1767
1768        return copy;
1769    }
1770
1771
1772    //-----------------------------------------------------------------------------------
1773    // SchemaObject operations
1774    //-----------------------------------------------------------------------------------
1775    /**
1776     * {@inheritDoc}
1777     */
1778    public boolean add( SchemaObject schemaObject ) throws LdapException
1779    {
1780        // First, clear the errors
1781        errors.clear();
1782
1783        // Clone the schemaObject
1784        SchemaObject copy = copy( schemaObject );
1785
1786        if ( copy == null )
1787        {
1788            return false;
1789        }
1790
1791        if ( registries.isRelaxed() )
1792        {
1793            // Apply the addition right away
1794            registries.add( errors, copy );
1795
1796            return errors.isEmpty();
1797        }
1798        else
1799        {
1800            // Clone, apply, check, then apply again if ok
1801            // The new schemaObject's OID must not already exist
1802            if ( checkOidExist( copy ) )
1803            {
1804                LdapSchemaException ldapSchemaException = new LdapSchemaException(
1805                    LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_11008, schemaObject.getOid() ) );
1806                ldapSchemaException.setSourceObject( schemaObject );
1807                errors.add( ldapSchemaException );
1808
1809                return false;
1810            }
1811
1812            // Build the new AttributeType from the given entry
1813            String schemaName = getSchemaName( copy );
1814
1815            if ( schemaName == null )
1816            {
1817                // The schema associated with the SchemaaObject does not exist. This is not valid.
1818
1819                LdapSchemaException ldapSchemaException = new LdapSchemaException(
1820                    LdapSchemaExceptionCodes.NONEXISTENT_SCHEMA, I18n.err( I18n.ERR_11009, schemaObject.getOid(),
1821                        copy.getSchemaName() ) );
1822                ldapSchemaException.setSourceObject( schemaObject );
1823                ldapSchemaException.setRelatedId( copy.getSchemaName() );
1824                errors.add( ldapSchemaException );
1825
1826                return false;
1827            }
1828
1829            // At this point, the constructed AttributeType has not been checked against the
1830            // existing Registries. It may be broken (missing SUP, or such), it will be checked
1831            // there, if the schema and the AttributeType are both enabled.
1832            Schema schema = getLoadedSchema( schemaName );
1833
1834            if ( schema == null )
1835            {
1836                // The SchemaObject must be associated with an existing schema
1837                String msg = I18n.err( I18n.ERR_11010, copy.getOid() );
1838                LOG.info( msg );
1839                Throwable error = new LdapProtocolErrorException( msg );
1840                errors.add( error );
1841                return false;
1842            }
1843
1844            if ( schema.isEnabled() && copy.isEnabled() )
1845            {
1846                // As we may break the registries, work on a cloned registries
1847                Registries clonedRegistries = null;
1848
1849                try
1850                {
1851                    clonedRegistries = registries.clone();
1852                }
1853                catch ( CloneNotSupportedException cnse )
1854                {
1855                    throw new LdapOtherException( cnse.getMessage() );
1856                }
1857
1858                // Inject the new SchemaObject in the cloned registries
1859                clonedRegistries.add( errors, copy );
1860
1861                // Remove the cloned registries
1862                clonedRegistries.clear();
1863
1864                // If we didn't get any error, apply the addition to the real retistries
1865                if ( errors.isEmpty() )
1866                {
1867                    // Copy again as the clonedRegistries clear has removed the previous copy
1868                    copy = copy( schemaObject );
1869
1870                    // Apply the addition to the real registries
1871                    registries.add( errors, copy );
1872
1873                    LOG.debug( "Added {} into the enabled schema {}", copy.getName(), schemaName );
1874
1875                    return true;
1876                }
1877                else
1878                {
1879                    // We have some error : reject the addition and get out
1880                    String msg = "Cannot add the SchemaObject " + copy.getOid() + " into the registries, "
1881                        + "the resulting registries would be inconsistent :" + Strings.listToString( errors );
1882                    LOG.info( msg );
1883
1884                    return false;
1885                }
1886            }
1887            else
1888            {
1889                // At least, we register the OID in the globalOidRegistry, and associates it with the
1890                // schema
1891                registries.associateWithSchema( errors, copy );
1892
1893                LOG.debug( "Added {} into the disabled schema {}", copy.getName(), schemaName );
1894                return errors.isEmpty();
1895            }
1896        }
1897    }
1898
1899
1900    /**
1901     * {@inheritDoc}
1902     */
1903    public boolean delete( SchemaObject schemaObject ) throws LdapException
1904    {
1905        // First, clear the errors
1906        errors.clear();
1907
1908        if ( registries.isRelaxed() )
1909        {
1910            // Apply the addition right away
1911            registries.delete( errors, schemaObject );
1912
1913            return errors.isEmpty();
1914        }
1915        else
1916        {
1917            // Clone, apply, check, then apply again if ok
1918            // The new schemaObject's OID must exist
1919            if ( !checkOidExist( schemaObject ) )
1920            {
1921                Throwable error = new LdapProtocolErrorException( I18n.err( I18n.ERR_11011, schemaObject.getOid() ) );
1922                errors.add( error );
1923                return false;
1924            }
1925
1926            // Get the SchemaObject to delete if it's not a LoadableSchemaObject
1927            SchemaObject toDelete = getSchemaObject( schemaObject );
1928
1929            // First check that this SchemaObject does not have any referencing SchemaObjects
1930            Set<SchemaObjectWrapper> referencing = registries.getReferencing( toDelete );
1931
1932            if ( ( referencing != null ) && !referencing.isEmpty() )
1933            {
1934                String msg = I18n.err( I18n.ERR_11012, schemaObject.getOid(), Strings.setToString( referencing ) );
1935
1936                Throwable error = new LdapProtocolErrorException( msg );
1937                errors.add( error );
1938                return false;
1939            }
1940
1941            String schemaName = getSchemaName( toDelete );
1942
1943            // At this point, the deleted AttributeType may be referenced, it will be checked
1944            // there, if the schema and the AttributeType are both enabled.
1945            Schema schema = getLoadedSchema( schemaName );
1946
1947            if ( schema == null )
1948            {
1949                // The SchemaObject must be associated with an existing schema
1950                String msg = I18n.err( I18n.ERR_11013, schemaObject.getOid() );
1951                LOG.info( msg );
1952                Throwable error = new LdapProtocolErrorException( msg );
1953                errors.add( error );
1954                return false;
1955            }
1956
1957            if ( schema.isEnabled() && schemaObject.isEnabled() )
1958            {
1959                // As we may break the registries, work on a cloned registries
1960                Registries clonedRegistries = null;
1961
1962                try
1963                {
1964                    clonedRegistries = registries.clone();
1965                }
1966                catch ( CloneNotSupportedException cnse )
1967                {
1968                    throw new LdapOtherException( cnse.getMessage() );
1969                }
1970
1971                // Delete the SchemaObject from the cloned registries
1972                clonedRegistries.delete( errors, toDelete );
1973
1974                // Remove the cloned registries
1975                clonedRegistries.clear();
1976
1977                // If we didn't get any error, apply the deletion to the real retistries
1978                if ( errors.isEmpty() )
1979                {
1980                    // Apply the deletion to the real registries
1981                    registries.delete( errors, toDelete );
1982
1983                    LOG.debug( "Removed {} from the enabled schema {}", toDelete.getName(), schemaName );
1984
1985                    return true;
1986                }
1987                else
1988                {
1989                    // We have some error : reject the deletion and get out
1990                    String msg = "Cannot delete the SchemaObject " + schemaObject.getOid() + " from the registries, "
1991                        + "the resulting registries would be inconsistent :" + Strings.listToString( errors );
1992                    LOG.info( msg );
1993
1994                    return false;
1995                }
1996            }
1997            else
1998            {
1999                // At least, we register the OID in the globalOidRegistry, and associates it with the
2000                // schema
2001                registries.associateWithSchema( errors, schemaObject );
2002
2003                LOG.debug( "Removed {} from the disabled schema {}", schemaObject.getName(), schemaName );
2004                return errors.isEmpty();
2005            }
2006        }
2007    }
2008
2009
2010    /**
2011     * {@inheritDoc}
2012     */
2013    public Map<String, OidNormalizer> getNormalizerMapping()
2014    {
2015        return registries.getAttributeTypeRegistry().getNormalizerMapping();
2016    }
2017
2018
2019    /**
2020     * {@inheritDoc}
2021     */
2022    @SuppressWarnings("rawtypes")
2023    public OidRegistry getGlobalOidRegistry()
2024    {
2025        return registries.getGlobalOidRegistry();
2026    }
2027
2028
2029    /**
2030     * {@inheritDoc}
2031     */
2032    public Schema getLoadedSchema( String schemaName )
2033    {
2034        return schemaLoader.getSchema( schemaName );
2035    }
2036
2037
2038    /**
2039     * {@inheritDoc}
2040     */
2041    public boolean isSchemaLoaded( String schemaName )
2042    {
2043        try
2044        {
2045            Schema schema = schemaLoader.getSchema( schemaName );
2046            return schema != null;
2047        }
2048        catch ( Exception e )
2049        {
2050            return false;
2051        }
2052    }
2053
2054
2055    /**
2056     * {@inheritDoc}
2057     */
2058    public SchemaObject unregisterAttributeType( String attributeTypeOid ) throws LdapException
2059    {
2060        return registries.getAttributeTypeRegistry().unregister( attributeTypeOid );
2061    }
2062
2063
2064    /**
2065     * {@inheritDoc}
2066     */
2067    public SchemaObject unregisterComparator( String comparatorOid ) throws LdapException
2068    {
2069        return registries.getComparatorRegistry().unregister( comparatorOid );
2070    }
2071
2072
2073    /**
2074     * {@inheritDoc}
2075     */
2076    public SchemaObject unregisterDitControlRule( String ditControlRuleOid ) throws LdapException
2077    {
2078        return registries.getDitContentRuleRegistry().unregister( ditControlRuleOid );
2079    }
2080
2081
2082    /**
2083     * {@inheritDoc}
2084     */
2085    public SchemaObject unregisterDitStructureRule( String ditStructureRuleOid ) throws LdapException
2086    {
2087        return registries.getDitStructureRuleRegistry().unregister( ditStructureRuleOid );
2088    }
2089
2090
2091    /**
2092     * {@inheritDoc}
2093     */
2094    public SchemaObject unregisterLdapSyntax( String ldapSyntaxOid ) throws LdapException
2095    {
2096        return registries.getLdapSyntaxRegistry().unregister( ldapSyntaxOid );
2097    }
2098
2099
2100    /**
2101     * {@inheritDoc}
2102     */
2103    public SchemaObject unregisterMatchingRule( String matchingRuleOid ) throws LdapException
2104    {
2105        return registries.getMatchingRuleRegistry().unregister( matchingRuleOid );
2106    }
2107
2108
2109    /**
2110     * {@inheritDoc}
2111     */
2112    public SchemaObject unregisterMatchingRuleUse( String matchingRuleUseOid ) throws LdapException
2113    {
2114        return registries.getMatchingRuleUseRegistry().unregister( matchingRuleUseOid );
2115    }
2116
2117
2118    /**
2119     * {@inheritDoc}
2120     */
2121    public SchemaObject unregisterNameForm( String nameFormOid ) throws LdapException
2122    {
2123        return registries.getNameFormRegistry().unregister( nameFormOid );
2124    }
2125
2126
2127    /**
2128     * {@inheritDoc}
2129     */
2130    public SchemaObject unregisterNormalizer( String normalizerOid ) throws LdapException
2131    {
2132        return registries.getNormalizerRegistry().unregister( normalizerOid );
2133    }
2134
2135
2136    /**
2137     * {@inheritDoc}
2138     */
2139    public SchemaObject unregisterObjectClass( String objectClassOid ) throws LdapException
2140    {
2141        return registries.getObjectClassRegistry().unregister( objectClassOid );
2142    }
2143
2144
2145    /**
2146     * {@inheritDoc}
2147     */
2148    public SchemaObject unregisterSyntaxChecker( String syntaxCheckerOid ) throws LdapException
2149    {
2150        return registries.getSyntaxCheckerRegistry().unregister( syntaxCheckerOid );
2151    }
2152
2153
2154    /**
2155     * Tells if the SchemaManager is permissive or if it must be checked
2156     * against inconsistencies.
2157     *
2158     * @return True if SchemaObjects can be added even if they break the consistency
2159     */
2160    public boolean isRelaxed()
2161    {
2162        return isRelaxed;
2163    }
2164
2165
2166    /**
2167     * Tells if the SchemaManager is strict.
2168     *
2169     * @return True if SchemaObjects cannot be added if they break the consistency
2170     */
2171    public boolean isStrict()
2172    {
2173        return !isRelaxed;
2174    }
2175
2176
2177    /**
2178     * {@inheritDoc}
2179     */
2180    public Set<String> listDependentSchemaNames( String schemaName )
2181    {
2182        return schemaDependences.get( schemaName );
2183    }
2184
2185
2186    /**
2187     * Change the SchemaManager to a relaxed mode, where invalid SchemaObjects
2188     * can be registered.
2189     */
2190    public void setRelaxed()
2191    {
2192        isRelaxed = RELAXED;
2193    }
2194
2195
2196    /**
2197     * Change the SchemaManager to a strict mode, where invalid SchemaObjects
2198     * cannot be registered.
2199     */
2200    public void setStrict()
2201    {
2202        isRelaxed = STRICT;
2203    }
2204
2205
2206    /**
2207     * {@inheritDoc}
2208     */
2209    public boolean isDisabled( String schemaName )
2210    {
2211        Schema schema = registries.getLoadedSchema( schemaName );
2212
2213        return ( schema != null ) && schema.isDisabled();
2214    }
2215
2216
2217    /**
2218     * {@inheritDoc}
2219     */
2220    public boolean isDisabled( Schema schema )
2221    {
2222        return ( schema != null ) && schema.isDisabled();
2223    }
2224
2225
2226    /**
2227     * {@inheritDoc}
2228     */
2229    public boolean isEnabled( String schemaName )
2230    {
2231        Schema schema = registries.getLoadedSchema( schemaName );
2232
2233        return ( schema != null ) && schema.isEnabled();
2234    }
2235
2236
2237    /**
2238     * {@inheritDoc}
2239     */
2240    public boolean isEnabled( Schema schema )
2241    {
2242        return ( schema != null ) && schema.isEnabled();
2243    }
2244}