blob: e64e443acbb6945dda788cfda43f8ba6bacab180 [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
*
* https://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.SchemaErrorHandler;
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.
*
* @param <T> The type of SchemaObject
*
* @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 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;
private SchemaErrorHandler errorHandler;
/**
* Creates a new DefaultSchemaObjectRegistry instance.
*
* @param schemaObjectType The Schema Object type
* @param oidRegistry The OID registry to use
*/
protected DefaultSchemaObjectRegistry( SchemaObjectType schemaObjectType, OidRegistry<T> oidRegistry )
{
byName = new HashMap<>();
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();
}
public SchemaErrorHandler getErrorHandler()
{
return errorHandler;
}
public void setErrorHandler( SchemaErrorHandler errorHandler )
{
this.errorHandler = errorHandler;
oidRegistry.setErrorHandler( errorHandler );
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains( String oid )
{
if ( !byName.containsKey( oid ) )
{
return byName.containsKey( Strings.toLowerCaseAscii( oid ) );
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String getSchemaName( String oid ) throws LdapException
{
if ( !Oid.isOid( oid ) )
{
String msg = I18n.err( I18n.ERR_13733_ARG_NOT_NUMERIC_OID );
if ( LOG.isWarnEnabled() )
{
LOG.warn( msg );
}
throw new LdapException( msg );
}
SchemaObject schemaObject = byName.get( oid );
if ( schemaObject != null )
{
return schemaObject.getSchemaName();
}
String msg = I18n.err( I18n.ERR_13734_OID_NOT_FOUND, oid );
if ( LOG.isWarnEnabled() )
{
LOG.warn( msg );
}
throw new LdapException( msg );
}
/**
* {@inheritDoc}
*/
@Override
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 ( LOG.isDebugEnabled() )
{
LOG.debug( I18n.msg( I18n.MSG_13722_RENAMED_SCHEMA_NAME_TO, schemaObject, newSchemaName ) );
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<T> iterator()
{
return oidRegistry.iterator();
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<String> oidsIterator()
{
return byName.keySet().iterator();
}
/**
* {@inheritDoc}
*/
@Override
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.toLowerCaseAscii( oid ) ) );
if ( schemaObject == null )
{
String msg = I18n.err( I18n.ERR_13735_ELEMENT_FOR_OID_DOES_NOT_EXIST, schemaObjectType.name(), oid );
if ( LOG.isDebugEnabled() )
{
LOG.debug( msg );
}
throw new LdapException( msg );
}
}
if ( LOG.isDebugEnabled() )
{
LOG.debug( I18n.msg( I18n.MSG_13723_FOUND_WITH_OID, schemaObject, oid ) );
}
return schemaObject;
}
/**
* {@inheritDoc}
*/
@Override
public void register( T schemaObject ) throws LdapException
{
String oid = schemaObject.getOid();
if ( byName.containsKey( oid ) )
{
String msg = I18n.err( I18n.ERR_13736_ELEMENT_FOR_OID_ALREADY_REGISTERED, schemaObjectType.name(), oid );
if ( LOG.isWarnEnabled() )
{
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.toLowerCaseAscii( name ) );
if ( byName.containsKey( lowerName ) )
{
String msg = I18n.err( I18n.ERR_13737_ELEMENT_WITH_NAME_ALREADY_REGISTERED, schemaObjectType.name(), name );
if ( LOG.isWarnEnabled() )
{
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( I18n.msg( I18n.MSG_13731_REGISTRED_FOR_OID, schemaObject.getName(), oid ) );
}
}
/**
* {@inheritDoc}
*/
@Override
public T unregister( String numericOid ) throws LdapException
{
if ( !Oid.isOid( numericOid ) )
{
String msg = I18n.err( I18n.ERR_13738_OID_NOT_A_NUMERIC_OID, 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 ( LOG.isDebugEnabled() )
{
LOG.debug( I18n.msg( I18n.MSG_13702_REMOVED_FROM_REGISTRY, schemaObject, numericOid ) );
}
return schemaObject;
}
/**
* {@inheritDoc}
*/
@Override
public T unregister( T schemaObject ) throws LdapException
{
String oid = schemaObject.getOid();
if ( !byName.containsKey( oid ) )
{
String msg = I18n.err( I18n.ERR_13739_ELEMENT_WITH_OID_NOT_REGISTERED, schemaObjectType.name(), oid );
if ( LOG.isWarnEnabled() )
{
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.toLowerCaseAscii( name ) ) );
}
// And unregister the oid -> schemaObject relation
oidRegistry.unregister( oid );
return removed;
}
/**
* {@inheritDoc}
*/
@Override
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 ( LOG.isDebugEnabled() )
{
LOG.debug( I18n.msg( I18n.MSG_13702_REMOVED_FROM_REGISTRY, removed, oid ) );
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
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.toLowerCaseAscii( 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_13740_CANNOT_FIND_OID_FROM_NAME, name ) );
}
}
// we found the schema object by key on the first lookup attempt
return schemaObject.getOid();
}
/**
* Copy a SchemaObject registry
*
* @param original The SchemaObject registry to copy
* @return The copied ShcemaObject registry
*/
// 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}
*/
@Override
public T get( String oid )
{
try
{
return oidRegistry.getSchemaObject( oid );
}
catch ( LdapException ne )
{
return null;
}
}
/**
* {@inheritDoc}
*/
@Override
public SchemaObjectType getType()
{
return schemaObjectType;
}
/**
* {@inheritDoc}
*/
@Override
public int size()
{
return oidRegistry.size();
}
/**
* @see Object#toString()
*/
@Override
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}
*/
@Override
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();
}
}