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.server.schema.bootstrap;
21  
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Comparator;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.Stack;
32  
33  import javax.naming.NamingException;
34  
35  import org.apache.directory.server.constants.MetaSchemaConstants;
36  import org.apache.directory.server.schema.bootstrap.SystemSchema;
37  import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
38  import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
39  import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapAttributeType;
40  import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapMatchingRule;
41  import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapObjectClass;
42  import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapSyntax;
43  import org.apache.directory.server.schema.registries.AbstractSchemaLoader;
44  import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
45  import org.apache.directory.server.schema.registries.ComparatorRegistry;
46  import org.apache.directory.server.schema.registries.DefaultRegistries;
47  import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
48  import org.apache.directory.server.schema.registries.NormalizerRegistry;
49  import org.apache.directory.server.schema.registries.ObjectClassRegistry;
50  import org.apache.directory.server.schema.registries.Registries;
51  import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
52  import org.apache.directory.server.schema.registries.SyntaxRegistry;
53  import org.apache.directory.shared.ldap.schema.AttributeType;
54  import org.apache.directory.shared.ldap.schema.MatchingRule;
55  import org.apache.directory.shared.ldap.schema.Normalizer;
56  import org.apache.directory.shared.ldap.schema.ObjectClass;
57  import org.apache.directory.shared.ldap.schema.Syntax;
58  import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
59  import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
60  import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
61  import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
62  
63  import org.slf4j.Logger;
64  import org.slf4j.LoggerFactory;
65  
66  
67  /**
68   * Class which handles bootstrap schema class file loading.
69   *
70   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
71   * @version $Rev: 658889 $
72   */
73  public class BootstrapSchemaLoader extends AbstractSchemaLoader
74  {
75      private static final Logger log = LoggerFactory.getLogger( BootstrapSchemaLoader.class );
76  
77      private ClassLoader cl = getClass().getClassLoader();
78  
79      /** stores schemas of producers for callback access */
80      private ThreadLocal<BootstrapSchema> schemas;
81      /** stores registries associated with producers for callback access */
82      private ThreadLocal<Registries> registries;
83      /** the callback that just calls register() */
84      private final ProducerCallback cb = new ProducerCallback()
85      {
86          public void schemaObjectProduced( BootstrapProducer producer, String registryKey, Object schemaObject )
87              throws NamingException
88          {
89              register( producer.getType(), registryKey, schemaObject );
90          }
91      };
92  
93  
94      /**
95       * Creates a BootstrapSchema loader.
96       */
97      public BootstrapSchemaLoader()
98      {
99          schemas = new ThreadLocal<BootstrapSchema>();
100         registries = new ThreadLocal<Registries>();
101     }
102 
103 
104     public BootstrapSchemaLoader( ClassLoader cl )
105     {
106         this();
107         this.cl = cl;
108     }
109 
110     public final void loadWithDependencies( Schema schema, Registries registries ) throws Exception
111     {
112         if ( ! ( schema instanceof BootstrapSchema ) )
113         {
114             throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
115         }
116         
117         Map<String, Schema> notLoaded = new HashMap<String, Schema>();
118         notLoaded.put( schema.getSchemaName(), schema );
119         Properties props = new Properties();
120         props.put( "package", ( ( BootstrapSchema ) schema ).getPackageName() );
121         loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
122     }
123 
124     
125     /**
126      * Loads a set of schemas by loading and running all producers for each
127      * dependent schema first.
128      *
129      * @param bootstrapSchemas Collection of {@link BootstrapSchema}s to load
130      * @param registries the registries to fill with producer created objects
131      * @throws NamingException if there are any failures during this process
132      */
133     public final void loadWithDependencies( Collection<Schema> bootstrapSchemas, Registries registries ) throws Exception
134     {
135         BootstrapSchema[] schemas = new BootstrapSchema[bootstrapSchemas.size()];
136         schemas = bootstrapSchemas.toArray( schemas );
137         HashMap<String,Schema> loaded = new HashMap<String,Schema>();
138         HashMap<String,Schema> notLoaded = new HashMap<String,Schema>();
139 
140         for ( BootstrapSchema schema:schemas )
141         {
142             notLoaded.put( schema.getSchemaName(), schema );
143         }
144 
145         BootstrapSchema schema;
146 
147         // Create system schema and kick it off by loading system which
148         // will never depend on anything.
149         schema = new SystemSchema();
150         load( schema, registries, false );
151         notLoaded.remove( schema.getSchemaName() ); // Remove if user specified it.
152         loaded.put( schema.getSchemaName(), schema );
153 
154         Iterator list = notLoaded.values().iterator();
155         while ( list.hasNext() )
156         {
157             schema = ( BootstrapSchema ) list.next();
158             Properties props = new Properties();
159             props.put( "package", schema.getPackageName() );
160             loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
161             list = notLoaded.values().iterator();
162         }
163     }
164 
165 
166     /**
167      * Loads a schema by loading and running all producers for the schema.
168      *
169      * @param schema the schema to load
170      * @param registries the registries to fill with producer created objects
171      * @throws NamingException if there are any failures during this process
172      */
173     public final void load( Schema schema, Registries registries, boolean isDepLoad ) throws NamingException
174     {
175         if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
176         {
177             return;
178         }
179         
180         if ( ! ( schema instanceof BootstrapSchema ) )
181         {
182             throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
183         }
184         
185         this.registries.set( registries );
186         this.schemas.set( ( BootstrapSchema ) schema );
187 
188         for ( ProducerTypeEnum producerType:ProducerTypeEnum.getList() )
189         {
190             BootstrapProducer producer = getProducer( ( BootstrapSchema ) schema, producerType.getName() );
191             producer.produce( registries, cb );
192         }
193 
194         notifyListenerOrRegistries( schema, registries );
195     }
196 
197 
198     // ------------------------------------------------------------------------
199     // Utility Methods
200     // ------------------------------------------------------------------------
201 
202     /**
203      * Registers objects
204      *
205      * @param type the type of the producer which determines the type of object produced
206      * @param id the primary key identifying the created object in a registry
207      * @param schemaObject the object being registered
208      * @throws NamingException if there are problems when registering the object
209      * in any of the registries
210      */
211     private void register( ProducerTypeEnum type, String id, Object schemaObject ) throws NamingException
212     {
213         BootstrapSchema schema = this.schemas.get();
214         DefaultRegistries registries = ( DefaultRegistries ) this.registries.get();
215         List<String> values = new ArrayList<String>(1);
216         values.add( schema.getSchemaName() );
217 
218         switch ( type )
219         {
220             case NORMALIZER_PRODUCER :
221                 Normalizer normalizer = ( Normalizer ) schemaObject;
222                 NormalizerRegistry normalizerRegistry;
223                 normalizerRegistry = registries.getNormalizerRegistry();
224                 
225                 NormalizerDescription normalizerDescription = new NormalizerDescription();
226                 normalizerDescription.setNumericOid( id );
227                 normalizerDescription.setFqcn( normalizer.getClass().getName() );
228                 normalizerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
229                 
230                 normalizerRegistry.register( normalizerDescription, normalizer );
231                 break;
232                 
233             case COMPARATOR_PRODUCER :
234                 Comparator comparator = ( Comparator ) schemaObject;
235                 ComparatorRegistry comparatorRegistry;
236                 comparatorRegistry = registries.getComparatorRegistry();
237                 
238                 ComparatorDescription comparatorDescription = new ComparatorDescription();
239                 comparatorDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
240                 comparatorDescription.setFqcn( comparator.getClass().getName() );
241                 comparatorDescription.setNumericOid( id );
242                 
243                 comparatorRegistry.register( comparatorDescription, comparator );
244                 break;
245                 
246             case SYNTAX_CHECKER_PRODUCER :
247                 SyntaxChecker syntaxChecker = ( SyntaxChecker ) schemaObject;
248                 SyntaxCheckerRegistry syntaxCheckerRegistry;
249                 syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
250                 
251                 SyntaxCheckerDescription syntaxCheckerDescription = new SyntaxCheckerDescription();
252                 syntaxCheckerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
253                 syntaxCheckerDescription.setFqcn( syntaxChecker.getClass().getName() );
254                 syntaxCheckerDescription.setNumericOid( id );
255                 
256                 syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
257                 break;
258                 
259             case SYNTAX_PRODUCER :
260                 Syntax syntax = ( Syntax ) schemaObject;
261                 
262                 if ( schemaObject instanceof BootstrapSyntax )
263                 {
264                     ( ( BootstrapSyntax ) syntax ).setSchema( schema.getSchemaName() );
265                 }
266 
267                 SyntaxRegistry syntaxRegistry = registries.getSyntaxRegistry();
268                 syntaxRegistry.register( syntax );
269                 break;
270                 
271             case MATCHING_RULE_PRODUCER :
272                 MatchingRule matchingRule = ( MatchingRule ) schemaObject;
273                 
274                 if ( schemaObject instanceof BootstrapMatchingRule )
275                 {
276                     ( ( BootstrapMatchingRule ) matchingRule ).setSchema( schema.getSchemaName() );
277                 }
278 
279                 MatchingRuleRegistry matchingRuleRegistry;
280                 matchingRuleRegistry = registries.getMatchingRuleRegistry();
281                 matchingRuleRegistry.register( matchingRule );
282                 break;
283                 
284             case ATTRIBUTE_TYPE_PRODUCER :
285                 AttributeType attributeType = ( AttributeType ) schemaObject;
286                 
287                 if ( attributeType instanceof BootstrapAttributeType )
288                 {
289                     ( ( BootstrapAttributeType ) attributeType ).setSchema( schema.getSchemaName() );
290                 }
291                 
292                 AttributeTypeRegistry attributeTypeRegistry;
293                 attributeTypeRegistry = registries.getAttributeTypeRegistry();
294                 attributeTypeRegistry.register( attributeType );
295                 break;
296                 
297             case OBJECT_CLASS_PRODUCER :
298                 ObjectClass objectClass = ( ObjectClass ) schemaObject;
299                 
300                 if ( objectClass instanceof BootstrapObjectClass )
301                 {
302                     ( ( BootstrapObjectClass ) objectClass ).setSchema( schema.getSchemaName() );
303                 }
304                 
305                 ObjectClassRegistry objectClassRegistry;
306                 objectClassRegistry = registries.getObjectClassRegistry();
307                 objectClassRegistry.register( objectClass );
308                 break;
309                 
310             default:
311                 throw new IllegalStateException( "ProducerTypeEnum value is invalid: " + type );
312         }
313     }
314 
315 
316     /**
317      * Attempts first to try to load the target class for the Producer,
318      * then tries for the default if the target load fails.
319      *
320      * @param schema the bootstrap schema
321      * @param producerBase the producer's base name
322      * @throws NamingException if there are failures loading classes
323      */
324     private BootstrapProducer getProducer( BootstrapSchema schema, String producerBase ) throws NamingException
325     {
326         Class<?> clazz = null;
327         boolean failedTargetLoad = false;
328         String defaultClassName;
329         String targetClassName = schema.getBaseClassName() + producerBase;
330 
331         try
332         {
333             clazz = Class.forName( targetClassName, true, cl );
334         }
335         catch ( ClassNotFoundException e )
336         {
337             failedTargetLoad = true;
338             log.debug( "Failed to load '" + targetClassName + "'.  Trying the alternative.", e );
339         }
340 
341         if ( failedTargetLoad )
342         {
343             defaultClassName = schema.getDefaultBaseClassName() + producerBase;
344 
345             try
346             {
347                 clazz = Class.forName( defaultClassName, true, cl );
348             }
349             catch ( ClassNotFoundException e )
350             {
351                 NamingException ne = new NamingException( "Failed to load " + producerBase + " for "
352                     + schema.getSchemaName() + " schema using following classes: " + targetClassName + ", "
353                     + defaultClassName );
354                 ne.setRootCause( e );
355                 throw ne;
356             }
357         }
358 
359         try
360         {
361             return ( BootstrapProducer ) clazz.newInstance();
362         }
363         catch ( IllegalAccessException e )
364         {
365             NamingException ne = new NamingException( "Failed to create " + clazz );
366             ne.setRootCause( e );
367             throw ne;
368         }
369         catch ( InstantiationException e )
370         {
371             NamingException ne = new NamingException( "Failed to create " + clazz );
372             ne.setRootCause( e );
373             throw ne;
374         }
375     }
376 
377 
378     public Schema getSchema( String schemaName ) throws NamingException
379     {
380         return getSchema( schemaName, null );
381     }
382     
383     
384     public Schema getSchema( String schemaName, Properties schemaProperties ) throws NamingException
385     {
386         String baseName = schemaName;
387         schemaName = schemaName.toLowerCase();
388         StringBuffer buf = new StringBuffer();
389 
390         
391         if ( schemaProperties == null || schemaProperties.getProperty( "package" ) == null )
392         {
393             // first see if we can load a schema object using the default bootstrap package
394             Properties props = new Properties();
395             props.put( "package", "org.apache.directory.server.schema.bootstrap" );
396             
397             try
398             {
399                 Schema schema = getSchema( baseName, props );
400                 return schema;
401             }
402             catch( NamingException e )
403             {
404                 throw new NamingException( "Can't find the bootstrap schema class in the default " +
405                         "\n bootstrap schema package.  I need a package name property with key \"package\"." );
406             }
407         }
408         
409         buf.append( schemaProperties.getProperty( "package" ) );
410         buf.append( '.' );
411         buf.append( Character.toUpperCase( schemaName.charAt( 0 ) ) );
412         buf.append( schemaName.substring( 1 ) );
413         schemaName = buf.toString();
414         
415         Schema schema = null;
416         try
417         {
418             schema = ( Schema ) Class.forName( schemaName, true, cl ).newInstance();
419         }
420         catch ( InstantiationException e )
421         {
422             NamingException ne = new NamingException( "Failed to instantiate schema object: " + schemaName );
423             ne.setRootCause( e );
424             throw ne;
425         }
426         catch ( IllegalAccessException e )
427         {
428             NamingException ne = 
429                 new NamingException( "Failed to access default constructor of schema object: " + schemaName );
430             ne.setRootCause( e );
431             throw ne;
432         }
433         catch ( ClassNotFoundException e )
434         {
435             NamingException ne = new NamingException( "Schema class not found: " + schemaName );
436             ne.setRootCause( e );
437             throw ne;
438         }
439         
440         return schema;
441     }
442 }