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.core.schema.registries.synchronizers;
021    
022    
023    import java.util.HashMap;
024    import java.util.HashSet;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import org.apache.directory.server.constants.ApacheSchemaConstants;
029    import org.apache.directory.server.core.interceptor.context.AddOperationContext;
030    import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
031    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
032    import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
033    import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
034    import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
035    import org.apache.directory.server.i18n.I18n;
036    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
037    import org.apache.directory.shared.ldap.constants.SchemaConstants;
038    import org.apache.directory.shared.ldap.entry.EntryAttribute;
039    import org.apache.directory.shared.ldap.entry.ServerEntry;
040    import org.apache.directory.shared.ldap.entry.Value;
041    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
042    import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
043    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
044    import org.apache.directory.shared.ldap.schema.AttributeType;
045    import org.apache.directory.shared.ldap.schema.ObjectClass;
046    import org.apache.directory.shared.ldap.schema.SchemaManager;
047    import org.apache.directory.shared.ldap.schema.registries.ObjectClassRegistry;
048    import org.apache.directory.shared.ldap.schema.registries.Registries;
049    import org.slf4j.Logger;
050    import org.slf4j.LoggerFactory;
051    
052    
053    /**
054     * Central point of control for schemas enforced by the server.  The 
055     * following duties are presently performed by this class:
056     * 
057     * <ul>
058     *   <li>Provide central point of access for all registries: global and SAA specific registries</li>
059     *   <li>Manage enabling and disabling schemas</li>
060     *   <li>Responding to specific schema object changes</li>
061     * </ul>
062     *
063     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
064     * @version $Rev$, $Date$
065     */
066    public class RegistrySynchronizerAdaptor
067    {
068        /** A logger for this class */
069        private static final Logger LOG = LoggerFactory.getLogger( RegistrySynchronizerAdaptor.class );
070    
071        // indices of handlers and object ids into arrays
072        private static final int COMPARATOR_INDEX = 0;
073        private static final int NORMALIZER_INDEX = 1;
074        private static final int SYNTAX_CHECKER_INDEX = 2;
075        private static final int SYNTAX_INDEX = 3;
076        private static final int MATCHING_RULE_INDEX = 4;
077        private static final int ATTRIBUTE_TYPE_INDEX = 5;
078        private static final int OBJECT_CLASS_INDEX = 6;
079        private static final int MATCHING_RULE_USE_INDEX = 7;
080        private static final int DIT_STRUCTURE_RULE_INDEX = 8;
081        private static final int DIT_CONTENT_RULE_INDEX = 9;
082        private static final int NAME_FORM_INDEX = 10;
083    
084        private static final Set<String> VALID_OU_VALUES = new HashSet<String>();
085        private static final String[] META_OBJECT_CLASSES = new String[] {
086            MetaSchemaConstants.META_COMPARATOR_OC,
087            MetaSchemaConstants.META_NORMALIZER_OC,
088            MetaSchemaConstants.META_SYNTAX_CHECKER_OC,
089            MetaSchemaConstants.META_SYNTAX_OC,
090            MetaSchemaConstants.META_MATCHING_RULE_OC,
091            MetaSchemaConstants.META_ATTRIBUTE_TYPE_OC,
092            MetaSchemaConstants.META_OBJECT_CLASS_OC,
093            MetaSchemaConstants.META_MATCHING_RULE_USE_OC,
094            MetaSchemaConstants.META_DIT_STRUCTURE_RULE_OC,
095            MetaSchemaConstants.META_DIT_CONTENT_RULE_OC,
096            MetaSchemaConstants.META_NAME_FORM_OC
097        };
098    
099        private final Registries registries;
100        private final AttributeType objectClassAT;
101        private final RegistrySynchronizer[] registrySynchronizers = new RegistrySynchronizer[11];
102        private final Map<String, RegistrySynchronizer> objectClass2synchronizerMap = new HashMap<String, RegistrySynchronizer>();
103        private final SchemaSynchronizer schemaSynchronizer;
104    
105        static 
106        {
107            VALID_OU_VALUES.add( SchemaConstants.NORMALIZERS_AT.toLowerCase() );
108            VALID_OU_VALUES.add( SchemaConstants.COMPARATORS_AT.toLowerCase() );
109            VALID_OU_VALUES.add( SchemaConstants.SYNTAX_CHECKERS_AT.toLowerCase() );
110            VALID_OU_VALUES.add( "syntaxes".toLowerCase() );
111            VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULES_AT.toLowerCase() );
112            VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULE_USE_AT.toLowerCase() );
113            VALID_OU_VALUES.add( SchemaConstants.ATTRIBUTE_TYPES_AT.toLowerCase() );
114            VALID_OU_VALUES.add( SchemaConstants.OBJECT_CLASSES_AT.toLowerCase() );
115            VALID_OU_VALUES.add( SchemaConstants.NAME_FORMS_AT.toLowerCase() );
116            VALID_OU_VALUES.add( SchemaConstants.DIT_CONTENT_RULES_AT.toLowerCase() );
117            VALID_OU_VALUES.add( SchemaConstants.DIT_STRUCTURE_RULES_AT.toLowerCase() );
118        }
119    
120    
121        public RegistrySynchronizerAdaptor( SchemaManager schemaManager ) throws Exception
122        {
123            this.registries = schemaManager.getRegistries();
124            this.schemaSynchronizer = new SchemaSynchronizer( schemaManager );
125            this.objectClassAT = this.registries.getAttributeTypeRegistry()
126                .lookup( SchemaConstants.OBJECT_CLASS_AT );
127            
128            this.registrySynchronizers[COMPARATOR_INDEX] = new ComparatorSynchronizer( schemaManager ); 
129            this.registrySynchronizers[NORMALIZER_INDEX] = new NormalizerSynchronizer( schemaManager );
130            this.registrySynchronizers[SYNTAX_CHECKER_INDEX] = new SyntaxCheckerSynchronizer( schemaManager );
131            this.registrySynchronizers[SYNTAX_INDEX] = new SyntaxSynchronizer( schemaManager );
132            this.registrySynchronizers[MATCHING_RULE_INDEX] = new MatchingRuleSynchronizer( schemaManager );
133            this.registrySynchronizers[ATTRIBUTE_TYPE_INDEX] = new AttributeTypeSynchronizer( schemaManager );
134            this.registrySynchronizers[OBJECT_CLASS_INDEX] = new ObjectClassSynchronizer( schemaManager );
135            this.registrySynchronizers[MATCHING_RULE_USE_INDEX] = new MatchingRuleUseSynchronizer( schemaManager );
136            this.registrySynchronizers[DIT_STRUCTURE_RULE_INDEX] = new DitStructureRuleSynchronizer( schemaManager ); 
137            this.registrySynchronizers[DIT_CONTENT_RULE_INDEX] = new DitContentRuleSynchronizer( schemaManager ); 
138            this.registrySynchronizers[NAME_FORM_INDEX] = new NameFormSynchronizer( schemaManager ); 
139    
140            ObjectClassRegistry ocReg = registries.getObjectClassRegistry();
141            for ( int ii = 0; ii < META_OBJECT_CLASSES.length; ii++ )
142            {
143                ObjectClass oc = ocReg.lookup( META_OBJECT_CLASSES[ii] );
144                objectClass2synchronizerMap.put( oc.getOid(), registrySynchronizers[ii] );
145            }
146        }
147    
148    
149        /**
150         * Add a new SchemaObject or a new Schema in the Schema partition.
151         *
152         * @param opContext The Add context, containing the entry to be added
153         * @throws Exception If the addition failed 
154         */
155        public void add( AddOperationContext opContext ) throws Exception
156        {
157            EntryAttribute oc = opContext.getEntry().get( objectClassAT );
158            
159            // First check if we are adding a schemaObject
160            for ( Value<?> value:oc )
161            {
162    
163                String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
164                
165                if ( objectClass2synchronizerMap.containsKey( oid ) )
166                {
167                    // This is one of the eleven SchemaObject :
168                    // AT, C, DCR, DSR, MR, MRU, NF, N, OC, S, SC
169                    RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
170                    ServerEntry entry = opContext.getEntry();
171                    synchronizer.add( entry );
172                    return;
173                }
174            }
175            
176            // This is a Schema
177            // e.g. ou=my custom schema,ou=schema
178            if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
179            {
180                ServerEntry entry = opContext.getEntry();
181                schemaSynchronizer.add( entry );
182                return;
183            }
184            
185            // Check if it is a valid container for AT, C, DCR, DSR, MR, MRU, NF, N, OC, S, SC
186            // e.g. ou=attributeTypes,ou=my custom schema,ou=schema
187            if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
188            {
189                if ( opContext.getDn().size() != 3 )
190                {
191                    String msg = I18n.err( I18n.ERR_81 );
192                    LOG.error( msg );
193                    throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, msg );
194                }
195                
196                String ouValue = ( String ) opContext.getDn().getRdn().getNormValue();
197                ouValue = ouValue.trim().toLowerCase();
198                
199                if ( ! VALID_OU_VALUES.contains( ouValue ) )
200                {
201                    String msg = I18n.err( I18n.ERR_82, VALID_OU_VALUES );
202                    LOG.error( msg );
203                    throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION, msg );
204                }
205                
206                // this is a valid container.
207                return;
208            }
209    
210            
211            String msg = I18n.err( I18n.ERR_83, opContext.getDn() );
212            LOG.error( msg );
213            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
214        }
215        
216    
217        /**
218         * {@inheritDoc}
219         */
220        public void delete( DeleteOperationContext opContext, boolean doCascadeDelete ) 
221            throws Exception
222        {
223            ServerEntry entry = opContext.getEntry();
224            
225            EntryAttribute oc = entry.get( objectClassAT );
226            
227            for ( Value<?> value:oc )
228            {
229                String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
230                
231                if ( objectClass2synchronizerMap.containsKey( oid ) )
232                {
233                    RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
234                    synchronizer.delete( entry, doCascadeDelete );
235                    return;
236                }
237            }
238    
239            if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
240            {
241                schemaSynchronizer.delete( entry, doCascadeDelete );
242                return;
243            }
244            
245            if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
246            {
247                if ( opContext.getDn().size() != 3 )
248                {
249                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, I18n.err( I18n.ERR_378 ) );
250                }
251                
252                String ouValue = ( String ) opContext.getDn().getRdn().getNormValue();
253                ouValue = ouValue.trim().toLowerCase();
254                
255                if ( ! VALID_OU_VALUES.contains( ouValue ) )
256                {
257                    throw new LdapInvalidDnException( ResultCodeEnum.NAMING_VIOLATION,
258                        I18n.err( I18n.ERR_379, VALID_OU_VALUES ) );
259                }
260                
261                return;
262            }
263    
264            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM );
265        }
266        
267    
268        /**
269         * Modify the schema
270         *
271         * @param opContext The context
272         * @param targetEntry The modified entry
273         * @param doCascadeModify Not used
274         * @throws Exception If the modification failed
275         */
276        public boolean modify( ModifyOperationContext opContext, ServerEntry targetEntry, boolean doCascadeModify ) throws Exception
277        {
278            ServerEntry entry = opContext.getEntry();
279            EntryAttribute oc = entry.get( objectClassAT );
280            
281            for ( Value<?> value:oc )
282            {
283                String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
284                
285                if ( objectClass2synchronizerMap.containsKey( oid ) )
286                {
287                    RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
288                    boolean hasModification = synchronizer.modify( opContext, targetEntry, doCascadeModify );
289                    return hasModification;
290                }
291            }
292    
293            if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
294            {
295                boolean hasModification = schemaSynchronizer.modify( opContext, targetEntry, doCascadeModify );
296                return hasModification;
297            }
298    
299            if ( oc.contains(  ApacheSchemaConstants.SCHEMA_MODIFICATION_ATTRIBUTES_OC ) )
300            {
301                return false;
302            }
303            
304            LOG.error( String.format( I18n.err( I18n.ERR_84 ), 
305                opContext.getDn(), entry, opContext.getModItems() ) );
306            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM );
307        }
308    
309    
310        /**
311         * Rename a Schema Object.
312         *
313         * @param opContext The contect contaoning the rename informations
314         * @param doCascadeModify unused
315         * @throws Exception If the rename failed
316         */
317        public void rename( RenameOperationContext opContext, boolean doCascadeModify ) 
318            throws Exception
319        {
320            ServerEntry originalEntry = opContext.getEntry().getOriginalEntry();
321            EntryAttribute oc = originalEntry.get( objectClassAT );
322            
323            for ( Value<?> value:oc )
324            {
325                String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
326                
327                if ( objectClass2synchronizerMap.containsKey( oid ) )
328                {
329                    RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
330                    synchronizer.rename( originalEntry, opContext.getNewRdn(), doCascadeModify );
331                    return;
332                }
333            }
334    
335            if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
336            {
337                schemaSynchronizer.rename( originalEntry, opContext.getNewRdn(), doCascadeModify );
338                return;
339            }
340            
341            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM );
342        }
343    
344    
345        /* (non-Javadoc)
346         * @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)
347         */
348        public void move( MoveOperationContext opContext, ServerEntry entry, boolean cascade ) throws Exception
349        {
350            EntryAttribute oc = entry.get( objectClassAT );
351            
352            for ( Value<?> value:oc )
353            {
354                String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
355                
356                if ( objectClass2synchronizerMap.containsKey( oid ) )
357                {
358                    RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
359                    synchronizer.move( opContext.getDn(), opContext.getParent(), entry, cascade );
360                    return;
361                }
362            }
363    
364            if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
365            {
366                schemaSynchronizer.move( opContext.getDn(), opContext.getParent(), entry, cascade );
367                return;
368            }
369            
370            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM );
371        }
372    
373    
374        /* (non-Javadoc)
375         * @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)
376         */
377        public void moveAndRename( MoveAndRenameOperationContext opContext, ServerEntry entry, boolean cascade ) throws Exception
378        {
379            EntryAttribute oc = entry.get( objectClassAT );
380            
381            for ( Value<?> value:oc )
382            {
383                String oid = registries.getObjectClassRegistry().getOidByName( value.getString() );
384                
385                if ( objectClass2synchronizerMap.containsKey( oid ) )
386                {
387                    RegistrySynchronizer synchronizer = objectClass2synchronizerMap.get( oid );
388                    synchronizer.moveAndRename( opContext.getDn(), opContext.getParent(), opContext.getNewRdn(), 
389                        opContext.getDelOldDn(), entry, cascade );
390                    return;
391                }
392            }
393    
394            if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
395            {
396                schemaSynchronizer.moveAndRename( opContext.getDn(), opContext.getParent(), opContext.getNewRdn(), 
397                    opContext.getDelOldDn(), entry, cascade );
398                return;
399            }
400            
401            throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM );
402        }
403    }