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;
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 javax.naming.directory.DirContext;
029    
030    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
031    import org.apache.directory.server.i18n.I18n;
032    import org.apache.directory.shared.ldap.constants.SchemaConstants;
033    import org.apache.directory.shared.ldap.entry.EntryAttribute;
034    import org.apache.directory.shared.ldap.entry.Modification;
035    import org.apache.directory.shared.ldap.entry.ServerEntry;
036    import org.apache.directory.shared.ldap.exception.LdapUnwillingToPerformException;
037    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
038    import org.apache.directory.shared.ldap.name.DN;
039    import org.apache.directory.shared.ldap.schema.AttributeType;
040    import org.apache.directory.shared.ldap.schema.DITContentRule;
041    import org.apache.directory.shared.ldap.schema.DITStructureRule;
042    import org.apache.directory.shared.ldap.schema.LdapSyntax;
043    import org.apache.directory.shared.ldap.schema.MatchingRule;
044    import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
045    import org.apache.directory.shared.ldap.schema.NameForm;
046    import org.apache.directory.shared.ldap.schema.ObjectClass;
047    import org.apache.directory.shared.ldap.schema.SchemaManager;
048    import org.apache.directory.shared.ldap.schema.parsers.LdapComparatorDescription;
049    import org.apache.directory.shared.ldap.schema.parsers.NormalizerDescription;
050    import org.apache.directory.shared.ldap.schema.parsers.SyntaxCheckerDescription;
051    import org.apache.directory.shared.ldap.schema.registries.SchemaLoader;
052    import org.slf4j.Logger;
053    import org.slf4j.LoggerFactory;
054    
055    
056    /**
057     *
058     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
059     * @version $Rev$, $Date$
060     */
061    public class SchemaSubentryManager
062    {
063        /** A logger for this class */
064        private static final Logger LOG = LoggerFactory.getLogger( SchemaSubentryManager.class );
065    
066        // indices of handlers and object ids into arrays
067        private static final int COMPARATOR_INDEX = 0;
068        private static final int NORMALIZER_INDEX = 1;
069        private static final int SYNTAX_CHECKER_INDEX = 2;
070        private static final int SYNTAX_INDEX = 3;
071        private static final int MATCHING_RULE_INDEX = 4;
072        private static final int ATTRIBUTE_TYPE_INDEX = 5;
073        private static final int OBJECT_CLASS_INDEX = 6;
074        private static final int MATCHING_RULE_USE_INDEX = 7;
075        private static final int DIT_STRUCTURE_RULE_INDEX = 8;
076        private static final int DIT_CONTENT_RULE_INDEX = 9;
077        private static final int NAME_FORM_INDEX = 10;
078    
079        private static final Set<String> VALID_OU_VALUES = new HashSet<String>();
080    
081        /** The schemaManager */
082        private final SchemaManager schemaManager;
083        
084        private final SchemaSubentryModifier subentryModifier;
085        
086        /** The description parsers */
087        private final DescriptionParsers parsers;
088        
089        /** 
090         * Maps the OID of a subschemaSubentry operational attribute to the index of 
091         * the handler in the schemaObjectHandlers array.
092         */ 
093        private final Map<String, Integer> opAttr2handlerIndex = new HashMap<String, Integer>( 11 );
094        private static final String CASCADING_ERROR =
095                "Cascading has not yet been implemented: standard operation is in effect.";
096    
097        static 
098        {
099            VALID_OU_VALUES.add( SchemaConstants.NORMALIZERS_AT.toLowerCase() );
100            VALID_OU_VALUES.add( SchemaConstants.COMPARATORS_AT.toLowerCase() );
101            VALID_OU_VALUES.add( SchemaConstants.SYNTAX_CHECKERS_AT.toLowerCase() );
102            VALID_OU_VALUES.add( "syntaxes".toLowerCase() );
103            VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULES_AT.toLowerCase() );
104            VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULE_USE_AT.toLowerCase() );
105            VALID_OU_VALUES.add( SchemaConstants.ATTRIBUTE_TYPES_AT.toLowerCase() );
106            VALID_OU_VALUES.add( SchemaConstants.OBJECT_CLASSES_AT.toLowerCase() );
107            VALID_OU_VALUES.add( SchemaConstants.NAME_FORMS_AT.toLowerCase() );
108            VALID_OU_VALUES.add( SchemaConstants.DIT_CONTENT_RULES_AT.toLowerCase() );
109            VALID_OU_VALUES.add( SchemaConstants.DIT_STRUCTURE_RULES_AT.toLowerCase() );
110        }
111    
112    
113        public SchemaSubentryManager( SchemaManager schemaManager, SchemaLoader loader )
114            throws Exception
115        {
116            this.schemaManager = schemaManager;
117            this.subentryModifier = new SchemaSubentryModifier( schemaManager );
118            this.parsers = new DescriptionParsers( schemaManager );
119            
120            String comparatorsOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.COMPARATORS_AT );
121            opAttr2handlerIndex.put( comparatorsOid, COMPARATOR_INDEX );
122    
123            String normalizersOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.NORMALIZERS_AT );
124            opAttr2handlerIndex.put( normalizersOid, NORMALIZER_INDEX );
125    
126            String syntaxCheckersOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.SYNTAX_CHECKERS_AT );
127            opAttr2handlerIndex.put( syntaxCheckersOid, SYNTAX_CHECKER_INDEX );
128    
129            String ldapSyntaxesOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.LDAP_SYNTAXES_AT );
130            opAttr2handlerIndex.put( ldapSyntaxesOid, SYNTAX_INDEX );
131    
132            String matchingRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.MATCHING_RULES_AT );
133            opAttr2handlerIndex.put( matchingRulesOid, MATCHING_RULE_INDEX );
134    
135            String attributeTypesOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.ATTRIBUTE_TYPES_AT );
136            opAttr2handlerIndex.put( attributeTypesOid, ATTRIBUTE_TYPE_INDEX );
137    
138            String objectClassesOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.OBJECT_CLASSES_AT );
139            opAttr2handlerIndex.put( objectClassesOid, OBJECT_CLASS_INDEX );
140    
141            String matchingRuleUseOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.MATCHING_RULE_USE_AT );
142            opAttr2handlerIndex.put( matchingRuleUseOid, MATCHING_RULE_USE_INDEX );
143    
144            String ditStructureRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.DIT_STRUCTURE_RULES_AT );
145            opAttr2handlerIndex.put( ditStructureRulesOid, DIT_STRUCTURE_RULE_INDEX );
146    
147            String ditContentRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.DIT_CONTENT_RULES_AT );
148            opAttr2handlerIndex.put( ditContentRulesOid, DIT_CONTENT_RULE_INDEX );
149    
150            String nameFormsOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.NAME_FORMS_AT );
151            opAttr2handlerIndex.put( nameFormsOid, NAME_FORM_INDEX );
152        }
153    
154        
155        /* (non-Javadoc)
156         * @see org.apache.directory.server.core.schema.SchemaChangeManager#modifySchemaSubentry(org.apache.directory.server.core.interceptor.context.ModifyOperationContext, org.apache.directory.server.core.entry.ServerEntry, org.apache.directory.server.core.entry.ServerEntry, boolean)
157         */
158        public void modifySchemaSubentry( ModifyOperationContext opContext, boolean doCascadeModify ) throws Exception 
159        {
160            for ( Modification mod : opContext.getModItems() )
161            {
162                String opAttrOid = schemaManager.getAttributeTypeRegistry().getOidByName( mod.getAttribute().getId() );
163                
164                EntryAttribute serverAttribute = mod.getAttribute();
165    
166                switch ( mod.getOperation() )
167                {
168                    case ADD_ATTRIBUTE :
169                        modifyAddOperation( opContext, opAttrOid, serverAttribute, doCascadeModify );
170                        break;
171                        
172                    case REMOVE_ATTRIBUTE :
173                        modifyRemoveOperation( opContext, opAttrOid, serverAttribute, doCascadeModify );
174                        break; 
175                        
176                    case REPLACE_ATTRIBUTE :
177                        throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, 
178                            I18n.err( I18n.ERR_283 ) );
179                    
180                    default:
181                        throw new IllegalStateException( I18n.err( I18n.ERR_284, mod.getOperation() ) );
182                }
183            }
184        }
185        
186        
187        /* (non-Javadoc)
188         * @see org.apache.directory.server.core.schema.SchemaChangeManager#modifySchemaSubentry(org.apache.directory.server.core.interceptor.context.ModifyOperationContext, org.apache.directory.shared.ldap.name.DN, int, org.apache.directory.server.core.entry.ServerEntry, org.apache.directory.server.core.entry.ServerEntry, org.apache.directory.server.core.entry.ServerEntry, boolean)
189         */
190        public void modifySchemaSubentry( ModifyOperationContext opContext, DN name, int modOp, ServerEntry mods, 
191            ServerEntry subentry, ServerEntry targetSubentry, boolean doCascadeModify ) throws Exception
192        {
193            Set<AttributeType> attributeTypes = mods.getAttributeTypes();
194            
195            switch ( modOp )
196            {
197                case( DirContext.ADD_ATTRIBUTE ):
198                    for ( AttributeType attributeType:attributeTypes )
199                    {
200                        modifyAddOperation( opContext, attributeType.getOid(), 
201                            mods.get( attributeType ), doCascadeModify );
202                    }
203                
204                    break;
205                    
206                case( DirContext.REMOVE_ATTRIBUTE ):
207                    for ( AttributeType attributeType:attributeTypes )
208                    {
209                        modifyRemoveOperation( opContext, attributeType.getOid(), 
210                            mods.get( attributeType ), doCascadeModify );
211                    }
212                
213                    break;
214                    
215                case( DirContext.REPLACE_ATTRIBUTE ):
216                    throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, 
217                        I18n.err( I18n.ERR_283 ) );
218                
219                default:
220                    throw new IllegalStateException( I18n.err( I18n.ERR_284, modOp ) );
221            }
222        }
223        
224    
225        /**
226         * Handles the modify remove operation on the subschemaSubentry for schema entities. 
227         * 
228         * @param opAttrOid the numeric id of the operational attribute modified
229         * @param mods the attribute with the modifications
230         * @param doCascadeModify determines if a cascading operation should be performed
231         * to effect all dependents on the changed entity
232         * @throws Exception if there are problems updating the registries and the
233         * schema partition
234         */
235        private void modifyRemoveOperation( ModifyOperationContext opContext, String opAttrOid, 
236            EntryAttribute mods, boolean doCascadeModify ) throws Exception
237        {
238            int index = opAttr2handlerIndex.get( opAttrOid );
239            
240            switch( index )
241            {
242                case( COMPARATOR_INDEX ):
243                    LdapComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
244                    
245                    for ( LdapComparatorDescription comparatorDescription : comparatorDescriptions )
246                    {
247                        subentryModifier.delete( opContext, comparatorDescription );
248                    }
249                    break;
250                case( NORMALIZER_INDEX ):
251                    NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
252                    
253                    for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
254                    {
255                        subentryModifier.delete( opContext, normalizerDescription );
256                    }
257                    break;
258                case( SYNTAX_CHECKER_INDEX ):
259                    SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
260                    
261                    for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
262                    {
263                        subentryModifier.delete( opContext, syntaxCheckerDescription );
264                    }
265                    break;
266                case( SYNTAX_INDEX ):
267                    LdapSyntax[] syntaxes = parsers.parseLdapSyntaxes( mods );
268                    
269                    for ( LdapSyntax syntax : syntaxes )
270                    {
271                        subentryModifier.deleteSchemaObject( opContext, syntax );
272                    }
273                    break;
274                case( MATCHING_RULE_INDEX ):
275                    MatchingRule[] mrs = parsers.parseMatchingRules( mods );
276                    
277                    for ( MatchingRule mr : mrs )
278                    {
279                        subentryModifier.deleteSchemaObject( opContext, mr );
280                    }
281                    break;
282                case( ATTRIBUTE_TYPE_INDEX ):
283                    AttributeType[] ats = parsers.parseAttributeTypes( mods );
284                    
285                    for ( AttributeType at : ats )
286                    {
287                        subentryModifier.deleteSchemaObject( opContext, at );
288                    }
289                    break;
290                case( OBJECT_CLASS_INDEX ):
291                    ObjectClass[] ocs = parsers.parseObjectClasses( mods );
292    
293                    for ( ObjectClass oc : ocs )
294                    {
295                        subentryModifier.deleteSchemaObject( opContext, oc );
296                    }
297                    break;
298                case( MATCHING_RULE_USE_INDEX ):
299                    MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
300                    
301                    for ( MatchingRuleUse mru : mrus )
302                    {
303                        subentryModifier.deleteSchemaObject( opContext, mru );
304                    }
305                    break;
306                case( DIT_STRUCTURE_RULE_INDEX ):
307                    DITStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
308                    
309                    for ( DITStructureRule dsr : dsrs )
310                    {
311                        subentryModifier.deleteSchemaObject( opContext, dsr );
312                    }
313                    break;
314                case( DIT_CONTENT_RULE_INDEX ):
315                    DITContentRule[] dcrs = parsers.parseDitContentRules( mods );
316                    
317                    for ( DITContentRule dcr : dcrs )
318                    {
319                        subentryModifier.deleteSchemaObject( opContext, dcr );
320                    }
321                    break;
322                case( NAME_FORM_INDEX ):
323                    NameForm[] nfs = parsers.parseNameForms( mods );
324                    
325                    for ( NameForm nf : nfs )
326                    {
327                        subentryModifier.deleteSchemaObject( opContext, nf );
328                    }
329                    break;
330                default:
331                    throw new IllegalStateException( I18n.err( I18n.ERR_285, index ) );
332            }
333        }
334        
335        
336        /**
337         * Handles the modify add operation on the subschemaSubentry for schema entities. 
338         * 
339         * @param opAttrOid the numeric id of the operational attribute modified
340         * @param mods the attribute with the modifications
341         * @param doCascadeModify determines if a cascading operation should be performed
342         * to effect all dependents on the changed entity
343         * @throws Exception if there are problems updating the registries and the
344         * schema partition
345         */
346        private void modifyAddOperation( ModifyOperationContext opContext, String opAttrOid, 
347            EntryAttribute mods, boolean doCascadeModify ) throws Exception
348        {
349            if ( doCascadeModify )
350            {
351                LOG.error( CASCADING_ERROR );
352            }
353    
354            int index = opAttr2handlerIndex.get( opAttrOid );
355            
356            switch( index )
357            {
358                case( COMPARATOR_INDEX ):
359                    LdapComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
360                    
361                    for ( LdapComparatorDescription comparatorDescription : comparatorDescriptions )
362                    {
363                        subentryModifier.add( opContext, comparatorDescription );
364                    }
365                    
366                    break;
367                    
368                case( NORMALIZER_INDEX ):
369                    NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
370                    
371                    for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
372                    {
373                        subentryModifier.add( opContext, normalizerDescription );
374                    }
375                    
376                    break;
377                    
378                case( SYNTAX_CHECKER_INDEX ):
379                    SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
380                    
381                    for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
382                    {
383                        subentryModifier.add( opContext, syntaxCheckerDescription );
384                    }
385                    
386                    break;
387                    
388                case( SYNTAX_INDEX ):
389                    LdapSyntax[] syntaxes = parsers.parseLdapSyntaxes( mods );
390                    
391                    for ( LdapSyntax syntax : syntaxes )
392                    {
393                        subentryModifier.addSchemaObject( opContext, syntax );
394                    }
395                    
396                    break;
397                    
398                case( MATCHING_RULE_INDEX ):
399                    MatchingRule[] mrs = parsers.parseMatchingRules( mods );
400                    
401                    for ( MatchingRule mr : mrs )
402                    {
403                        subentryModifier.addSchemaObject( opContext, mr );
404                    }
405                    
406                    break;
407                    
408                case( ATTRIBUTE_TYPE_INDEX ):
409                    AttributeType[] ats = parsers.parseAttributeTypes( mods );
410                    
411                    for ( AttributeType at : ats )
412                    {
413                        subentryModifier.addSchemaObject( opContext, at );
414                    }
415                    
416                    break;
417                    
418                case( OBJECT_CLASS_INDEX ):
419                    ObjectClass[] ocs = parsers.parseObjectClasses( mods );
420    
421                    for ( ObjectClass oc : ocs )
422                    {
423                        subentryModifier.addSchemaObject( opContext, oc );
424                    }
425                    
426                    break;
427                    
428                case( MATCHING_RULE_USE_INDEX ):
429                    MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
430                    
431                    for ( MatchingRuleUse mru : mrus )
432                    {
433                        subentryModifier.addSchemaObject( opContext, mru );
434                    }
435                    
436                    break;
437                    
438                case( DIT_STRUCTURE_RULE_INDEX ):
439                    DITStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
440                    
441                    for ( DITStructureRule dsr : dsrs )
442                    {
443                        subentryModifier.addSchemaObject( opContext, dsr );
444                    }
445                    
446                    break;
447                    
448                case( DIT_CONTENT_RULE_INDEX ):
449                    DITContentRule[] dcrs = parsers.parseDitContentRules( mods );
450                    
451                    for ( DITContentRule dcr : dcrs )
452                    {
453                        subentryModifier.addSchemaObject( opContext, dcr );
454                    }
455                    
456                    break;
457                    
458                case( NAME_FORM_INDEX ):
459                    NameForm[] nfs = parsers.parseNameForms( mods );
460                    
461                    for ( NameForm nf : nfs )
462                    {
463                        subentryModifier.addSchemaObject( opContext, nf );
464                    }
465                    
466                    break;
467                    
468                default:
469                    throw new IllegalStateException( I18n.err( I18n.ERR_285, index ) );
470            }
471        }
472    }