001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.schema.bootstrap;
021
022
023 import java.util.ArrayList;
024 import java.util.Collection;
025 import java.util.Comparator;
026 import java.util.HashMap;
027 import java.util.Iterator;
028 import java.util.List;
029 import java.util.Map;
030 import java.util.Properties;
031 import java.util.Stack;
032
033 import javax.naming.NamingException;
034
035 import org.apache.directory.server.constants.MetaSchemaConstants;
036 import org.apache.directory.server.schema.bootstrap.SystemSchema;
037 import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
038 import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
039 import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapAttributeType;
040 import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapMatchingRule;
041 import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapObjectClass;
042 import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapSyntax;
043 import org.apache.directory.server.schema.registries.AbstractSchemaLoader;
044 import org.apache.directory.server.schema.registries.ComparatorRegistry;
045 import org.apache.directory.server.schema.registries.DefaultRegistries;
046 import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
047 import org.apache.directory.server.schema.registries.NormalizerRegistry;
048 import org.apache.directory.server.schema.registries.ObjectClassRegistry;
049 import org.apache.directory.server.schema.registries.Registries;
050 import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
051 import org.apache.directory.server.schema.registries.SyntaxRegistry;
052 import org.apache.directory.shared.ldap.schema.AttributeType;
053 import org.apache.directory.shared.ldap.schema.MatchingRule;
054 import org.apache.directory.shared.ldap.schema.Normalizer;
055 import org.apache.directory.shared.ldap.schema.ObjectClass;
056 import org.apache.directory.shared.ldap.schema.Syntax;
057 import org.apache.directory.shared.ldap.schema.SyntaxChecker;
058 import org.apache.directory.shared.ldap.schema.parsers.ComparatorDescription;
059 import org.apache.directory.shared.ldap.schema.parsers.NormalizerDescription;
060 import org.apache.directory.shared.ldap.schema.parsers.SyntaxCheckerDescription;
061
062 import org.slf4j.Logger;
063 import org.slf4j.LoggerFactory;
064
065
066 /**
067 * Class which handles bootstrap schema class file loading.
068 *
069 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
070 * @version $Rev: 780471 $
071 */
072 public class BootstrapSchemaLoader extends AbstractSchemaLoader
073 {
074 private static final Logger log = LoggerFactory.getLogger( BootstrapSchemaLoader.class );
075
076 private ClassLoader cl = getClass().getClassLoader();
077
078 /** stores schemas of producers for callback access */
079 private ThreadLocal<BootstrapSchema> schemas;
080
081 /** stores registries associated with producers for callback access */
082 private ThreadLocal<Registries> registries;
083
084 /** the callback that just calls register() */
085 private final ProducerCallback cb = new ProducerCallback()
086 {
087 public void schemaObjectProduced( BootstrapProducer producer, String registryKey, Object schemaObject )
088 throws NamingException
089 {
090 register( producer.getType(), registryKey, schemaObject );
091 }
092 };
093
094
095 /**
096 * Creates a BootstrapSchema loader.
097 */
098 public BootstrapSchemaLoader()
099 {
100 schemas = new ThreadLocal<BootstrapSchema>();
101 registries = new ThreadLocal<Registries>();
102 }
103
104
105 public BootstrapSchemaLoader( ClassLoader cl )
106 {
107 this();
108 this.cl = cl;
109 }
110
111 public final void loadWithDependencies( Schema schema, Registries registries ) throws Exception
112 {
113 if ( ! ( schema instanceof BootstrapSchema ) )
114 {
115 throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
116 }
117
118 Map<String, Schema> notLoaded = new HashMap<String, Schema>();
119 notLoaded.put( schema.getSchemaName(), schema );
120 Properties props = new Properties();
121 props.put( "package", ( ( BootstrapSchema ) schema ).getPackageName() );
122 loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
123 }
124
125
126 /**
127 * Loads a set of schemas by loading and running all producers for each
128 * dependent schema first.
129 *
130 * @param bootstrapSchemas Collection of {@link BootstrapSchema}s to load
131 * @param registries the registries to fill with producer created objects
132 * @throws NamingException if there are any failures during this process
133 */
134 public final void loadWithDependencies( Collection<Schema> bootstrapSchemas, Registries registries ) throws Exception
135 {
136 BootstrapSchema[] schemas = new BootstrapSchema[bootstrapSchemas.size()];
137 schemas = bootstrapSchemas.toArray( schemas );
138 HashMap<String,Schema> loaded = new HashMap<String,Schema>();
139 HashMap<String,Schema> notLoaded = new HashMap<String,Schema>();
140
141 for ( BootstrapSchema schema:schemas )
142 {
143 notLoaded.put( schema.getSchemaName(), schema );
144 }
145
146 BootstrapSchema schema;
147
148 // Create system schema and kick it off by loading system which
149 // will never depend on anything.
150 schema = new SystemSchema();
151 load( schema, registries, false );
152 notLoaded.remove( schema.getSchemaName() ); // Remove if user specified it.
153 loaded.put( schema.getSchemaName(), schema );
154
155 Iterator<Schema> list = notLoaded.values().iterator();
156
157 while ( list.hasNext() )
158 {
159 schema = ( BootstrapSchema ) list.next();
160 Properties props = new Properties();
161 props.put( "package", schema.getPackageName() );
162 loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
163 list = notLoaded.values().iterator();
164 }
165 }
166
167
168 /**
169 * Loads a schema by loading and running all producers for the schema.
170 *
171 * @param schema the schema to load
172 * @param registries the registries to fill with producer created objects
173 * @throws NamingException if there are any failures during this process
174 */
175 public final void load( Schema schema, Registries registries, boolean isDepLoad ) throws NamingException
176 {
177 if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
178 {
179 return;
180 }
181
182 if ( ! ( schema instanceof BootstrapSchema ) )
183 {
184 throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
185 }
186
187 this.registries.set( registries );
188 this.schemas.set( ( BootstrapSchema ) schema );
189
190 try
191 {
192 for ( ProducerTypeEnum producerType:ProducerTypeEnum.getList() )
193 {
194 BootstrapProducer producer = getProducer( ( BootstrapSchema ) schema, producerType.getName() );
195 producer.produce( registries, cb );
196 }
197 }
198 finally
199 {
200 // Don't forget to release the ThreadLocal variables when done !
201 this.registries.set( null );
202 this.schemas.set( null );
203 }
204
205 notifyListenerOrRegistries( schema, registries );
206 }
207
208
209 // ------------------------------------------------------------------------
210 // Utility Methods
211 // ------------------------------------------------------------------------
212
213 /**
214 * Registers objects
215 *
216 * @param type the type of the producer which determines the type of object produced
217 * @param id the primary key identifying the created object in a registry
218 * @param schemaObject the object being registered
219 * @throws NamingException if there are problems when registering the object
220 * in any of the registries
221 */
222 private void register( ProducerTypeEnum type, String id, Object schemaObject ) throws NamingException
223 {
224 BootstrapSchema schema = this.schemas.get();
225 DefaultRegistries registries = ( DefaultRegistries ) this.registries.get();
226 List<String> values = new ArrayList<String>(1);
227 values.add( schema.getSchemaName() );
228
229 switch ( type )
230 {
231 case NORMALIZER_PRODUCER :
232 Normalizer normalizer = ( Normalizer ) schemaObject;
233 NormalizerRegistry normalizerRegistry;
234 normalizerRegistry = registries.getNormalizerRegistry();
235
236 NormalizerDescription normalizerDescription = new NormalizerDescription();
237 normalizerDescription.setNumericOid( id );
238 normalizerDescription.setFqcn( normalizer.getClass().getName() );
239 normalizerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
240
241 normalizerRegistry.register( normalizerDescription, normalizer );
242 break;
243
244 case COMPARATOR_PRODUCER :
245 Comparator comparator = ( Comparator ) schemaObject;
246 ComparatorRegistry comparatorRegistry;
247 comparatorRegistry = registries.getComparatorRegistry();
248
249 ComparatorDescription comparatorDescription = new ComparatorDescription();
250 comparatorDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
251 comparatorDescription.setFqcn( comparator.getClass().getName() );
252 comparatorDescription.setNumericOid( id );
253
254 comparatorRegistry.register( comparatorDescription, comparator );
255 break;
256
257 case SYNTAX_CHECKER_PRODUCER :
258 SyntaxChecker syntaxChecker = ( SyntaxChecker ) schemaObject;
259 SyntaxCheckerRegistry syntaxCheckerRegistry;
260 syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
261
262 SyntaxCheckerDescription syntaxCheckerDescription = new SyntaxCheckerDescription();
263 syntaxCheckerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
264 syntaxCheckerDescription.setFqcn( syntaxChecker.getClass().getName() );
265 syntaxCheckerDescription.setNumericOid( id );
266
267 syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
268 break;
269
270 case SYNTAX_PRODUCER :
271 Syntax syntax = ( Syntax ) schemaObject;
272
273 if ( schemaObject instanceof BootstrapSyntax )
274 {
275 ( ( BootstrapSyntax ) syntax ).setSchema( schema.getSchemaName() );
276 }
277
278 SyntaxRegistry syntaxRegistry = registries.getSyntaxRegistry();
279 syntaxRegistry.register( syntax );
280 break;
281
282 case MATCHING_RULE_PRODUCER :
283 MatchingRule matchingRule = ( MatchingRule ) schemaObject;
284
285 if ( schemaObject instanceof BootstrapMatchingRule )
286 {
287 ( ( BootstrapMatchingRule ) matchingRule ).setSchema( schema.getSchemaName() );
288 }
289
290 MatchingRuleRegistry matchingRuleRegistry;
291 matchingRuleRegistry = registries.getMatchingRuleRegistry();
292 matchingRuleRegistry.register( matchingRule );
293 break;
294
295 case ATTRIBUTE_TYPE_PRODUCER :
296 AttributeType attributeType = ( AttributeType ) schemaObject;
297
298 if ( attributeType instanceof BootstrapAttributeType )
299 {
300 ( ( BootstrapAttributeType ) attributeType ).setSchema( schema.getSchemaName() );
301 }
302
303 registries.getAttributeTypeRegistry().register( attributeType );
304 break;
305
306 case OBJECT_CLASS_PRODUCER :
307 ObjectClass objectClass = ( ObjectClass ) schemaObject;
308
309 if ( objectClass instanceof BootstrapObjectClass )
310 {
311 ( ( BootstrapObjectClass ) objectClass ).setSchema( schema.getSchemaName() );
312 }
313
314 ObjectClassRegistry objectClassRegistry;
315 objectClassRegistry = registries.getObjectClassRegistry();
316 objectClassRegistry.register( objectClass );
317 break;
318
319 default:
320 throw new IllegalStateException( "ProducerTypeEnum value is invalid: " + type );
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 ) throws NamingException
334 {
335 Class<?> clazz = null;
336 boolean failedTargetLoad = false;
337 String defaultClassName;
338 String targetClassName = schema.getBaseClassName() + producerBase;
339
340 try
341 {
342 clazz = Class.forName( targetClassName, true, cl );
343 }
344 catch ( ClassNotFoundException e )
345 {
346 failedTargetLoad = true;
347 log.debug( "Failed to load '" + targetClassName + "'. Trying the alternative.", e );
348 }
349
350 if ( failedTargetLoad )
351 {
352 defaultClassName = schema.getDefaultBaseClassName() + producerBase;
353
354 try
355 {
356 clazz = Class.forName( defaultClassName, true, cl );
357 }
358 catch ( ClassNotFoundException e )
359 {
360 NamingException ne = new NamingException( "Failed to load " + producerBase + " for "
361 + schema.getSchemaName() + " schema using following classes: " + targetClassName + ", "
362 + defaultClassName );
363 ne.setRootCause( e );
364 throw ne;
365 }
366 }
367
368 try
369 {
370 return ( BootstrapProducer ) clazz.newInstance();
371 }
372 catch ( IllegalAccessException e )
373 {
374 NamingException ne = new NamingException( "Failed to create " + clazz );
375 ne.setRootCause( e );
376 throw ne;
377 }
378 catch ( InstantiationException e )
379 {
380 NamingException ne = new NamingException( "Failed to create " + clazz );
381 ne.setRootCause( e );
382 throw ne;
383 }
384 }
385
386
387 public Schema getSchema( String schemaName ) throws NamingException
388 {
389 return getSchema( schemaName, null );
390 }
391
392
393 public Schema getSchema( String schemaName, Properties schemaProperties ) throws NamingException
394 {
395 String baseName = schemaName;
396 schemaName = schemaName.toLowerCase();
397 StringBuffer buf = new StringBuffer();
398
399
400 if ( schemaProperties == null || schemaProperties.getProperty( "package" ) == null )
401 {
402 // first see if we can load a schema object using the default bootstrap package
403 Properties props = new Properties();
404 props.put( "package", "org.apache.directory.server.schema.bootstrap" );
405
406 try
407 {
408 Schema schema = getSchema( baseName, props );
409 return schema;
410 }
411 catch( NamingException e )
412 {
413 throw new NamingException( "Can't find the bootstrap schema class in the default " +
414 "\n bootstrap schema package. I need a package name property with key \"package\"." );
415 }
416 }
417
418 buf.append( schemaProperties.getProperty( "package" ) );
419 buf.append( '.' );
420 buf.append( Character.toUpperCase( schemaName.charAt( 0 ) ) );
421 buf.append( schemaName.substring( 1 ) );
422 schemaName = buf.toString();
423
424 Schema schema = null;
425 try
426 {
427 schema = ( Schema ) Class.forName( schemaName, true, cl ).newInstance();
428 }
429 catch ( InstantiationException e )
430 {
431 NamingException ne = new NamingException( "Failed to instantiate schema object: " + schemaName );
432 ne.setRootCause( e );
433 throw ne;
434 }
435 catch ( IllegalAccessException e )
436 {
437 NamingException ne =
438 new NamingException( "Failed to access default constructor of schema object: " + schemaName );
439 ne.setRootCause( e );
440 throw ne;
441 }
442 catch ( ClassNotFoundException e )
443 {
444 NamingException ne = new NamingException( "Schema class not found: " + schemaName );
445 ne.setRootCause( e );
446 throw ne;
447 }
448
449 return schema;
450 }
451 }