View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.ldap.schema.manager.impl;
21  
22  
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.directory.api.i18n.I18n;
33  import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
34  import org.apache.directory.api.ldap.model.entry.Entry;
35  import org.apache.directory.api.ldap.model.exception.LdapException;
36  import org.apache.directory.api.ldap.model.exception.LdapOtherException;
37  import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException;
38  import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
39  import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
40  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
41  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
42  import org.apache.directory.api.ldap.model.name.Dn;
43  import org.apache.directory.api.ldap.model.schema.AttributeType;
44  import org.apache.directory.api.ldap.model.schema.LdapComparator;
45  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
46  import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject;
47  import org.apache.directory.api.ldap.model.schema.MatchingRule;
48  import org.apache.directory.api.ldap.model.schema.Normalizer;
49  import org.apache.directory.api.ldap.model.schema.ObjectClass;
50  import org.apache.directory.api.ldap.model.schema.SchemaManager;
51  import org.apache.directory.api.ldap.model.schema.SchemaObject;
52  import org.apache.directory.api.ldap.model.schema.SchemaObjectWrapper;
53  import org.apache.directory.api.ldap.model.schema.SchemaUtils;
54  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
55  import org.apache.directory.api.ldap.model.schema.normalizers.OidNormalizer;
56  import org.apache.directory.api.ldap.model.schema.registries.AttributeTypeRegistry;
57  import org.apache.directory.api.ldap.model.schema.registries.ComparatorRegistry;
58  import org.apache.directory.api.ldap.model.schema.registries.DitContentRuleRegistry;
59  import org.apache.directory.api.ldap.model.schema.registries.DitStructureRuleRegistry;
60  import org.apache.directory.api.ldap.model.schema.registries.ImmutableAttributeTypeRegistry;
61  import org.apache.directory.api.ldap.model.schema.registries.ImmutableComparatorRegistry;
62  import org.apache.directory.api.ldap.model.schema.registries.ImmutableDitContentRuleRegistry;
63  import org.apache.directory.api.ldap.model.schema.registries.ImmutableDitStructureRuleRegistry;
64  import org.apache.directory.api.ldap.model.schema.registries.ImmutableLdapSyntaxRegistry;
65  import org.apache.directory.api.ldap.model.schema.registries.ImmutableMatchingRuleRegistry;
66  import org.apache.directory.api.ldap.model.schema.registries.ImmutableMatchingRuleUseRegistry;
67  import org.apache.directory.api.ldap.model.schema.registries.ImmutableNameFormRegistry;
68  import org.apache.directory.api.ldap.model.schema.registries.ImmutableNormalizerRegistry;
69  import org.apache.directory.api.ldap.model.schema.registries.ImmutableObjectClassRegistry;
70  import org.apache.directory.api.ldap.model.schema.registries.ImmutableSyntaxCheckerRegistry;
71  import org.apache.directory.api.ldap.model.schema.registries.LdapSyntaxRegistry;
72  import org.apache.directory.api.ldap.model.schema.registries.LowerCaseKeyMap;
73  import org.apache.directory.api.ldap.model.schema.registries.MatchingRuleRegistry;
74  import org.apache.directory.api.ldap.model.schema.registries.MatchingRuleUseRegistry;
75  import org.apache.directory.api.ldap.model.schema.registries.NameFormRegistry;
76  import org.apache.directory.api.ldap.model.schema.registries.NormalizerRegistry;
77  import org.apache.directory.api.ldap.model.schema.registries.ObjectClassRegistry;
78  import org.apache.directory.api.ldap.model.schema.registries.OidRegistry;
79  import org.apache.directory.api.ldap.model.schema.registries.Registries;
80  import org.apache.directory.api.ldap.model.schema.registries.Schema;
81  import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
82  import org.apache.directory.api.ldap.model.schema.registries.SyntaxCheckerRegistry;
83  import org.apache.directory.api.ldap.schema.loader.EntityFactory;
84  import org.apache.directory.api.ldap.schema.loader.JarLdifSchemaLoader;
85  import org.apache.directory.api.ldap.schema.loader.SchemaEntityFactory;
86  import org.apache.directory.api.util.Strings;
87  import org.slf4j.Logger;
88  import org.slf4j.LoggerFactory;
89  
90  
91  /**
92   * The SchemaManager class : it handles all the schema operations (addition, removal,
93   * modification).
94   *
95   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
96   */
97  public class DefaultSchemaManager implements SchemaManager
98  {
99      /** 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 }