/*
* 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.core.schema.registries.synchronizers;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.directory.server.constants.ApacheSchemaConstants;
import org.apache.directory.server.core.entry.ServerEntry;
import org.apache.directory.server.core.interceptor.context.AddOperationContext;
import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
import org.apache.directory.shared.ldap.constants.SchemaConstants;
import org.apache.directory.shared.ldap.entry.EntryAttribute;
import org.apache.directory.shared.ldap.entry.Value;
import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
import org.apache.directory.shared.ldap.exception.LdapNamingException;
import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
import org.apache.directory.shared.ldap.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.schema.AttributeType;
import org.apache.directory.shared.ldap.schema.ObjectClass;
import org.apache.directory.shared.ldap.schema.SchemaManager;
import org.apache.directory.shared.ldap.schema.registries.ObjectClassRegistry;
import org.apache.directory.shared.ldap.schema.registries.Registries;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Central point of control for schemas enforced by the server. The
* following duties are presently performed by this class:
*
* <ul>
* <li>Provide central point of access for all registries: global and SAA specific registries</li>
* <li>Manage enabling and disabling schemas</li>
* <li>Responding to specific schema object changes</li>
* </ul>
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public class RegistrySynchronizerAdaptor
{
/** A logger for this class */
private static final Logger LOG = LoggerFactory.getLogger( RegistrySynchronizerAdaptor.class );
// indices of handlers and object ids into arrays
private static final int COMPARATOR_INDEX = 0;
private static final int NORMALIZER_INDEX = 1;
private static final int SYNTAX_CHECKER_INDEX = 2;
private static final int SYNTAX_INDEX = 3;
private static final int MATCHING_RULE_INDEX = 4;
private static final int ATTRIBUTE_TYPE_INDEX = 5;
private static final int OBJECT_CLASS_INDEX = 6;
private static final int MATCHING_RULE_USE_INDEX = 7;
private static final int DIT_STRUCTURE_RULE_INDEX = 8;
private static final int DIT_CONTENT_RULE_INDEX = 9;
private static final int NAME_FORM_INDEX = 10;
private static final Set<String> VALID_OU_VALUES = new HashSet<String>();
private static final String[] META_OBJECT_CLASSES = new String[] {
MetaSchemaConstants.META_COMPARATOR_OC,
MetaSchemaConstants.META_NORMALIZER_OC,
MetaSchemaConstants.META_SYNTAX_CHECKER_OC,
MetaSchemaConstants.META_SYNTAX_OC,
MetaSchemaConstants.META_MATCHING_RULE_OC,
MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC,
MetaSchemaConstants.META_OBJECT_CLASS_OC,
MetaSchemaConstants.META_MATCHING_RULE_USE_OC,
MetaSchemaConstants.META_DIT_STRUCTURE_RULE_OC,
MetaSchemaConstants.META_DIT_CONTENT_RULE_OC,
MetaSchemaConstants.META_NAME_FORM_OC
};
private final Registries registries;
private final AttributeType objectClassAT;
private final RegistrySynchronizer[] registrySynchronizers = new RegistrySynchronizer[11];
private final Map<String, RegistrySynchronizer> objectClass2synchronizerMap = new HashMap<String, RegistrySynchronizer>();
private final SchemaSynchronizer schemaSynchronizer;
static
{
VALID_OU_VALUES.add( SchemaConstants.NORMALIZERS_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.COMPARATORS_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.SYNTAX_CHECKERS_AT.toLowerCase() );
VALID_OU_VALUES.add( "syntaxes".toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULES_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULE_USE_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.ATTRIBUTE_TYPES_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.OBJECT_CLASSES_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.NAME_FORMS_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.DIT_CONTENT_RULES_AT.toLowerCase() );
VALID_OU_VALUES.add( SchemaConstants.DIT_STRUCTURE_RULES_AT.toLowerCase() );
}
public RegistrySynchronizerAdaptor( SchemaManager schemaManager ) throws Exception
{
this.registries = schemaManager.getRegistries();
this.schemaSynchronizer = new SchemaSynchronizer( schemaManager );
this.objectClassAT = this.registries.getAttributeTypeRegistry()
.lookup( SchemaConstants.OBJECT_CLASS_AT );
this.registrySynchronizers[COMPARATOR_INDEX] = new ComparatorSynchronizer( schemaManager );
this.registrySynchronizers[NORMALIZER_INDEX] = new NormalizerSynchronizer( schemaManager );
this.registrySynchronizers[SYNTAX_CHECKER_INDEX] = new SyntaxCheckerSynchronizer( schemaManager );
this.registrySynchronizers[SYNTAX_INDEX] = new SyntaxSynchronizer( schemaManager );
this.registrySynchronizers[MATCHING_RULE_INDEX] = new MatchingRuleSynchronizer( schemaManager );
this.registrySynchronizers[ATTRIBUTE_TYPE_INDEX] = new AttributeTypeSynchronizer( schemaManager );
this.registrySynchronizers[OBJECT_CLASS_INDEX] = new ObjectClassSynchronizer( schemaManager );
this.registrySynchronizers[MATCHING_RULE_USE_INDEX] = new MatchingRuleUseSynchronizer( schemaManager );
this.registrySynchronizers[DIT_STRUCTURE_RULE_INDEX] = new DitStructureRuleSynchronizer( schemaManager );
this.registrySynchronizers[DIT_CONTENT_RULE_INDEX] = new DitContentRuleSynchronizer( schemaManager );
this.registrySynchronizers[NAME_FORM_INDEX] = new NameFormSynchronizer( schemaManager );
ObjectClassRegistry ocReg = registries.getObjectClassRegistry();
for ( int ii = 0; ii < META_OBJECT_CLASSES.length; ii++ )
{
ObjectClass oc = ocReg.lookup( META_OBJECT_CLASSES[ii] );
objectClass2synchronizerMap.put( oc.getOid(), registrySynchronizers[ii] );
}
}
/**
* Add a new SchemaObject or a new Schema in the Schema partition.
*
* @param opContext The Add context, containing the entry to be added
* @throws Exception If the addition failed
*/
public void add( AddOperationContext opContext ) throws Exception
{
EntryAttribute oc = opContext.getEntry().get( objectClassAT );
// First check if we are adding a schemaObject
for ( Value<?> value:oc )
{
String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
if ( objectClass2synchronizerMap.containsKey( oid ) )
{
// This is one of the eleven SchemaObject :
// AT, C, DCR, DSR, MR, MRU, NF, N, OC, S, SC
RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
ServerEntry entry = opContext.getEntry();
synchronizer.add( entry );
return;
}
}
// This is a Schema
// e.g. ou=my custom schema,ou=schema
if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
{
ServerEntry entry = opContext.getEntry();
schemaSynchronizer.add( entry );
return;
}
// Check if it is a valid container for AT, C, DCR, DSR, MR, MRU, NF, N, OC, S, SC
// e.g. ou=attributeTypes,ou=my custom schema,ou=schema
if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
{
if ( opContext.getDn().size() != 3 )
{
String msg = I18n.err( I18n.ERR_81 );
LOG.error( msg );
throw new LdapInvalidNameException( msg, ResultCodeEnum.NAMING_VIOLATION );
}
String ouValue = ( String ) opContext.getDn().getRdn().getNormValue();
ouValue = ouValue.trim().toLowerCase();
if ( ! VALID_OU_VALUES.contains( ouValue ) )
{
String msg = I18n.err( I18n.ERR_82, VALID_OU_VALUES );
LOG.error( msg );
throw new LdapInvalidNameException( msg, ResultCodeEnum.NAMING_VIOLATION );
}
// this is a valid container.
return;
}
String msg = I18n.err( I18n.ERR_83, opContext.getDn() );
LOG.error( msg );
throw new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM );
}
/**
* {@inheritDoc}
*/
public void delete( DeleteOperationContext opContext, boolean doCascadeDelete )
throws Exception
{
ServerEntry entry = opContext.getEntry();
EntryAttribute oc = entry.get( objectClassAT );
for ( Value<?> value:oc )
{
String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
if ( objectClass2synchronizerMap.containsKey( oid ) )
{
RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
synchronizer.delete( entry, doCascadeDelete );
return;
}
}
if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
{
schemaSynchronizer.delete( entry, doCascadeDelete );
return;
}
if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
{
if ( opContext.getDn().size() != 3 )
{
throw new LdapNamingException( I18n.err( I18n.ERR_378 ), ResultCodeEnum.UNWILLING_TO_PERFORM );
}
String ouValue = ( String ) opContext.getDn().getRdn().getNormValue();
ouValue = ouValue.trim().toLowerCase();
if ( ! VALID_OU_VALUES.contains( ouValue ) )
{
throw new LdapInvalidNameException( I18n.err( I18n.ERR_379, VALID_OU_VALUES ),
ResultCodeEnum.NAMING_VIOLATION );
}
return;
}
throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
}
/**
* Modify the schema
*
* @param opContext The context
* @param targetEntry The modified entry
* @param doCascadeModify Not used
* @throws Exception If the modification failed
*/
public boolean modify( ModifyOperationContext opContext, ServerEntry targetEntry, boolean doCascadeModify ) throws Exception
{
ServerEntry entry = opContext.getEntry();
EntryAttribute oc = entry.get( objectClassAT );
for ( Value<?> value:oc )
{
String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
if ( objectClass2synchronizerMap.containsKey( oid ) )
{
RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
boolean hasModification = synchronizer.modify( opContext, targetEntry, doCascadeModify );
return hasModification;
}
}
if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
{
boolean hasModification = schemaSynchronizer.modify( opContext, targetEntry, doCascadeModify );
return hasModification;
}
if ( oc.contains( ApacheSchemaConstants.SCHEMA_MODIFICATION_ATTRIBUTES_OC ) )
{
return false;
}
LOG.error( String.format( I18n.err( I18n.ERR_84 ),
opContext.getDn(), entry, opContext.getModItems() ) );
throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
}
/**
* Rename a Schema Object.
*
* @param opContext The contect contaoning the rename informations
* @param doCascadeModify unused
* @throws Exception If the rename failed
*/
public void rename( RenameOperationContext opContext, boolean doCascadeModify )
throws Exception
{
ServerEntry originalEntry = opContext.getEntry().getOriginalEntry();
EntryAttribute oc = originalEntry.get( objectClassAT );
for ( Value<?> value:oc )
{
String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
if ( objectClass2synchronizerMap.containsKey( oid ) )
{
RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
synchronizer.rename( originalEntry, opContext.getNewRdn(), doCascadeModify );
return;
}
}
if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
{
schemaSynchronizer.rename( originalEntry, opContext.getNewRdn(), doCascadeModify );
return;
}
throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.schema.SchemaChangeManager#replace(org.apache.directory.server.core.interceptor.context.MoveOperationContext, org.apache.directory.server.core.entry.ServerEntry, boolean)
*/
public void move( MoveOperationContext opContext, ServerEntry entry, boolean cascade ) throws Exception
{
EntryAttribute oc = entry.get( objectClassAT );
for ( Value<?> value:oc )
{
String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
if ( objectClass2synchronizerMap.containsKey( oid ) )
{
RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
synchronizer.move( opContext.getDn(), opContext.getParent(), entry, cascade );
return;
}
}
if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
{
schemaSynchronizer.move( opContext.getDn(), opContext.getParent(), entry, cascade );
return;
}
throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
}
/* (non-Javadoc)
* @see org.apache.directory.server.core.schema.SchemaChangeManager#move(org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext, org.apache.directory.server.core.entry.ServerEntry, boolean)
*/
public void moveAndRename( MoveAndRenameOperationContext opContext, ServerEntry entry, boolean cascade ) throws Exception
{
EntryAttribute oc = entry.get( objectClassAT );
for ( Value<?> value:oc )
{
String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
if ( objectClass2synchronizerMap.containsKey( oid ) )
{
RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
synchronizer.moveAndRename( opContext.getDn(), opContext.getParent(), opContext.getNewRdn(),
opContext.getDelOldDn(), entry, cascade );
return;
}
}
if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
{
schemaSynchronizer.moveAndRename( opContext.getDn(), opContext.getParent(), opContext.getNewRdn(),
opContext.getDelOldDn(), entry, cascade );
return;
}
throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
}
}