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.schemamanager.impl;
21  
22  
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.directory.api.i18n.I18n;
32  import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
33  import org.apache.directory.api.ldap.model.entry.Entry;
34  import org.apache.directory.api.ldap.model.exception.LdapException;
35  import org.apache.directory.api.ldap.model.exception.LdapOtherException;
36  import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException;
37  import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
38  import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
39  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
40  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
41  import org.apache.directory.api.ldap.model.name.Dn;
42  import org.apache.directory.api.ldap.model.schema.AttributeType;
43  import org.apache.directory.api.ldap.model.schema.LdapComparator;
44  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
45  import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject;
46  import org.apache.directory.api.ldap.model.schema.MatchingRule;
47  import org.apache.directory.api.ldap.model.schema.Normalizer;
48  import org.apache.directory.api.ldap.model.schema.ObjectClass;
49  import org.apache.directory.api.ldap.model.schema.SchemaManager;
50  import org.apache.directory.api.ldap.model.schema.SchemaObject;
51  import org.apache.directory.api.ldap.model.schema.SchemaObjectWrapper;
52  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
53  import org.apache.directory.api.ldap.model.schema.normalizers.OidNormalizer;
54  import org.apache.directory.api.ldap.model.schema.registries.AttributeTypeRegistry;
55  import org.apache.directory.api.ldap.model.schema.registries.ComparatorRegistry;
56  import org.apache.directory.api.ldap.model.schema.registries.DitContentRuleRegistry;
57  import org.apache.directory.api.ldap.model.schema.registries.DitStructureRuleRegistry;
58  import org.apache.directory.api.ldap.model.schema.registries.ImmutableAttributeTypeRegistry;
59  import org.apache.directory.api.ldap.model.schema.registries.ImmutableComparatorRegistry;
60  import org.apache.directory.api.ldap.model.schema.registries.ImmutableDitContentRuleRegistry;
61  import org.apache.directory.api.ldap.model.schema.registries.ImmutableDitStructureRuleRegistry;
62  import org.apache.directory.api.ldap.model.schema.registries.ImmutableLdapSyntaxRegistry;
63  import org.apache.directory.api.ldap.model.schema.registries.ImmutableMatchingRuleRegistry;
64  import org.apache.directory.api.ldap.model.schema.registries.ImmutableMatchingRuleUseRegistry;
65  import org.apache.directory.api.ldap.model.schema.registries.ImmutableNameFormRegistry;
66  import org.apache.directory.api.ldap.model.schema.registries.ImmutableNormalizerRegistry;
67  import org.apache.directory.api.ldap.model.schema.registries.ImmutableObjectClassRegistry;
68  import org.apache.directory.api.ldap.model.schema.registries.ImmutableSyntaxCheckerRegistry;
69  import org.apache.directory.api.ldap.model.schema.registries.LdapSyntaxRegistry;
70  import org.apache.directory.api.ldap.model.schema.registries.MatchingRuleRegistry;
71  import org.apache.directory.api.ldap.model.schema.registries.MatchingRuleUseRegistry;
72  import org.apache.directory.api.ldap.model.schema.registries.NameFormRegistry;
73  import org.apache.directory.api.ldap.model.schema.registries.NormalizerRegistry;
74  import org.apache.directory.api.ldap.model.schema.registries.ObjectClassRegistry;
75  import org.apache.directory.api.ldap.model.schema.registries.OidRegistry;
76  import org.apache.directory.api.ldap.model.schema.registries.Registries;
77  import org.apache.directory.api.ldap.model.schema.registries.Schema;
78  import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
79  import org.apache.directory.api.ldap.model.schema.registries.SyntaxCheckerRegistry;
80  import org.apache.directory.api.ldap.schemaloader.EntityFactory;
81  import org.apache.directory.api.ldap.schemaloader.JarLdifSchemaLoader;
82  import org.apache.directory.api.ldap.schemaloader.SchemaEntityFactory;
83  import org.apache.directory.api.util.Strings;
84  import org.slf4j.Logger;
85  import org.slf4j.LoggerFactory;
86  
87  
88  /**
89   * The SchemaManager class : it handles all the schema operations (addition, removal,
90   * modification).
91   *
92   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
93   */
94  public class DefaultSchemaManager implements SchemaManager
95  {
96      /** static class logger */
97      private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaManager.class );
98  
99      /** 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 }