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