View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.schema.bootstrap;
18  
19  
20  import java.util.Collection;
21  import java.util.Comparator;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Stack;
26  
27  import javax.naming.NamingException;
28  
29  import org.apache.ldap.common.schema.AttributeType;
30  import org.apache.ldap.common.schema.DITContentRule;
31  import org.apache.ldap.common.schema.DITStructureRule;
32  import org.apache.ldap.common.schema.MatchingRule;
33  import org.apache.ldap.common.schema.MatchingRuleUse;
34  import org.apache.ldap.common.schema.NameForm;
35  import org.apache.ldap.common.schema.Normalizer;
36  import org.apache.ldap.common.schema.ObjectClass;
37  import org.apache.ldap.common.schema.Syntax;
38  import org.apache.ldap.common.schema.SyntaxChecker;
39  import org.apache.ldap.server.jndi.ServerDirObjectFactory;
40  import org.apache.ldap.server.jndi.ServerDirStateFactory;
41  import org.apache.ldap.server.schema.AttributeTypeRegistry;
42  import org.apache.ldap.server.schema.ComparatorRegistry;
43  import org.apache.ldap.server.schema.DITContentRuleRegistry;
44  import org.apache.ldap.server.schema.DITStructureRuleRegistry;
45  import org.apache.ldap.server.schema.MatchingRuleRegistry;
46  import org.apache.ldap.server.schema.MatchingRuleUseRegistry;
47  import org.apache.ldap.server.schema.NameFormRegistry;
48  import org.apache.ldap.server.schema.NormalizerRegistry;
49  import org.apache.ldap.server.schema.ObjectClassRegistry;
50  import org.apache.ldap.server.schema.ObjectFactoryRegistry;
51  import org.apache.ldap.server.schema.StateFactoryRegistry;
52  import org.apache.ldap.server.schema.SyntaxCheckerRegistry;
53  import org.apache.ldap.server.schema.SyntaxRegistry;
54  
55  
56  /***
57   * Class which handles bootstrap schema class file loading.
58   *
59   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
60   * @version $Rev: 264732 $
61   */
62  public class BootstrapSchemaLoader
63  {
64      /*** stores schemas of producers for callback access */
65      private ThreadLocal schemas;
66      /*** stores registries associated with producers for callback access */
67      private ThreadLocal registries;
68      /*** the callback that just calls register() */
69      private final ProducerCallback cb = new ProducerCallback()
70      {
71          public void schemaObjectProduced( BootstrapProducer producer,
72                                            String registryKey,
73                                            Object schemaObject )
74              throws NamingException
75          {
76              register( producer.getType(), registryKey, schemaObject );
77          }
78      };
79  
80  
81      /***
82       * Creates a BootstrapSchema loader.
83       */
84      public BootstrapSchemaLoader()
85      {
86          schemas = new ThreadLocal();
87          registries = new ThreadLocal();
88      }
89  
90  
91  
92      /***
93       * Loads a set of schemas by loading and running all producers for each
94       * dependent schema first.
95       *
96       * @param bootstrapSchemas Collection of {@link BootstrapSchema}s to load
97       * @param registries the registries to fill with producer created objects
98       * @throws NamingException if there are any failures during this process
99       */
100     public final void load( Collection bootstrapSchemas, BootstrapRegistries registries )
101         throws NamingException
102     {
103         BootstrapSchema[] schemas = new BootstrapSchema[ bootstrapSchemas.size() ];
104         schemas = ( BootstrapSchema[] ) bootstrapSchemas.toArray( schemas );
105         HashMap loaded = new HashMap();
106         HashMap notLoaded = new HashMap();
107 
108         for ( int ii = 0; ii < schemas.length; ii++ )
109         {
110             notLoaded.put( schemas[ii].getSchemaName(), schemas[ii] );
111         }
112         
113         BootstrapSchema schema;
114         
115         // Create system schema and kick it off by loading system which
116         // will never depend on anything.
117         schema = new SystemSchema();
118         load( schema, registries );
119         notLoaded.remove( schema.getSchemaName() ); // Remove if user specified it.
120         loaded.put( schema.getSchemaName(), schema );
121 
122         Iterator list = notLoaded.values().iterator();
123         while ( list.hasNext() )
124         {
125             schema = ( BootstrapSchema ) list.next();
126             loadDepsFirst( new Stack(), notLoaded, schema, registries );
127             list = notLoaded.values().iterator();
128         }
129     }
130 
131 
132     /***
133      * Recursive method which loads schema's with their dependent schemas first
134      * and tracks what schemas it has seen so the recursion does not go out of
135      * control with depenency cycle detection.
136      *
137      * @param beenthere stack of schema names we have visited and have yet to load
138      * @param notLoaded hash of schemas keyed by name which have yet to be loaded
139      * @param schema the current schema we are attempting to load
140      * @param registries the set of registries to use while loading
141      * @throws NamingException if there is a cycle detected and/or another
142      * failure results while loading, producing and or registering schema objects
143      */
144     public final void loadDepsFirst( Stack beenthere, HashMap notLoaded,
145                                      BootstrapSchema schema,
146                                      BootstrapRegistries registries )
147         throws NamingException
148     {
149         beenthere.push( schema.getSchemaName() );
150         String[] deps = schema.getDependencies();
151 
152         // if no deps then load this guy and return
153         if ( deps == null || deps.length == 0 )
154         {
155             load( schema, registries );
156             notLoaded.remove( schema.getSchemaName() );
157             beenthere.pop();
158             return;
159         }
160 
161         /*
162          * We got deps and need to load them before this schema.  We go through
163          * all deps loading them with their deps first if they have not been
164          * loaded.
165          */
166         for ( int ii = 0; ii < deps.length; ii++ )
167         {
168             if ( ! notLoaded.containsKey( deps[ii] ) )
169             {
170                 continue;
171             }
172 
173             BootstrapSchema dep = ( BootstrapSchema ) notLoaded.get( deps[ii] );
174 
175             if ( beenthere.contains( dep.getSchemaName() ) )
176             {
177                 // push again so we show the cycle in output
178                 beenthere.push( dep.getSchemaName() );
179                 throw new NamingException( "schema dependency cycle detected: "
180                     + beenthere );
181             }
182 
183             loadDepsFirst( beenthere, notLoaded, dep, registries );
184         }
185 
186         // We have loaded all our deps so we can load this schema
187         load( schema, registries );
188         notLoaded.remove( schema.getSchemaName() );
189         beenthere.pop();
190     }
191 
192 
193     /***
194      * Loads a schema by loading and running all producers for te schema.
195      *
196      * @param schema the schema to load
197      * @param registries the registries to fill with producer created objects
198      * @throws NamingException if there are any failures during this process
199      */
200     public final void load( BootstrapSchema schema, BootstrapRegistries registries )
201         throws NamingException
202     {
203         this.registries.set( registries );
204         this.schemas.set( schema );
205 
206         List producers = ProducerTypeEnum.list();
207         for ( int ii = 0; ii < producers.size(); ii++ )
208         {
209             ProducerTypeEnum producerType = ( ProducerTypeEnum ) producers.get( ii );
210             BootstrapProducer producer = getProducer( schema, producerType.getName() );
211             producer.produce( registries, cb );
212         }
213     }
214 
215 
216     // ------------------------------------------------------------------------
217     // Utility Methods
218     // ------------------------------------------------------------------------
219 
220 
221     /***
222      * Registers objects
223      *
224      * @param type the type of the producer which determines the type of object produced
225      * @param id the primary key identifying the created object in a registry
226      * @param schemaObject the object being registered
227      * @throws NamingException if there are problems when registering the object
228      * in any of the registries
229      */
230     private void register( ProducerTypeEnum type, String id,
231                            Object schemaObject ) throws NamingException
232     {
233         BootstrapSchema schema = ( BootstrapSchema ) this.schemas.get();
234         BootstrapRegistries registries = ( BootstrapRegistries ) this.registries.get();
235 
236         switch( type.getValue() )
237         {
238             case( ProducerTypeEnum.NORMALIZER_PRODUCER_VAL ):
239                 Normalizer normalizer = ( Normalizer ) schemaObject;
240                 NormalizerRegistry normalizerRegistry;
241                 normalizerRegistry = registries.getNormalizerRegistry();
242                 normalizerRegistry.register( schema.getSchemaName(), id, normalizer );
243                 break;
244             case( ProducerTypeEnum.COMPARATOR_PRODUCER_VAL ):
245                 Comparator comparator = ( Comparator ) schemaObject;
246                 ComparatorRegistry comparatorRegistry;
247                 comparatorRegistry = registries.getComparatorRegistry();
248                 comparatorRegistry.register( schema.getSchemaName(), id, comparator );
249                 break;
250             case( ProducerTypeEnum.SYNTAX_CHECKER_PRODUCER_VAL ):
251                 SyntaxChecker syntaxChecker = ( SyntaxChecker ) schemaObject;
252                 SyntaxCheckerRegistry syntaxCheckerRegistry;
253                 syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
254                 syntaxCheckerRegistry.register( schema.getSchemaName(), id, syntaxChecker );
255                 break;
256             case( ProducerTypeEnum.SYNTAX_PRODUCER_VAL ):
257                 Syntax syntax = ( Syntax ) schemaObject;
258                 SyntaxRegistry syntaxRegistry = registries.getSyntaxRegistry();
259                 syntaxRegistry.register( schema.getSchemaName(), syntax );
260                 break;
261             case( ProducerTypeEnum.MATCHING_RULE_PRODUCER_VAL ):
262                 MatchingRule matchingRule = ( MatchingRule ) schemaObject;
263                 MatchingRuleRegistry matchingRuleRegistry;
264                 matchingRuleRegistry = registries.getMatchingRuleRegistry();
265                 matchingRuleRegistry.register( schema.getSchemaName(), matchingRule );
266                 break;
267             case( ProducerTypeEnum.ATTRIBUTE_TYPE_PRODUCER_VAL ):
268                 AttributeType attributeType = ( AttributeType ) schemaObject;
269                 AttributeTypeRegistry attributeTypeRegistry;
270                 attributeTypeRegistry = registries.getAttributeTypeRegistry();
271                 attributeTypeRegistry.register( schema.getSchemaName(), attributeType );
272                 break;
273             case( ProducerTypeEnum.OBJECT_CLASS_PRODUCER_VAL ):
274                 ObjectClass objectClass = ( ObjectClass ) schemaObject;
275                 ObjectClassRegistry objectClassRegistry;
276                 objectClassRegistry = registries.getObjectClassRegistry();
277                 objectClassRegistry.register( schema.getSchemaName(), objectClass );
278                 break;
279             case( ProducerTypeEnum.MATCHING_RULE_USE_PRODUCER_VAL ):
280                 MatchingRuleUse matchingRuleUse = ( MatchingRuleUse ) schemaObject;
281                 MatchingRuleUseRegistry matchingRuleUseRegistry;
282                 matchingRuleUseRegistry = registries.getMatchingRuleUseRegistry();
283                 matchingRuleUseRegistry.register( schema.getSchemaName(), matchingRuleUse );
284                 break;
285             case( ProducerTypeEnum.DIT_CONTENT_RULE_PRODUCER_VAL ):
286                 DITContentRule ditContentRule = ( DITContentRule ) schemaObject;
287                 DITContentRuleRegistry ditContentRuleRegistry;
288                 ditContentRuleRegistry = registries.getDitContentRuleRegistry();
289                 ditContentRuleRegistry.register( schema.getSchemaName(), ditContentRule );
290                 break;
291             case( ProducerTypeEnum.NAME_FORM_PRODUCER_VAL ):
292                 NameForm nameForm = ( NameForm ) schemaObject;
293                 NameFormRegistry nameFormRegistry;
294                 nameFormRegistry = registries.getNameFormRegistry();
295                 nameFormRegistry.register( schema.getSchemaName(), nameForm );
296                 break;
297             case( ProducerTypeEnum.DIT_STRUCTURE_RULE_PRODUCER_VAL ):
298                 DITStructureRule ditStructureRule = ( DITStructureRule ) schemaObject;
299                 DITStructureRuleRegistry ditStructureRuleRegistry;
300                 ditStructureRuleRegistry = registries.getDitStructureRuleRegistry();
301                 ditStructureRuleRegistry.register( schema.getSchemaName(), ditStructureRule );
302                 break;
303             case( ProducerTypeEnum.STATE_FACTORY_PRODUCER_VAL ):
304                 ServerDirStateFactory stateFactory = ( ServerDirStateFactory ) schemaObject;
305                 StateFactoryRegistry stateFactoryRegistry;
306                 stateFactoryRegistry = registries.getStateFactoryRegistry();
307                 stateFactoryRegistry.register( stateFactory );
308                 break;
309             case( ProducerTypeEnum.OBJECT_FACTORY_PRODUCER_VAL ):
310                 ServerDirObjectFactory objectFactory = ( ServerDirObjectFactory ) schemaObject;
311                 ObjectFactoryRegistry objectFactoryRegistry;
312                 objectFactoryRegistry = registries.getObjectFactoryRegistry();
313                 objectFactoryRegistry.register( objectFactory );
314                 break;
315             default:
316                 throw new IllegalStateException( "ProducerTypeEnum is broke!" );
317         }
318     }
319 
320 
321     /***
322      * Attempts first to try to load the target class for the Producer,
323      * then tries for the default if the target load fails.
324      *
325      * @param schema the bootstrap schema
326      * @param producerBase the producer's base name
327      * @throws NamingException if there are failures loading classes
328      */
329     private BootstrapProducer getProducer( BootstrapSchema schema, String producerBase )
330         throws NamingException
331     {
332         Class clazz = null;
333         boolean failedTargetLoad = false;
334         String defaultClassName;
335         String targetClassName = schema.getBaseClassName() + producerBase;
336 
337         try
338         {
339             clazz = Class.forName( targetClassName );
340         }
341         catch ( ClassNotFoundException e )
342         {
343             failedTargetLoad = true;
344             // @todo instead of trace report target class load failure to monitor
345             e.printStackTrace();
346         }
347 
348         if ( failedTargetLoad )
349         {
350             defaultClassName = schema.getDefaultBaseClassName() + producerBase;
351 
352             try
353             {
354                 clazz = Class.forName( defaultClassName );
355             }
356             catch ( ClassNotFoundException e )
357             {
358                 NamingException ne = new NamingException( "Failed to load " +
359                     producerBase + " for " + schema.getSchemaName()
360                     + " schema using following classes: "  + targetClassName
361                     + ", " + defaultClassName );
362                 ne.setRootCause( e );
363                 throw ne;
364             }
365         }
366 
367         try
368         {
369             return ( BootstrapProducer ) clazz.newInstance();
370         }
371         catch ( IllegalAccessException e )
372         {
373             NamingException ne = new NamingException( "Failed to create " + clazz );
374             ne.setRootCause( e );
375             throw ne;
376         }
377         catch ( InstantiationException e )
378         {
379             NamingException ne = new NamingException( "Failed to create " + clazz );
380             ne.setRootCause( e );
381             throw ne;
382         }
383     }
384 }