blob: 8dde0da2542828773dcb04bdbe6133dad7683973 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.schema.bootstrap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import javax.naming.NamingException;
import org.apache.directory.server.constants.MetaSchemaConstants;
import org.apache.directory.server.schema.bootstrap.SystemSchema;
import org.apache.directory.server.schema.bootstrap.BootstrapSchema;
import org.apache.directory.server.schema.bootstrap.ProducerTypeEnum;
import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapAttributeType;
import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapMatchingRule;
import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapObjectClass;
import org.apache.directory.server.schema.bootstrap.AbstractBootstrapProducer.BootstrapSyntax;
import org.apache.directory.server.schema.registries.AbstractSchemaLoader;
import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
import org.apache.directory.server.schema.registries.ComparatorRegistry;
import org.apache.directory.server.schema.registries.DefaultRegistries;
import org.apache.directory.server.schema.registries.MatchingRuleRegistry;
import org.apache.directory.server.schema.registries.NormalizerRegistry;
import org.apache.directory.server.schema.registries.ObjectClassRegistry;
import org.apache.directory.server.schema.registries.Registries;
import org.apache.directory.server.schema.registries.SyntaxCheckerRegistry;
import org.apache.directory.server.schema.registries.SyntaxRegistry;
import org.apache.directory.shared.ldap.schema.AttributeType;
import org.apache.directory.shared.ldap.schema.MatchingRule;
import org.apache.directory.shared.ldap.schema.Normalizer;
import org.apache.directory.shared.ldap.schema.ObjectClass;
import org.apache.directory.shared.ldap.schema.Syntax;
import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
import org.apache.directory.shared.ldap.schema.syntax.SyntaxChecker;
import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class which handles bootstrap schema class file loading.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class BootstrapSchemaLoader extends AbstractSchemaLoader
{
private static final Logger log = LoggerFactory.getLogger( BootstrapSchemaLoader.class );
private ClassLoader cl = getClass().getClassLoader();
/** stores schemas of producers for callback access */
private ThreadLocal<BootstrapSchema> schemas;
/** stores registries associated with producers for callback access */
private ThreadLocal<Registries> registries;
/** the callback that just calls register() */
private final ProducerCallback cb = new ProducerCallback()
{
public void schemaObjectProduced( BootstrapProducer producer, String registryKey, Object schemaObject )
throws NamingException
{
register( producer.getType(), registryKey, schemaObject );
}
};
/**
* Creates a BootstrapSchema loader.
*/
public BootstrapSchemaLoader()
{
schemas = new ThreadLocal<BootstrapSchema>();
registries = new ThreadLocal<Registries>();
}
public BootstrapSchemaLoader( ClassLoader cl )
{
this();
this.cl = cl;
}
public final void loadWithDependencies( Schema schema, Registries registries ) throws NamingException
{
if ( ! ( schema instanceof BootstrapSchema ) )
{
throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
}
Map<String, Schema> notLoaded = new HashMap<String, Schema>();
notLoaded.put( schema.getSchemaName(), schema );
Properties props = new Properties();
props.put( "package", ( ( BootstrapSchema ) schema ).getPackageName() );
loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
}
/**
* Loads a set of schemas by loading and running all producers for each
* dependent schema first.
*
* @param bootstrapSchemas Collection of {@link BootstrapSchema}s to load
* @param registries the registries to fill with producer created objects
* @throws NamingException if there are any failures during this process
*/
public final void loadWithDependencies( Collection<Schema> bootstrapSchemas, Registries registries ) throws NamingException
{
BootstrapSchema[] schemas = new BootstrapSchema[bootstrapSchemas.size()];
schemas = bootstrapSchemas.toArray( schemas );
HashMap<String,Schema> loaded = new HashMap<String,Schema>();
HashMap<String,Schema> notLoaded = new HashMap<String,Schema>();
for ( BootstrapSchema schema:schemas )
{
notLoaded.put( schema.getSchemaName(), schema );
}
BootstrapSchema schema;
// Create system schema and kick it off by loading system which
// will never depend on anything.
schema = new SystemSchema();
load( schema, registries, false );
notLoaded.remove( schema.getSchemaName() ); // Remove if user specified it.
loaded.put( schema.getSchemaName(), schema );
Iterator list = notLoaded.values().iterator();
while ( list.hasNext() )
{
schema = ( BootstrapSchema ) list.next();
Properties props = new Properties();
props.put( "package", schema.getPackageName() );
loadDepsFirst( schema, new Stack<String>(), notLoaded, schema, registries, props );
list = notLoaded.values().iterator();
}
}
/**
* Loads a schema by loading and running all producers for the schema.
*
* @param schema the schema to load
* @param registries the registries to fill with producer created objects
* @throws NamingException if there are any failures during this process
*/
public final void load( Schema schema, Registries registries, boolean isDepLoad ) throws NamingException
{
if ( registries.getLoadedSchemas().containsKey( schema.getSchemaName() ) )
{
return;
}
if ( ! ( schema instanceof BootstrapSchema ) )
{
throw new NamingException( "Expecting schema to be of sub-type BootstrapSchema" );
}
this.registries.set( registries );
this.schemas.set( ( BootstrapSchema ) schema );
for ( ProducerTypeEnum producerType:ProducerTypeEnum.getList() )
{
BootstrapProducer producer = getProducer( ( BootstrapSchema ) schema, producerType.getName() );
producer.produce( registries, cb );
}
notifyListenerOrRegistries( schema, registries );
}
// ------------------------------------------------------------------------
// Utility Methods
// ------------------------------------------------------------------------
/**
* Registers objects
*
* @param type the type of the producer which determines the type of object produced
* @param id the primary key identifying the created object in a registry
* @param schemaObject the object being registered
* @throws NamingException if there are problems when registering the object
* in any of the registries
*/
private void register( ProducerTypeEnum type, String id, Object schemaObject ) throws NamingException
{
BootstrapSchema schema = this.schemas.get();
DefaultRegistries registries = ( DefaultRegistries ) this.registries.get();
List<String> values = new ArrayList<String>(1);
values.add( schema.getSchemaName() );
switch ( type )
{
case NORMALIZER_PRODUCER :
Normalizer normalizer = ( Normalizer ) schemaObject;
NormalizerRegistry normalizerRegistry;
normalizerRegistry = registries.getNormalizerRegistry();
NormalizerDescription normalizerDescription = new NormalizerDescription();
normalizerDescription.setNumericOid( id );
normalizerDescription.setFqcn( normalizer.getClass().getName() );
normalizerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
normalizerRegistry.register( normalizerDescription, normalizer );
break;
case COMPARATOR_PRODUCER :
Comparator comparator = ( Comparator ) schemaObject;
ComparatorRegistry comparatorRegistry;
comparatorRegistry = registries.getComparatorRegistry();
ComparatorDescription comparatorDescription = new ComparatorDescription();
comparatorDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
comparatorDescription.setFqcn( comparator.getClass().getName() );
comparatorDescription.setNumericOid( id );
comparatorRegistry.register( comparatorDescription, comparator );
break;
case SYNTAX_CHECKER_PRODUCER :
SyntaxChecker syntaxChecker = ( SyntaxChecker ) schemaObject;
SyntaxCheckerRegistry syntaxCheckerRegistry;
syntaxCheckerRegistry = registries.getSyntaxCheckerRegistry();
SyntaxCheckerDescription syntaxCheckerDescription = new SyntaxCheckerDescription();
syntaxCheckerDescription.addExtension( MetaSchemaConstants.X_SCHEMA, values );
syntaxCheckerDescription.setFqcn( syntaxChecker.getClass().getName() );
syntaxCheckerDescription.setNumericOid( id );
syntaxCheckerRegistry.register( syntaxCheckerDescription, syntaxChecker );
break;
case SYNTAX_PRODUCER :
Syntax syntax = ( Syntax ) schemaObject;
if ( schemaObject instanceof BootstrapSyntax )
{
( ( BootstrapSyntax ) syntax ).setSchema( schema.getSchemaName() );
}
SyntaxRegistry syntaxRegistry = registries.getSyntaxRegistry();
syntaxRegistry.register( syntax );
break;
case MATCHING_RULE_PRODUCER :
MatchingRule matchingRule = ( MatchingRule ) schemaObject;
if ( schemaObject instanceof BootstrapMatchingRule )
{
( ( BootstrapMatchingRule ) matchingRule ).setSchema( schema.getSchemaName() );
}
MatchingRuleRegistry matchingRuleRegistry;
matchingRuleRegistry = registries.getMatchingRuleRegistry();
matchingRuleRegistry.register( matchingRule );
break;
case ATTRIBUTE_TYPE_PRODUCER :
AttributeType attributeType = ( AttributeType ) schemaObject;
if ( attributeType instanceof BootstrapAttributeType )
{
( ( BootstrapAttributeType ) attributeType ).setSchema( schema.getSchemaName() );
}
AttributeTypeRegistry attributeTypeRegistry;
attributeTypeRegistry = registries.getAttributeTypeRegistry();
attributeTypeRegistry.register( attributeType );
break;
case OBJECT_CLASS_PRODUCER :
ObjectClass objectClass = ( ObjectClass ) schemaObject;
if ( objectClass instanceof BootstrapObjectClass )
{
( ( BootstrapObjectClass ) objectClass ).setSchema( schema.getSchemaName() );
}
ObjectClassRegistry objectClassRegistry;
objectClassRegistry = registries.getObjectClassRegistry();
objectClassRegistry.register( objectClass );
break;
default:
throw new IllegalStateException( "ProducerTypeEnum value is invalid: " + type );
}
}
/**
* Attempts first to try to load the target class for the Producer,
* then tries for the default if the target load fails.
*
* @param schema the bootstrap schema
* @param producerBase the producer's base name
* @throws NamingException if there are failures loading classes
*/
private BootstrapProducer getProducer( BootstrapSchema schema, String producerBase ) throws NamingException
{
Class<?> clazz = null;
boolean failedTargetLoad = false;
String defaultClassName;
String targetClassName = schema.getBaseClassName() + producerBase;
try
{
clazz = Class.forName( targetClassName, true, cl );
}
catch ( ClassNotFoundException e )
{
failedTargetLoad = true;
log.debug( "Failed to load '" + targetClassName + "'. Trying the alternative.", e );
}
if ( failedTargetLoad )
{
defaultClassName = schema.getDefaultBaseClassName() + producerBase;
try
{
clazz = Class.forName( defaultClassName, true, cl );
}
catch ( ClassNotFoundException e )
{
NamingException ne = new NamingException( "Failed to load " + producerBase + " for "
+ schema.getSchemaName() + " schema using following classes: " + targetClassName + ", "
+ defaultClassName );
ne.setRootCause( e );
throw ne;
}
}
try
{
return ( BootstrapProducer ) clazz.newInstance();
}
catch ( IllegalAccessException e )
{
NamingException ne = new NamingException( "Failed to create " + clazz );
ne.setRootCause( e );
throw ne;
}
catch ( InstantiationException e )
{
NamingException ne = new NamingException( "Failed to create " + clazz );
ne.setRootCause( e );
throw ne;
}
}
public Schema getSchema( String schemaName ) throws NamingException
{
return getSchema( schemaName, null );
}
public Schema getSchema( String schemaName, Properties schemaProperties ) throws NamingException
{
String baseName = schemaName;
schemaName = schemaName.toLowerCase();
StringBuffer buf = new StringBuffer();
if ( schemaProperties == null || schemaProperties.getProperty( "package" ) == null )
{
// first see if we can load a schema object using the default bootstrap package
Properties props = new Properties();
props.put( "package", "org.apache.directory.server.schema.bootstrap" );
try
{
Schema schema = getSchema( baseName, props );
return schema;
}
catch( NamingException e )
{
throw new NamingException( "Can't find the bootstrap schema class in the default " +
"\n bootstrap schema package. I need a package name property with key \"package\"." );
}
}
buf.append( schemaProperties.getProperty( "package" ) );
buf.append( '.' );
buf.append( Character.toUpperCase( schemaName.charAt( 0 ) ) );
buf.append( schemaName.substring( 1 ) );
schemaName = buf.toString();
Schema schema = null;
try
{
schema = ( Schema ) Class.forName( schemaName, true, cl ).newInstance();
}
catch ( InstantiationException e )
{
NamingException ne = new NamingException( "Failed to instantiate schema object: " + schemaName );
ne.setRootCause( e );
throw ne;
}
catch ( IllegalAccessException e )
{
NamingException ne =
new NamingException( "Failed to access default constructor of schema object: " + schemaName );
ne.setRootCause( e );
throw ne;
}
catch ( ClassNotFoundException e )
{
NamingException ne = new NamingException( "Schema class not found: " + schemaName );
ne.setRootCause( e );
throw ne;
}
return schema;
}
}