blob: 1af49fab5373fc0d5a0927a2ccd07898dcbcf142 [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.api.ldap.model.schema.registries;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.directory.api.asn1.util.Oid;
import org.apache.directory.api.i18n.I18n;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject;
import org.apache.directory.api.ldap.model.schema.SchemaObject;
import org.apache.directory.api.ldap.model.schema.SchemaObjectType;
import org.apache.directory.api.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Common schema object registry interface.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public abstract class DefaultSchemaObjectRegistry<T extends SchemaObject> implements SchemaObjectRegistry<T>,
Iterable<T>
{
/** static class logger */
private static final Logger LOG = LoggerFactory.getLogger( DefaultSchemaObjectRegistry.class );
/** A speedup for debug */
private static final boolean DEBUG = LOG.isDebugEnabled();
/** a map of SchemaObject looked up by name */
protected Map<String, T> byName;
/** The SchemaObject type, used by the toString() method */
protected SchemaObjectType schemaObjectType;
/** the global OID Registry */
protected OidRegistry<T> oidRegistry;
/** A flag indicating that the Registry is relaxed or not */
private boolean isRelaxed;
/**
* Creates a new DefaultSchemaObjectRegistry instance.
*/
protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry<T> oidRegistry )
{
byName = new HashMap<String, T>();
this.schemaObjectType = schemaObjectType;
this.oidRegistry = oidRegistry;
this.isRelaxed = Registries.STRICT;
}
/**
* Tells if the Registry is permissive or if it must be checked
* against inconsistencies.
*
* @return True if SchemaObjects can be added even if they break the consistency
*/
public boolean isRelaxed()
{
return isRelaxed;
}
/**
* Tells if the Registry is strict.
*
* @return True if SchemaObjects cannot be added if they break the consistency
*/
public boolean isStrict()
{
return !isRelaxed;
}
/**
* Change the Registry to a relaxed mode, where invalid SchemaObjects
* can be registered.
*/
public void setRelaxed()
{
isRelaxed = Registries.RELAXED;
oidRegistry.setRelaxed();
}
/**
* Change the Registry to a strict mode, where invalid SchemaObjects
* cannot be registered.
*/
public void setStrict()
{
isRelaxed = Registries.STRICT;
oidRegistry.setStrict();
}
/**
* {@inheritDoc}
*/
public boolean contains( String oid )
{
if ( !byName.containsKey( oid ) )
{
return byName.containsKey( Strings.toLowerCase( oid ) );
}
return true;
}
/**
* {@inheritDoc}
*/
public String getSchemaName( String oid ) throws LdapException
{
if ( !Oid.isOid( oid ) )
{
String msg = I18n.err( I18n.ERR_04267 );
LOG.warn( msg );
throw new LdapException( msg );
}
SchemaObject schemaObject = byName.get( oid );
if ( schemaObject != null )
{
return schemaObject.getSchemaName();
}
String msg = I18n.err( I18n.ERR_04268_OID_NOT_FOUND, oid );
LOG.warn( msg );
throw new LdapException( msg );
}
/**
* {@inheritDoc}
*/
public void renameSchema( String originalSchemaName, String newSchemaName )
{
// Loop on all the SchemaObjects stored and remove those associated
// with the give schemaName
for ( T schemaObject : this )
{
if ( originalSchemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
{
schemaObject.setSchemaName( newSchemaName );
if ( DEBUG )
{
LOG.debug( "Renamed {} schemaName to {}", schemaObject, newSchemaName );
}
}
}
}
/**
* {@inheritDoc}
*/
public Iterator<T> iterator()
{
return oidRegistry.iterator();
}
/**
* {@inheritDoc}
*/
public Iterator<String> oidsIterator()
{
return byName.keySet().iterator();
}
/**
* {@inheritDoc}
*/
public T lookup( String oid ) throws LdapException
{
if ( oid == null )
{
return null;
}
T schemaObject = byName.get( oid );
if ( schemaObject == null )
{
// let's try with trimming and lowercasing now
schemaObject = byName.get( Strings.trim( Strings.toLowerCase( oid ) ) );
}
if ( schemaObject == null )
{
String msg = I18n.err( I18n.ERR_04269, schemaObjectType.name(), oid );
LOG.debug( msg );
throw new LdapException( msg );
}
if ( DEBUG )
{
LOG.debug( "Found {} with oid: {}", schemaObject, oid );
}
return schemaObject;
}
/**
* {@inheritDoc}
*/
public void register( T schemaObject ) throws LdapException
{
String oid = schemaObject.getOid();
if ( byName.containsKey( oid ) )
{
String msg = I18n.err( I18n.ERR_04270, schemaObjectType.name(), oid );
LOG.warn( msg );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, msg );
ldapSchemaException.setSourceObject( schemaObject );
throw ldapSchemaException;
}
byName.put( oid, schemaObject );
/*
* add the aliases/names to the name map along with their toLowerCase
* versions of the name: this is used to make sure name lookups work
*/
for ( String name : schemaObject.getNames() )
{
String lowerName = Strings.trim( Strings.toLowerCase( name ) );
if ( byName.containsKey( lowerName ) )
{
String msg = I18n.err( I18n.ERR_04271, schemaObjectType.name(), name );
LOG.warn( msg );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.NAME_ALREADY_REGISTERED, msg );
ldapSchemaException.setSourceObject( schemaObject );
throw ldapSchemaException;
}
else
{
byName.put( lowerName, schemaObject );
}
}
// And register the oid -> schemaObject relation
oidRegistry.register( schemaObject );
if ( LOG.isDebugEnabled() )
{
LOG.debug( "registered " + schemaObject.getName() + " for OID {}", oid );
}
}
/**
* {@inheritDoc}
*/
public T unregister( String numericOid ) throws LdapException
{
if ( !Oid.isOid( numericOid ) )
{
String msg = I18n.err( I18n.ERR_04272, numericOid );
LOG.error( msg );
throw new LdapException( msg );
}
T schemaObject = byName.remove( numericOid );
for ( String name : schemaObject.getNames() )
{
byName.remove( name );
}
// And remove the SchemaObject from the oidRegistry
oidRegistry.unregister( numericOid );
if ( DEBUG )
{
LOG.debug( "Removed {} with oid {} from the registry", schemaObject, numericOid );
}
return schemaObject;
}
/**
* {@inheritDoc}
*/
public T unregister( T schemaObject ) throws LdapException
{
String oid = schemaObject.getOid();
if ( !byName.containsKey( oid ) )
{
String msg = I18n.err( I18n.ERR_04273, schemaObjectType.name(), oid );
LOG.warn( msg );
throw new LdapException( msg );
}
// Remove the oid
T removed = byName.remove( oid );
/*
* Remove the aliases/names from the name map along with their toLowerCase
* versions of the name.
*/
for ( String name : schemaObject.getNames() )
{
byName.remove( Strings.trim( Strings.toLowerCase( name ) ) );
}
// And unregister the oid -> schemaObject relation
oidRegistry.unregister( oid );
return removed;
}
/**
* {@inheritDoc}
*/
public void unregisterSchemaElements( String schemaName ) throws LdapException
{
if ( schemaName == null )
{
return;
}
// Loop on all the SchemaObjects stored and remove those associated
// with the give schemaName
for ( T schemaObject : this )
{
if ( schemaName.equalsIgnoreCase( schemaObject.getSchemaName() ) )
{
String oid = schemaObject.getOid();
SchemaObject removed = unregister( oid );
if ( DEBUG )
{
LOG.debug( "Removed {} with oid {} from the registry", removed, oid );
}
}
}
}
/**
* {@inheritDoc}
*/
public String getOidByName( String name ) throws LdapException
{
T schemaObject = byName.get( name );
if ( schemaObject == null )
{
// last resort before giving up check with lower cased version
String lowerCased = Strings.toLowerCase( name );
schemaObject = byName.get( lowerCased );
// ok this name is not for a schema object in the registry
if ( schemaObject == null )
{
throw new LdapException( I18n.err( I18n.ERR_04274, name ) );
}
}
// we found the schema object by key on the first lookup attempt
return schemaObject.getOid();
}
/**
* {@inheritDoc}
*/
// This will suppress PMD.EmptyCatchBlock warnings in this method
@SuppressWarnings("unchecked")
public SchemaObjectRegistry<T> copy( SchemaObjectRegistry<T> original )
{
// Fill the byName and OidRegistry maps, the type has already be copied
for ( Map.Entry<String, T> entry : ( ( DefaultSchemaObjectRegistry<T> ) original ).byName.entrySet() )
{
String key = entry.getKey();
// Clone each SchemaObject
T value = entry.getValue();
if ( value instanceof LoadableSchemaObject )
{
// Update the data structure.
// Comparators, Normalizers and SyntaxCheckers aren't copied,
// they are immutable
byName.put( key, value );
// Update the OidRegistry
oidRegistry.put( value );
}
else
{
T copiedValue = null;
// Copy the value if it's not already in the oidRegistry
if ( oidRegistry.contains( value.getOid() ) )
{
try
{
copiedValue = oidRegistry.getSchemaObject( value.getOid() );
}
catch ( LdapException ne )
{
// Can't happen
}
}
else
{
copiedValue = ( T ) value.copy();
}
// Update the data structure.
byName.put( key, copiedValue );
// Update the OidRegistry
oidRegistry.put( copiedValue );
}
}
return this;
}
/**
* {@inheritDoc}
*/
public T get( String oid )
{
try
{
return oidRegistry.getSchemaObject( oid );
}
catch ( LdapException ne )
{
return null;
}
}
/**
* {@inheritDoc}
*/
public SchemaObjectType getType()
{
return schemaObjectType;
}
/**
* {@inheritDoc}
*/
public int size()
{
return oidRegistry.size();
}
/**
* @see Object#toString()
*/
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( schemaObjectType ).append( ": " );
boolean isFirst = true;
for ( Map.Entry<String, T> entry : byName.entrySet() )
{
if ( isFirst )
{
isFirst = false;
}
else
{
sb.append( ", " );
}
String name = entry.getKey();
T schemaObject = entry.getValue();
sb.append( '<' ).append( name ).append( ", " ).append( schemaObject.getOid() ).append( '>' );
}
return sb.toString();
}
/**
* {@inheritDoc}
*/
public void clear()
{
// Clear all the schemaObjects
for ( SchemaObject schemaObject : oidRegistry )
{
// Don't clear LoadableSchemaObject
if ( !( schemaObject instanceof LoadableSchemaObject ) )
{
schemaObject.clear();
}
}
// Remove the byName elements
byName.clear();
// Clear the OidRegistry
oidRegistry.clear();
}
}