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