View Javadoc

1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.core.schema;
21  
22  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.directory.server.constants.ApacheSchemaConstants;
32  import org.apache.directory.server.constants.MetaSchemaConstants;
33  import org.apache.directory.server.core.authn.AuthenticationInterceptor;
34  import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
35  import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
36  import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
37  import org.apache.directory.server.core.entry.ClonedServerEntry;
38  import org.apache.directory.server.core.entry.DefaultServerAttribute;
39  import org.apache.directory.server.core.entry.ServerEntry;
40  import org.apache.directory.server.core.entry.ServerAttribute;
41  import org.apache.directory.server.core.entry.ServerModification;
42  import org.apache.directory.server.core.exception.ExceptionInterceptor;
43  import org.apache.directory.server.core.interceptor.context.AddOperationContext;
44  import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
45  import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
46  import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
47  import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
48  import org.apache.directory.server.core.interceptor.context.OperationContext;
49  import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
50  import org.apache.directory.server.core.normalization.NormalizationInterceptor;
51  import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
52  import org.apache.directory.server.schema.registries.ObjectClassRegistry;
53  import org.apache.directory.server.schema.registries.OidRegistry;
54  import org.apache.directory.server.schema.registries.Registries;
55  import org.apache.directory.shared.ldap.NotImplementedException;
56  import org.apache.directory.shared.ldap.constants.SchemaConstants;
57  import org.apache.directory.shared.ldap.entry.EntryAttribute;
58  import org.apache.directory.shared.ldap.entry.Modification;
59  import org.apache.directory.shared.ldap.entry.ModificationOperation;
60  import org.apache.directory.shared.ldap.entry.Value;
61  import org.apache.directory.shared.ldap.exception.LdapInvalidNameException;
62  import org.apache.directory.shared.ldap.exception.LdapNamingException;
63  import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
64  import org.apache.directory.shared.ldap.message.ResultCodeEnum;
65  import org.apache.directory.shared.ldap.name.LdapDN;
66  import org.apache.directory.shared.ldap.schema.AttributeType;
67  import org.apache.directory.shared.ldap.schema.DITContentRule;
68  import org.apache.directory.shared.ldap.schema.DITStructureRule;
69  import org.apache.directory.shared.ldap.schema.MatchingRule;
70  import org.apache.directory.shared.ldap.schema.MatchingRuleUse;
71  import org.apache.directory.shared.ldap.schema.NameForm;
72  import org.apache.directory.shared.ldap.schema.ObjectClass;
73  import org.apache.directory.shared.ldap.schema.Syntax;
74  import org.apache.directory.shared.ldap.schema.syntax.AbstractSchemaDescription;
75  import org.apache.directory.shared.ldap.schema.syntax.ComparatorDescription;
76  import org.apache.directory.shared.ldap.schema.syntax.NormalizerDescription;
77  import org.apache.directory.shared.ldap.schema.syntax.SyntaxCheckerDescription;
78  import org.apache.directory.shared.ldap.util.DateUtils;
79  
80  import org.slf4j.Logger;
81  import org.slf4j.LoggerFactory;
82  
83  import javax.naming.NamingException;
84  import javax.naming.directory.DirContext;
85  
86  
87  /**
88   * Central point of control for schemas enforced by the server.  The 
89   * following duties are presently performed by this class:
90   * 
91   * <ul>
92   *   <li>Provide central point of access for all registries: global and SAA specific registries</li>
93   *   <li>Manage enabling and disabling schemas</li>
94   *   <li>Responding to specific schema object changes</li>
95   * </ul>
96   *
97   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
98   * @version $Rev$, $Date$
99   */
100 public class SchemaOperationControl
101 {
102     private static final Logger LOG = LoggerFactory.getLogger( SchemaOperationControl.class );
103 
104     // indices of handlers and object ids into arrays
105     private static final int COMPARATOR_INDEX = 0;
106     private static final int NORMALIZER_INDEX = 1;
107     private static final int SYNTAX_CHECKER_INDEX = 2;
108     private static final int SYNTAX_INDEX = 3;
109     private static final int MATCHING_RULE_INDEX = 4;
110     private static final int ATTRIBUTE_TYPE_INDEX = 5;
111     private static final int OBJECT_CLASS_INDEX = 6;
112     private static final int MATCHING_RULE_USE_INDEX = 7;
113     private static final int DIT_STRUCTURE_RULE_INDEX = 8;
114     private static final int DIT_CONTENT_RULE_INDEX = 9;
115     private static final int NAME_FORM_INDEX = 10;
116 
117     private static final Set<String> VALID_OU_VALUES = new HashSet<String>();
118     private static final String[] OP_ATTRS = new String[] {
119         SchemaConstants.COMPARATORS_AT,
120         SchemaConstants.NORMALIZERS_AT,
121         SchemaConstants.SYNTAX_CHECKERS_AT,
122         SchemaConstants.LDAP_SYNTAXES_AT,
123         SchemaConstants.MATCHING_RULES_AT,
124         SchemaConstants.ATTRIBUTE_TYPES_AT,
125         SchemaConstants.OBJECT_CLASSES_AT,
126         SchemaConstants.MATCHING_RULE_USE_AT,
127         SchemaConstants.DIT_STRUCTURE_RULES_AT,
128         SchemaConstants.DIT_CONTENT_RULES_AT,
129         SchemaConstants.NAME_FORMS_AT
130     };
131     private static final String[] META_OBJECT_CLASSES = new String[] {
132         "metaComparator",
133         "metaNormalizer",
134         "metaSyntaxChecker",
135         "metaSyntax",
136         "metaMatchingRule",
137         "metaAttributeType",
138         "metaObjectClass",
139         "metaMatchingRuleUse",
140         "metaDITStructureRule",
141         "metaDITContentRule",
142         "metaNameForm"
143     };
144     private static final java.util.Collection<String> SCHEMA_MODIFICATION_ATTRIBUTES_UPDATE_BYPASS;
145 
146     private final MetaSchemaHandler metaSchemaHandler;
147     private final Registries registries;
148     private final AttributeType objectClassAT;
149     private final SchemaSubentryModifier subentryModifier;
150     private final SchemaChangeHandler[] schemaObjectHandlers = new SchemaChangeHandler[11];
151 
152     private final DescriptionParsers parsers;
153     
154     private final Map<String, SchemaChangeHandler> opAttr2handlerMap = new HashMap<String, SchemaChangeHandler>();
155     private final Map<String, SchemaChangeHandler> objectClass2handlerMap = new HashMap<String, SchemaChangeHandler>();
156     
157     /** 
158      * Maps the OID of a subschemaSubentry operational attribute to the index of 
159      * the handler in the schemaObjectHandlers array.
160      */ 
161     private final Map<String, Integer> opAttr2handlerIndex = new HashMap<String, Integer>( 11 );
162     private static final String CASCADING_ERROR =
163             "Cascading has not yet been implemented: standard operation is in effect.";
164 
165 
166     static 
167     {
168         VALID_OU_VALUES.add( SchemaConstants.NORMALIZERS_AT.toLowerCase() );
169         VALID_OU_VALUES.add( SchemaConstants.COMPARATORS_AT.toLowerCase() );
170         VALID_OU_VALUES.add( SchemaConstants.SYNTAX_CHECKERS_AT.toLowerCase() );
171         VALID_OU_VALUES.add( "syntaxes".toLowerCase() );
172         VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULES_AT.toLowerCase() );
173         VALID_OU_VALUES.add( SchemaConstants.MATCHING_RULE_USE_AT.toLowerCase() );
174         VALID_OU_VALUES.add( SchemaConstants.ATTRIBUTE_TYPES_AT.toLowerCase() );
175         VALID_OU_VALUES.add( SchemaConstants.OBJECT_CLASSES_AT.toLowerCase() );
176         VALID_OU_VALUES.add( SchemaConstants.NAME_FORMS_AT.toLowerCase() );
177         VALID_OU_VALUES.add( SchemaConstants.DIT_CONTENT_RULES_AT.toLowerCase() );
178         VALID_OU_VALUES.add( SchemaConstants.DIT_STRUCTURE_RULES_AT.toLowerCase() );
179         
180         HashSet<String> c = new HashSet<String>();
181         c.add( NormalizationInterceptor.class.getName() );
182         c.add( AuthenticationInterceptor.class.getName() );
183         c.add( AciAuthorizationInterceptor.class.getName() );
184         c.add( DefaultAuthorizationInterceptor.class.getName() );
185         c.add( ExceptionInterceptor.class.getName() );
186 //        c.add( ChangeLogInterceptor.class.getName() );
187 //        c.add( OperationalAttributeInterceptor.class.getName() );
188         c.add( SchemaInterceptor.class.getName() );
189 //        c.add( SubentryInterceptor.class.getName() );
190         c.add( CollectiveAttributeInterceptor.class.getName() );
191 //        c.add( EventInterceptor.class.getName() );
192 //        c.add( TriggerInterceptor.class.getName() );
193         SCHEMA_MODIFICATION_ATTRIBUTES_UPDATE_BYPASS = Collections.unmodifiableCollection( c );
194     }
195 
196 
197     public SchemaOperationControl( Registries registries, PartitionSchemaLoader loader, SchemaPartitionDao dao )
198         throws Exception
199     {
200         this.registries = registries;
201         this.objectClassAT = this.registries.getAttributeTypeRegistry()
202             .lookup( SchemaConstants.OBJECT_CLASS_AT );
203         
204         this.metaSchemaHandler = new MetaSchemaHandler( this.registries, loader );
205         
206         this.schemaObjectHandlers[COMPARATOR_INDEX] = new MetaComparatorHandler( registries, loader ); 
207         this.schemaObjectHandlers[NORMALIZER_INDEX] = new MetaNormalizerHandler( registries, loader );
208         this.schemaObjectHandlers[SYNTAX_CHECKER_INDEX] = new MetaSyntaxCheckerHandler( registries, loader );
209         this.schemaObjectHandlers[SYNTAX_INDEX] = new MetaSyntaxHandler( registries, loader, dao );
210         this.schemaObjectHandlers[MATCHING_RULE_INDEX] = new MetaMatchingRuleHandler( registries, loader, dao );
211         this.schemaObjectHandlers[ATTRIBUTE_TYPE_INDEX] = new MetaAttributeTypeHandler( registries, loader, dao );
212         this.schemaObjectHandlers[OBJECT_CLASS_INDEX] = new MetaObjectClassHandler( registries, loader, dao );
213         this.schemaObjectHandlers[MATCHING_RULE_USE_INDEX] = new MetaMatchingRuleUseHandler( registries, loader );
214         this.schemaObjectHandlers[DIT_STRUCTURE_RULE_INDEX] = new MetaDitStructureRuleHandler( registries, loader ); 
215         this.schemaObjectHandlers[DIT_CONTENT_RULE_INDEX] = new MetaDitContentRuleHandler( registries, loader ); 
216         this.schemaObjectHandlers[NAME_FORM_INDEX] = new MetaNameFormHandler( registries, loader ); 
217 
218         this.subentryModifier = new SchemaSubentryModifier( registries, dao );
219         this.parsers = new DescriptionParsers( registries, dao );
220         
221         OidRegistry oidRegistry = registries.getOidRegistry();
222 
223         String comparatorsOid = oidRegistry.getOid( SchemaConstants.COMPARATORS_AT );
224         opAttr2handlerIndex.put( comparatorsOid, COMPARATOR_INDEX );
225 
226         String normalizersOid = oidRegistry.getOid( SchemaConstants.NORMALIZERS_AT );
227         opAttr2handlerIndex.put( normalizersOid, NORMALIZER_INDEX );
228 
229         String syntaxCheckersOid = oidRegistry.getOid( SchemaConstants.SYNTAX_CHECKERS_AT );
230         opAttr2handlerIndex.put( syntaxCheckersOid, SYNTAX_CHECKER_INDEX );
231 
232         String ldapSyntaxesOid = oidRegistry.getOid( SchemaConstants.LDAP_SYNTAXES_AT );
233         opAttr2handlerIndex.put( ldapSyntaxesOid, SYNTAX_INDEX );
234 
235         String matchingRulesOid = oidRegistry.getOid( SchemaConstants.MATCHING_RULES_AT );
236         opAttr2handlerIndex.put( matchingRulesOid, MATCHING_RULE_INDEX );
237 
238         String attributeTypesOid = oidRegistry.getOid( SchemaConstants.ATTRIBUTE_TYPES_AT );
239         opAttr2handlerIndex.put( attributeTypesOid, ATTRIBUTE_TYPE_INDEX );
240 
241         String objectClassesOid = oidRegistry.getOid( SchemaConstants.OBJECT_CLASSES_AT );
242         opAttr2handlerIndex.put( objectClassesOid, OBJECT_CLASS_INDEX );
243 
244         String matchingRuleUseOid = oidRegistry.getOid( SchemaConstants.MATCHING_RULE_USE_AT );
245         opAttr2handlerIndex.put( matchingRuleUseOid, MATCHING_RULE_USE_INDEX );
246 
247         String ditStructureRulesOid = oidRegistry.getOid( SchemaConstants.DIT_STRUCTURE_RULES_AT );
248         opAttr2handlerIndex.put( ditStructureRulesOid, DIT_STRUCTURE_RULE_INDEX );
249 
250         String ditContentRulesOid = oidRegistry.getOid( SchemaConstants.DIT_CONTENT_RULES_AT );
251         opAttr2handlerIndex.put( ditContentRulesOid, DIT_CONTENT_RULE_INDEX );
252 
253         String nameFormsOid = oidRegistry.getOid( SchemaConstants.NAME_FORMS_AT );
254         opAttr2handlerIndex.put( nameFormsOid, NAME_FORM_INDEX );
255         
256         initHandlerMaps();
257     }
258 
259     
260     private void initHandlerMaps() throws NamingException
261     {
262         AttributeTypeRegistry atReg = registries.getAttributeTypeRegistry();
263         for ( int ii = 0; ii < OP_ATTRS.length; ii++ )
264         {
265             AttributeType at = atReg.lookup( OP_ATTRS[ii] );
266             opAttr2handlerMap.put( at.getOid(), schemaObjectHandlers[ii] );
267         }
268 
269         ObjectClassRegistry ocReg = registries.getObjectClassRegistry();
270         for ( int ii = 0; ii < META_OBJECT_CLASSES.length; ii++ )
271         {
272             ObjectClass oc = ocReg.lookup( META_OBJECT_CLASSES[ii] );
273             objectClass2handlerMap.put( oc.getOid(), schemaObjectHandlers[ii] );
274         }
275     }
276     
277     
278     public Registries getGlobalRegistries()
279     {
280         return registries;
281     }
282     
283     
284     public Registries getRegistries( LdapDN dn )
285     {
286         LOG.error( "Ignoring request for specific registries under dn {}", dn );
287         throw new NotImplementedException();
288     }
289 
290 
291     public void add( AddOperationContext opContext ) throws Exception
292     {
293         EntryAttribute oc = opContext.getEntry().get( objectClassAT );
294         
295         for ( Value<?> value:oc )
296         {
297 
298             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
299             
300             if ( objectClass2handlerMap.containsKey( oid ) )
301             {
302                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
303                 handler.add( opContext.getDn(), opContext.getEntry() );
304                 updateSchemaModificationAttributes( opContext );
305                 return;
306             }
307         }
308         
309         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
310         {
311             metaSchemaHandler.add( opContext.getDn(), opContext.getEntry() );
312             updateSchemaModificationAttributes( opContext );
313             return;
314         }
315         
316         if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
317         {
318             if ( opContext.getDn().size() != 3 )
319             {
320                 throw new LdapInvalidNameException( 
321                     "Schema entity containers of objectClass organizationalUnit should be 3 name components in length.", 
322                     ResultCodeEnum.NAMING_VIOLATION );
323             }
324             
325             String ouValue = ( String ) opContext.getDn().getRdn().getValue();
326             ouValue = ouValue.trim().toLowerCase();
327             if ( ! VALID_OU_VALUES.contains( ouValue ) )
328             {
329                 throw new LdapInvalidNameException( 
330                     "Expecting organizationalUnit with one of the following names: " + VALID_OU_VALUES, 
331                     ResultCodeEnum.NAMING_VIOLATION );
332             }
333             return;
334         }
335 
336         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
337     }
338     
339 
340     public void delete( DeleteOperationContext opContext, ClonedServerEntry entry, boolean doCascadeDelete ) 
341         throws Exception
342     {
343         EntryAttribute oc = entry.get( objectClassAT );
344         
345         for ( Value<?> value:oc )
346         {
347             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
348             
349             if ( objectClass2handlerMap.containsKey( oid ) )
350             {
351                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
352                 handler.delete( opContext.getDn(), entry, doCascadeDelete );
353                 updateSchemaModificationAttributes( opContext );
354                 return;
355             }
356         }
357 
358         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
359         {
360             metaSchemaHandler.delete( opContext.getDn(), entry, doCascadeDelete );
361             updateSchemaModificationAttributes( opContext );
362             return;
363         }
364         
365         if ( oc.contains( SchemaConstants.ORGANIZATIONAL_UNIT_OC ) )
366         {
367             if ( opContext.getDn().size() != 3 )
368             {
369                 throw new LdapNamingException( 
370                     "Only schema entity containers of objectClass organizationalUnit with 3 name components in length" +
371                     " can be deleted.", ResultCodeEnum.UNWILLING_TO_PERFORM );
372             }
373             
374             String ouValue = ( String ) opContext.getDn().getRdn().getValue();
375             ouValue = ouValue.trim().toLowerCase();
376             if ( ! VALID_OU_VALUES.contains( ouValue ) )
377             {
378                 throw new LdapInvalidNameException( 
379                     "Can only delete organizationalUnit entity containers with one of the following names: " 
380                     + VALID_OU_VALUES, ResultCodeEnum.NAMING_VIOLATION );
381             }
382             return;
383         }
384 
385         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
386     }
387     
388 
389     public void modify( ModifyOperationContext opContext, ModificationOperation modOp, ServerEntry mods, 
390         ServerEntry entry, ServerEntry targetEntry, boolean cascade ) throws Exception
391     {
392         EntryAttribute oc = entry.get( objectClassAT );
393         
394         for ( Value<?> value:oc )
395         {
396             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
397             
398             if ( objectClass2handlerMap.containsKey( oid ) )
399             {
400                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
401                 handler.modify( opContext.getDn(), modOp, mods, entry, targetEntry, cascade );
402                 updateSchemaModificationAttributes( opContext );
403                 return;
404             }
405         }
406 
407         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
408         {
409             metaSchemaHandler.modify( opContext.getDn(), modOp, mods, entry, targetEntry, cascade );
410             updateSchemaModificationAttributes( opContext );
411             return;
412         }
413         
414         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
415     }
416 
417 
418     public void modify( ModifyOperationContext opContext, ServerEntry entry, 
419         ServerEntry targetEntry, boolean doCascadeModify ) throws Exception
420     {
421         EntryAttribute oc = entry.get( objectClassAT );
422         
423         for ( Value<?> value:oc )
424         {
425             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
426             
427             if ( objectClass2handlerMap.containsKey( oid ) )
428             {
429                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
430                 handler.modify( opContext.getDn(), opContext.getModItems(), entry, targetEntry, doCascadeModify );
431                 updateSchemaModificationAttributes( opContext );
432                 return;
433             }
434         }
435 
436         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
437         {
438             metaSchemaHandler.modify( opContext.getDn(), opContext.getModItems(), entry, targetEntry, doCascadeModify );
439             updateSchemaModificationAttributes( opContext );
440             return;
441         }
442 
443         LOG.error( String.format( "Unwilling to perform modify on %s:\n\nEntry:\n%s\n\nModifications:\n%s", 
444             opContext.getDn(), entry, opContext.getModItems() ) );
445         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
446     }
447 
448 
449     public void modifyRn( RenameOperationContext opContext, ServerEntry entry, boolean doCascadeModify ) 
450         throws Exception
451     {
452         EntryAttribute oc = entry.get( objectClassAT );
453         
454         for ( Value<?> value:oc )
455         {
456             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
457             
458             if ( objectClass2handlerMap.containsKey( oid ) )
459             {
460                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
461                 handler.rename( opContext.getDn(), entry, opContext.getNewRdn(), doCascadeModify );
462                 updateSchemaModificationAttributes( opContext );
463                 return;
464             }
465         }
466 
467         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
468         {
469             metaSchemaHandler.rename( opContext.getDn(), entry, opContext.getNewRdn(), doCascadeModify );
470             updateSchemaModificationAttributes( opContext );
471             return;
472         }
473         
474         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
475     }
476 
477 
478     public void replace( MoveOperationContext opContext, ServerEntry entry, boolean cascade ) throws Exception
479     {
480         EntryAttribute oc = entry.get( objectClassAT );
481         
482         for ( Value<?> value:oc )
483         {
484             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
485             
486             if ( objectClass2handlerMap.containsKey( oid ) )
487             {
488                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
489                 handler.replace( opContext.getDn(), opContext.getParent(), entry, cascade );
490                 updateSchemaModificationAttributes( opContext );
491                 return;
492             }
493         }
494 
495         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
496         {
497             metaSchemaHandler.replace( opContext.getDn(), opContext.getParent(), entry, cascade );
498             updateSchemaModificationAttributes( opContext );
499             return;
500         }
501         
502         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
503     }
504 
505 
506     public void move( MoveAndRenameOperationContext opContext, ServerEntry entry, boolean cascade ) throws Exception
507     {
508         EntryAttribute oc = entry.get( objectClassAT );
509         
510         for ( Value<?> value:oc )
511         {
512             String oid = registries.getOidRegistry().getOid( ( String ) value.get() );
513             
514             if ( objectClass2handlerMap.containsKey( oid ) )
515             {
516                 SchemaChangeHandler handler = objectClass2handlerMap.get( oid );
517                 handler.move( opContext.getDn(), opContext.getParent(), opContext.getNewRdn(), 
518                     opContext.getDelOldDn(), entry, cascade );
519                 updateSchemaModificationAttributes( opContext );
520                 return;
521             }
522         }
523 
524         if ( oc.contains( MetaSchemaConstants.META_SCHEMA_OC ) )
525         {
526             metaSchemaHandler.move( opContext.getDn(), opContext.getParent(), opContext.getNewRdn(), 
527                 opContext.getDelOldDn(), entry, cascade );
528             updateSchemaModificationAttributes( opContext );
529             return;
530         }
531         
532         throw new LdapOperationNotSupportedException( ResultCodeEnum.UNWILLING_TO_PERFORM );
533     }
534 
535     
536     /**
537      * Translates modify operations on schema subentries into one or more operations 
538      * on meta schema entities within the ou=schema partition and updates the registries
539      * accordingly.  This uses direct access to the partition to bypass all interceptors.
540      * 
541      * @param name the name of the subentry
542      * @param mods the modification operations performed on the subentry
543      * @param subentry the attributes of the subentry
544      * @param targetSubentry the target subentry after being modified
545      * @param doCascadeModify determines if a cascading operation should be performed
546      * to effect all dependents on the changed entity
547      * @throws NamingException if the operation fails
548      */
549     public void modifySchemaSubentry( ModifyOperationContext opContext, 
550         ServerEntry subentry, ServerEntry targetSubentry, boolean doCascadeModify ) throws Exception 
551     {
552         for ( Modification mod : opContext.getModItems() )
553         {
554             String opAttrOid = registries.getOidRegistry().getOid( mod.getAttribute().getId() );
555             
556             ServerAttribute serverAttribute = (ServerAttribute)mod.getAttribute();
557 
558             switch ( mod.getOperation() )
559             {
560                 case ADD_ATTRIBUTE :
561 
562                     modifyAddOperation( opContext, opAttrOid, serverAttribute, doCascadeModify );
563                     break;
564                     
565                 case REMOVE_ATTRIBUTE :
566                     modifyRemoveOperation( opContext, opAttrOid, serverAttribute, doCascadeModify );
567                     break; 
568                     
569                 case REPLACE_ATTRIBUTE :
570                     throw new LdapOperationNotSupportedException( 
571                         "Modify REPLACE operations on schema subentries are not allowed: " +
572                         "it's just silly to destroy and recreate so many \nschema entities " +
573                         "that reside in schema operational attributes.  Instead use \na " +
574                         "targeted combination of modify ADD and REMOVE operations.", 
575                         ResultCodeEnum.UNWILLING_TO_PERFORM );
576                 
577                 default:
578                     throw new IllegalStateException( "Undefined modify operation: " + mod.getOperation() );
579             }
580         }
581         
582         if ( opContext.getModItems().size() > 0 )
583         {
584             updateSchemaModificationAttributes( opContext );
585         }
586     }
587     
588     
589     /**
590      * Translates modify operations on schema subentries into one or more operations 
591      * on meta schema entities within the ou=schema partition and updates the registries
592      * accordingly.  This uses direct access to the partition to bypass all interceptors.
593      * 
594      * @param name the name of the subentry
595      * @param modOp the modification operation performed on the subentry
596      * @param mods the modification operations performed on the subentry
597      * @param subentry the attributes of the subentry
598      * @param targetSubentry the target subentry after being modified
599      * @param doCascadeModify determines if a cascading operation should be performed
600      * to effect all dependents on the changed entity
601      * @throws NamingException if the modify fails
602      */
603     public void modifySchemaSubentry( ModifyOperationContext opContext, LdapDN name, int modOp, ServerEntry mods, 
604         ServerEntry subentry, ServerEntry targetSubentry, boolean doCascadeModify ) throws Exception
605     {
606         Set<AttributeType> attributeTypes = mods.getAttributeTypes();
607         
608         switch ( modOp )
609         {
610             case( DirContext.ADD_ATTRIBUTE ):
611                 for ( AttributeType attributeType:attributeTypes )
612                 {
613                     modifyAddOperation( opContext, attributeType.getOid(), 
614                         mods.get( attributeType ), doCascadeModify );
615                 }
616             
617                 break;
618                 
619             case( DirContext.REMOVE_ATTRIBUTE ):
620                 for ( AttributeType attributeType:attributeTypes )
621                 {
622                     modifyRemoveOperation( opContext, attributeType.getOid(), 
623                         mods.get( attributeType ), doCascadeModify );
624                 }
625             
626                 break;
627                 
628             case( DirContext.REPLACE_ATTRIBUTE ):
629                 throw new LdapOperationNotSupportedException( 
630                     "Modify REPLACE operations on schema subentries are not allowed: " +
631                     "it's just silly to destroy and recreate so many \nschema entities " +
632                     "that reside in schema operational attributes.  Instead use \na " +
633                     "targeted combination of modify ADD and REMOVE operations.", 
634                     ResultCodeEnum.UNWILLING_TO_PERFORM );
635             
636             default:
637                 throw new IllegalStateException( "Undefined modify operation: " + modOp );
638         }
639         
640         updateSchemaModificationAttributes( opContext );
641     }
642 
643     
644     public String getSchema( AbstractSchemaDescription desc ) 
645     {
646         if ( desc.getExtensions().containsKey( MetaSchemaConstants.X_SCHEMA ) )
647         {
648             return desc.getExtensions().get( MetaSchemaConstants.X_SCHEMA ).get( 0 );
649         }
650         
651         return MetaSchemaConstants.SCHEMA_OTHER;
652     }
653     
654 
655     /**
656      * Handles the modify remove operation on the subschemaSubentry for schema entities. 
657      * 
658      * @param opAttrOid the numeric id of the operational attribute modified
659      * @param mods the attribute with the modifications
660      * @param doCascadeModify determines if a cascading operation should be performed
661      * to effect all dependents on the changed entity
662      * @throws NamingException if there are problems updating the registries and the
663      * schema partition
664      */
665     private void modifyRemoveOperation( ModifyOperationContext opContext, String opAttrOid, 
666         EntryAttribute mods, boolean doCascadeModify ) throws Exception
667     {
668         int index = opAttr2handlerIndex.get( opAttrOid );
669         SchemaChangeHandler handler = opAttr2handlerMap.get( opAttrOid );
670         
671         switch( index )
672         {
673             case( COMPARATOR_INDEX ):
674                 MetaComparatorHandler comparatorHandler = ( MetaComparatorHandler ) handler;
675                 ComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
676                 
677                 for ( ComparatorDescription comparatorDescription : comparatorDescriptions )
678                 {
679                     comparatorHandler.delete( comparatorDescription.getNumericOid(), doCascadeModify );
680                     subentryModifier.delete( opContext, comparatorDescription );
681                 }
682                 break;
683             case( NORMALIZER_INDEX ):
684                 MetaNormalizerHandler normalizerHandler = ( MetaNormalizerHandler ) handler;
685                 NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
686                 
687                 for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
688                 {
689                     normalizerHandler.delete( normalizerDescription.getNumericOid(), doCascadeModify );
690                     subentryModifier.delete( opContext, normalizerDescription );
691                 }
692                 break;
693             case( SYNTAX_CHECKER_INDEX ):
694                 MetaSyntaxCheckerHandler syntaxCheckerHandler = ( MetaSyntaxCheckerHandler ) handler;
695                 SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
696                 
697                 for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
698                 {
699                     syntaxCheckerHandler.delete( syntaxCheckerDescription.getNumericOid(), doCascadeModify );
700                     subentryModifier.delete( opContext, syntaxCheckerDescription );
701                 }
702                 break;
703             case( SYNTAX_INDEX ):
704                 MetaSyntaxHandler syntaxHandler = ( MetaSyntaxHandler ) handler;
705                 Syntax[] syntaxes = parsers.parseSyntaxes( mods );
706                 
707                 for ( Syntax syntax : syntaxes )
708                 {
709                     syntaxHandler.delete( syntax, doCascadeModify );
710                     subentryModifier.deleteSchemaObject( opContext, syntax );
711                 }
712                 break;
713             case( MATCHING_RULE_INDEX ):
714                 MetaMatchingRuleHandler matchingRuleHandler = ( MetaMatchingRuleHandler ) handler;
715                 MatchingRule[] mrs = parsers.parseMatchingRules( mods );
716                 
717                 for ( MatchingRule mr : mrs )
718                 {
719                     matchingRuleHandler.delete( mr, doCascadeModify );
720                     subentryModifier.deleteSchemaObject( opContext, mr );
721                 }
722                 break;
723             case( ATTRIBUTE_TYPE_INDEX ):
724                 MetaAttributeTypeHandler atHandler = ( MetaAttributeTypeHandler ) handler;
725                 AttributeType[] ats = parsers.parseAttributeTypes( mods );
726                 
727                 for ( AttributeType at : ats )
728                 {
729                     atHandler.delete( at, doCascadeModify );
730                     subentryModifier.deleteSchemaObject( opContext, at );
731                 }
732                 break;
733             case( OBJECT_CLASS_INDEX ):
734                 MetaObjectClassHandler ocHandler = ( MetaObjectClassHandler ) handler;
735                 ObjectClass[] ocs = parsers.parseObjectClasses( mods );
736 
737                 for ( ObjectClass oc : ocs )
738                 {
739                     ocHandler.delete( oc, doCascadeModify );
740                     subentryModifier.deleteSchemaObject( opContext, oc );
741                 }
742                 break;
743             case( MATCHING_RULE_USE_INDEX ):
744                 MetaMatchingRuleUseHandler mruHandler = ( MetaMatchingRuleUseHandler ) handler;
745                 MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
746                 
747                 for ( MatchingRuleUse mru : mrus )
748                 {
749                     mruHandler.delete( mru, doCascadeModify );
750                     subentryModifier.deleteSchemaObject( opContext, mru );
751                 }
752                 break;
753             case( DIT_STRUCTURE_RULE_INDEX ):
754                 MetaDitStructureRuleHandler dsrHandler = ( MetaDitStructureRuleHandler ) handler;
755                 DITStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
756                 
757                 for ( DITStructureRule dsr : dsrs )
758                 {
759                     dsrHandler.delete( dsr, doCascadeModify );
760                     subentryModifier.deleteSchemaObject( opContext, dsr );
761                 }
762                 break;
763             case( DIT_CONTENT_RULE_INDEX ):
764                 MetaDitContentRuleHandler dcrHandler = ( MetaDitContentRuleHandler ) handler;
765                 DITContentRule[] dcrs = parsers.parseDitContentRules( mods );
766                 
767                 for ( DITContentRule dcr : dcrs )
768                 {
769                     dcrHandler.delete( dcr, doCascadeModify );
770                     subentryModifier.deleteSchemaObject( opContext, dcr );
771                 }
772                 break;
773             case( NAME_FORM_INDEX ):
774                 MetaNameFormHandler nfHandler = ( MetaNameFormHandler ) handler;
775                 NameForm[] nfs = parsers.parseNameForms( mods );
776                 
777                 for ( NameForm nf : nfs )
778                 {
779                     nfHandler.delete( nf, doCascadeModify );
780                     subentryModifier.deleteSchemaObject( opContext, nf );
781                 }
782                 break;
783             default:
784                 throw new IllegalStateException( "Unknown index into handler array: " + index );
785         }
786     }
787     
788     
789     /**
790      * Handles the modify add operation on the subschemaSubentry for schema entities. 
791      * 
792      * @param opAttrOid the numeric id of the operational attribute modified
793      * @param mods the attribute with the modifications
794      * @param doCascadeModify determines if a cascading operation should be performed
795      * to effect all dependents on the changed entity
796      * @throws NamingException if there are problems updating the registries and the
797      * schema partition
798      */
799     private void modifyAddOperation( ModifyOperationContext opContext, String opAttrOid, 
800         EntryAttribute mods, boolean doCascadeModify ) throws Exception
801     {
802         if ( doCascadeModify )
803         {
804             LOG.error( CASCADING_ERROR );
805         }
806 
807         int index = opAttr2handlerIndex.get( opAttrOid );
808         SchemaChangeHandler handler = opAttr2handlerMap.get( opAttrOid );
809         
810         switch( index )
811         {
812             case( COMPARATOR_INDEX ):
813                 MetaComparatorHandler comparatorHandler = ( MetaComparatorHandler ) handler;
814                 ComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
815                 
816                 for ( ComparatorDescription comparatorDescription : comparatorDescriptions )
817                 {
818                     comparatorHandler.add( comparatorDescription );
819                     subentryModifier.add( opContext, comparatorDescription );
820                 }
821                 break;
822             case( NORMALIZER_INDEX ):
823                 MetaNormalizerHandler normalizerHandler = ( MetaNormalizerHandler ) handler;
824                 NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
825                 
826                 for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
827                 {
828                     normalizerHandler.add( normalizerDescription );
829                     subentryModifier.add( opContext, normalizerDescription );
830                 }
831                 break;
832             case( SYNTAX_CHECKER_INDEX ):
833                 MetaSyntaxCheckerHandler syntaxCheckerHandler = ( MetaSyntaxCheckerHandler ) handler;
834                 SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
835                 
836                 for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
837                 {
838                     syntaxCheckerHandler.add( syntaxCheckerDescription );
839                     subentryModifier.add( opContext, syntaxCheckerDescription );
840                 }
841                 break;
842             case( SYNTAX_INDEX ):
843                 MetaSyntaxHandler syntaxHandler = ( MetaSyntaxHandler ) handler;
844                 Syntax[] syntaxes = parsers.parseSyntaxes( mods );
845                 
846                 for ( Syntax syntax : syntaxes )
847                 {
848                     syntaxHandler.add( syntax );
849                     subentryModifier.addSchemaObject( opContext, syntax );
850                 }
851                 break;
852             case( MATCHING_RULE_INDEX ):
853                 MetaMatchingRuleHandler matchingRuleHandler = ( MetaMatchingRuleHandler ) handler;
854                 MatchingRule[] mrs = parsers.parseMatchingRules( mods );
855                 
856                 for ( MatchingRule mr : mrs )
857                 {
858                     matchingRuleHandler.add( mr );
859                     subentryModifier.addSchemaObject( opContext, mr );
860                 }
861                 break;
862             case( ATTRIBUTE_TYPE_INDEX ):
863                 MetaAttributeTypeHandler atHandler = ( MetaAttributeTypeHandler ) handler;
864                 AttributeType[] ats = parsers.parseAttributeTypes( mods );
865                 
866                 for ( AttributeType at : ats )
867                 {
868                     atHandler.add( at );
869                     subentryModifier.addSchemaObject( opContext, at );
870                 }
871                 break;
872             case( OBJECT_CLASS_INDEX ):
873                 MetaObjectClassHandler ocHandler = ( MetaObjectClassHandler ) handler;
874                 ObjectClass[] ocs = parsers.parseObjectClasses( mods );
875 
876                 for ( ObjectClass oc : ocs )
877                 {
878                     ocHandler.add( oc );
879                     subentryModifier.addSchemaObject( opContext, oc );
880                 }
881                 break;
882             case( MATCHING_RULE_USE_INDEX ):
883                 MetaMatchingRuleUseHandler mruHandler = ( MetaMatchingRuleUseHandler ) handler;
884                 MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
885                 
886                 for ( MatchingRuleUse mru : mrus )
887                 {
888                     mruHandler.add( mru );
889                     subentryModifier.addSchemaObject( opContext, mru );
890                 }
891                 break;
892             case( DIT_STRUCTURE_RULE_INDEX ):
893                 MetaDitStructureRuleHandler dsrHandler = ( MetaDitStructureRuleHandler ) handler;
894                 DITStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
895                 
896                 for ( DITStructureRule dsr : dsrs )
897                 {
898                     dsrHandler.add( dsr );
899                     subentryModifier.addSchemaObject( opContext, dsr );
900                 }
901                 break;
902             case( DIT_CONTENT_RULE_INDEX ):
903                 MetaDitContentRuleHandler dcrHandler = ( MetaDitContentRuleHandler ) handler;
904                 DITContentRule[] dcrs = parsers.parseDitContentRules( mods );
905                 
906                 for ( DITContentRule dcr : dcrs )
907                 {
908                     dcrHandler.add( dcr );
909                     subentryModifier.addSchemaObject( opContext, dcr );
910                 }
911                 break;
912             case( NAME_FORM_INDEX ):
913                 MetaNameFormHandler nfHandler = ( MetaNameFormHandler ) handler;
914                 NameForm[] nfs = parsers.parseNameForms( mods );
915                 
916                 for ( NameForm nf : nfs )
917                 {
918                     nfHandler.add( nf );
919                     subentryModifier.addSchemaObject( opContext, nf );
920                 }
921                 break;
922             default:
923                 throw new IllegalStateException( "Unknown index into handler array: " + index );
924         }
925     }
926     
927     
928     /**
929      * Updates the schemaModifiersName and schemaModifyTimestamp attributes of
930      * the schemaModificationAttributes entry for the global schema at 
931      * ou=schema,cn=schemaModifications.  This entry is hardcoded at that 
932      * position for now.
933      * 
934      * The current time is used to set the timestamp and the DN of current user
935      * is set for the modifiersName.
936      * 
937      * @throws NamingException if the update fails
938      */
939     private void updateSchemaModificationAttributes( OperationContext opContext ) throws Exception
940     {
941         String modifiersName = opContext.getSession().getEffectivePrincipal().getJndiName().getNormName();
942         String modifyTimestamp = DateUtils.getGeneralizedTime();
943         
944         List<Modification> mods = new ArrayList<Modification>( 2 );
945         
946         mods.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, 
947             new DefaultServerAttribute( 
948                 ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT,
949                 registries.getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT ),
950                 modifyTimestamp ) ) );
951         
952         mods.add( new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE,
953             new DefaultServerAttribute( 
954                 ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT, 
955                 registries.getAttributeTypeRegistry().lookup( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT ),
956                 modifiersName ) ) );
957         
958         LdapDN name = new LdapDN( "cn=schemaModifications,ou=schema" );
959         name.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() );
960         
961         opContext.modify( name, mods, SCHEMA_MODIFICATION_ATTRIBUTES_UPDATE_BYPASS );
962     }
963 }