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