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.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
30  import org.apache.directory.api.ldap.model.entry.Attribute;
31  import org.apache.directory.api.ldap.model.entry.Modification;
32  import org.apache.directory.api.ldap.model.exception.LdapException;
33  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
34  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
35  import org.apache.directory.api.ldap.model.schema.AttributeType;
36  import org.apache.directory.api.ldap.model.schema.DitContentRule;
37  import org.apache.directory.api.ldap.model.schema.DitStructureRule;
38  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
39  import org.apache.directory.api.ldap.model.schema.MatchingRule;
40  import org.apache.directory.api.ldap.model.schema.MatchingRuleUse;
41  import org.apache.directory.api.ldap.model.schema.NameForm;
42  import org.apache.directory.api.ldap.model.schema.ObjectClass;
43  import org.apache.directory.api.ldap.model.schema.SchemaManager;
44  import org.apache.directory.api.ldap.model.schema.parsers.LdapComparatorDescription;
45  import org.apache.directory.api.ldap.model.schema.parsers.NormalizerDescription;
46  import org.apache.directory.api.ldap.model.schema.parsers.SyntaxCheckerDescription;
47  import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
48  import org.apache.directory.api.util.Strings;
49  import org.apache.directory.server.core.api.DirectoryService;
50  import org.apache.directory.server.core.api.DnFactory;
51  import org.apache.directory.server.core.api.InterceptorEnum;
52  import org.apache.directory.server.core.api.OperationEnum;
53  import org.apache.directory.server.core.api.interceptor.Interceptor;
54  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
55  import org.apache.directory.server.core.api.schema.DescriptionParsers;
56  import org.apache.directory.server.i18n.I18n;
57  import org.slf4j.Logger;
58  import org.slf4j.LoggerFactory;
59  
60  
61  /**
62   *
63   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
64   */
65  public class SchemaSubentryManager
66  {
67      /** A logger for this class */
68      private static final Logger LOG = LoggerFactory.getLogger( SchemaSubentryManager.class );
69  
70      // indices of handlers and object ids into arrays
71      private static final int COMPARATOR_INDEX = 0;
72      private static final int NORMALIZER_INDEX = 1;
73      private static final int SYNTAX_CHECKER_INDEX = 2;
74      private static final int SYNTAX_INDEX = 3;
75      private static final int MATCHING_RULE_INDEX = 4;
76      private static final int ATTRIBUTE_TYPE_INDEX = 5;
77      private static final int OBJECT_CLASS_INDEX = 6;
78      private static final int MATCHING_RULE_USE_INDEX = 7;
79      private static final int DIT_STRUCTURE_RULE_INDEX = 8;
80      private static final int DIT_CONTENT_RULE_INDEX = 9;
81      private static final int NAME_FORM_INDEX = 10;
82  
83      private static final Set<String> VALID_OU_VALUES = new HashSet<String>();
84  
85      /** The schemaManager */
86      private final SchemaManager schemaManager;
87  
88      private final SchemaSubentryModifier subentryModifier;
89  
90      /** The description parsers */
91      private final DescriptionParsers parsers;
92  
93      /**
94       * Maps the OID of a subschemaSubentry operational attribute to the index of
95       * the handler in the schemaObjectHandlers array.
96       */
97      private final Map<String, Integer> opAttr2handlerIndex = new HashMap<String, Integer>( 11 );
98      private static final String CASCADING_ERROR =
99          "Cascading has not yet been implemented: standard operation is in effect.";
100 
101     private static AttributeType ENTRY_CSN_ATTRIBUTE_TYPE;
102 
103     static
104     {
105         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.NORMALIZERS_AT ) );
106         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.COMPARATORS_AT ) );
107         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.SYNTAX_CHECKERS_AT ) );
108         VALID_OU_VALUES.add( Strings.toLowerCase( "syntaxes" ) );
109         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.MATCHING_RULES_AT ) );
110         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.MATCHING_RULE_USE_AT ) );
111         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.ATTRIBUTE_TYPES_AT ) );
112         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.OBJECT_CLASSES_AT ) );
113         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.NAME_FORMS_AT ) );
114         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.DIT_CONTENT_RULES_AT ) );
115         VALID_OU_VALUES.add( Strings.toLowerCase( SchemaConstants.DIT_STRUCTURE_RULES_AT ) );
116     }
117 
118 
119     public SchemaSubentryManager( SchemaManager schemaManager, SchemaLoader loader, DnFactory dnFactory )
120         throws LdapException
121     {
122         this.schemaManager = schemaManager;
123         this.subentryModifier = new SchemaSubentryModifier( schemaManager, dnFactory );
124         this.parsers = new DescriptionParsers( schemaManager );
125 
126         String comparatorsOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.COMPARATORS_AT );
127         opAttr2handlerIndex.put( comparatorsOid, COMPARATOR_INDEX );
128 
129         String normalizersOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.NORMALIZERS_AT );
130         opAttr2handlerIndex.put( normalizersOid, NORMALIZER_INDEX );
131 
132         String syntaxCheckersOid = schemaManager.getAttributeTypeRegistry().getOidByName(
133             SchemaConstants.SYNTAX_CHECKERS_AT );
134         opAttr2handlerIndex.put( syntaxCheckersOid, SYNTAX_CHECKER_INDEX );
135 
136         String ldapSyntaxesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
137             SchemaConstants.LDAP_SYNTAXES_AT );
138         opAttr2handlerIndex.put( ldapSyntaxesOid, SYNTAX_INDEX );
139 
140         String matchingRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
141             SchemaConstants.MATCHING_RULES_AT );
142         opAttr2handlerIndex.put( matchingRulesOid, MATCHING_RULE_INDEX );
143 
144         String attributeTypesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
145             SchemaConstants.ATTRIBUTE_TYPES_AT );
146         opAttr2handlerIndex.put( attributeTypesOid, ATTRIBUTE_TYPE_INDEX );
147 
148         String objectClassesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
149             SchemaConstants.OBJECT_CLASSES_AT );
150         opAttr2handlerIndex.put( objectClassesOid, OBJECT_CLASS_INDEX );
151 
152         String matchingRuleUseOid = schemaManager.getAttributeTypeRegistry().getOidByName(
153             SchemaConstants.MATCHING_RULE_USE_AT );
154         opAttr2handlerIndex.put( matchingRuleUseOid, MATCHING_RULE_USE_INDEX );
155 
156         String ditStructureRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
157             SchemaConstants.DIT_STRUCTURE_RULES_AT );
158         opAttr2handlerIndex.put( ditStructureRulesOid, DIT_STRUCTURE_RULE_INDEX );
159 
160         String ditContentRulesOid = schemaManager.getAttributeTypeRegistry().getOidByName(
161             SchemaConstants.DIT_CONTENT_RULES_AT );
162         opAttr2handlerIndex.put( ditContentRulesOid, DIT_CONTENT_RULE_INDEX );
163 
164         String nameFormsOid = schemaManager.getAttributeTypeRegistry().getOidByName( SchemaConstants.NAME_FORMS_AT );
165         opAttr2handlerIndex.put( nameFormsOid, NAME_FORM_INDEX );
166 
167         ENTRY_CSN_ATTRIBUTE_TYPE = schemaManager.getAttributeType( SchemaConstants.ENTRY_CSN_AT );
168     }
169 
170 
171     /**
172      * Find the next interceptor in an operation's list of interceptors, assuming that
173      * we are already processing an operation, and we have stopped in a specific
174      * interceptor.<br/>
175      * For instance, if the list of all the interceptors is : <br/>
176      * [A, B, C, D, E, F]<br/>
177      * and we ave two operations op1 and op2 with the following interceptors list : <br/>
178      * op1 -> [A, D, F]<br/>
179      * op2 -> [B, C, E]<br/>
180      * then assuming that we have stopped at D, then op1.next -> F and op2.next -> E.
181      */
182     private Interceptor findNextInterceptor( OperationEnum operation, DirectoryService directoryService )
183     {
184         Interceptor interceptor = null;
185 
186         List<Interceptor> allInterceptors = directoryService.getInterceptors();
187         List<String> operationInterceptors = directoryService.getInterceptors( operation );
188         int position = 0;
189         String addInterceptor = operationInterceptors.get( position );
190 
191         for ( Interceptor inter : allInterceptors )
192         {
193             String interName = inter.getName();
194 
195             if ( interName.equals( InterceptorEnum.SCHEMA_INTERCEPTOR.getName() ) )
196             {
197                 // Found, get out
198                 position++;
199 
200                 if ( position < operationInterceptors.size() )
201                 {
202                     interceptor = directoryService.getInterceptor( operationInterceptors.get( position ) );
203                 }
204 
205                 break;
206             }
207 
208             if ( interName.equals( addInterceptor ) )
209             {
210                 position++;
211                 addInterceptor = operationInterceptors.get( position );
212             }
213         }
214 
215         return interceptor;
216     }
217 
218 
219     /**
220      * Find the position in the operation's list knowing the inteceptor name.
221      */
222     private int findPosition( OperationEnum operation, Interceptor interceptor, DirectoryService directoryService )
223     {
224         int position = 1;
225 
226         List<String> interceptors = directoryService.getInterceptors( operation );
227 
228         String interceptorName = interceptor.getName();
229 
230         for ( String name : interceptors )
231         {
232             if ( name.equals( interceptorName ) )
233             {
234                 break;
235             }
236 
237             position++;
238         }
239 
240         return position;
241     }
242 
243 
244     /**
245      * Update the SubschemaSubentry with all the modifications
246      */
247     public void modifySchemaSubentry( ModifyOperationContext modifyContext, boolean doCascadeModify )
248         throws LdapException
249     {
250         DirectoryService directoryService = modifyContext.getSession().getDirectoryService();
251 
252         // Compute the next interceptor for the Add and Delete operation, starting from
253         // the schemaInterceptor. We also need to get the position of this next interceptor
254         // in the operation's list.
255         Interceptor nextAdd = findNextInterceptor( OperationEnum.ADD, directoryService );
256         int positionAdd = findPosition( OperationEnum.ADD, nextAdd, directoryService );
257         Interceptor nextDelete = findNextInterceptor( OperationEnum.DELETE, directoryService );
258         int positionDelete = findPosition( OperationEnum.DELETE, nextDelete, directoryService );
259 
260         for ( Modification mod : modifyContext.getModItems() )
261         {
262             String opAttrOid = schemaManager.getAttributeTypeRegistry().getOidByName( mod.getAttribute().getId() );
263 
264             Attribute serverAttribute = mod.getAttribute();
265 
266             switch ( mod.getOperation() )
267             {
268                 case ADD_ATTRIBUTE:
269                     modifyAddOperation( nextAdd, positionAdd, modifyContext, opAttrOid, serverAttribute,
270                         doCascadeModify );
271                     break;
272 
273                 case REMOVE_ATTRIBUTE:
274                     modifyRemoveOperation( nextDelete, positionDelete, modifyContext, opAttrOid, serverAttribute );
275                     break;
276 
277                 case REPLACE_ATTRIBUTE:
278                     // a hack to allow entryCSN modification
279                     if ( ENTRY_CSN_ATTRIBUTE_TYPE.equals( serverAttribute.getAttributeType() ) )
280                     {
281                         break;
282                     }
283 
284                     throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM,
285                         I18n.err( I18n.ERR_283 ) );
286 
287                 default:
288                     throw new IllegalStateException( I18n.err( I18n.ERR_284, mod.getOperation() ) );
289             }
290         }
291     }
292 
293 
294     /**
295      * Handles the modify remove operation on the subschemaSubentry for schema entities.
296      * 
297      * @param opAttrOid the numeric id of the operational attribute modified
298      * @param mods the attribute with the modifications
299      * to effect all dependents on the changed entity
300      * @throws Exception if there are problems updating the registries and the
301      * schema partition
302      */
303     private void modifyRemoveOperation( Interceptor nextInterceptor, int position,
304         ModifyOperationContext modifyContext, String opAttrOid,
305         Attribute mods ) throws LdapException
306     {
307         int index = opAttr2handlerIndex.get( opAttrOid );
308 
309         switch ( index )
310         {
311             case ( COMPARATOR_INDEX ):
312                 LdapComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
313 
314                 for ( LdapComparatorDescription comparatorDescription : comparatorDescriptions )
315                 {
316                     subentryModifier.delete( nextInterceptor, position, modifyContext, comparatorDescription );
317                 }
318 
319                 break;
320 
321             case ( NORMALIZER_INDEX ):
322                 NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
323 
324                 for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
325                 {
326                     subentryModifier.delete( nextInterceptor, position, modifyContext, normalizerDescription );
327                 }
328 
329                 break;
330 
331             case ( SYNTAX_CHECKER_INDEX ):
332                 SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
333 
334                 for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
335                 {
336                     subentryModifier.delete( nextInterceptor, position, modifyContext, syntaxCheckerDescription );
337                 }
338 
339                 break;
340 
341             case ( SYNTAX_INDEX ):
342                 LdapSyntax[] syntaxes = parsers.parseLdapSyntaxes( mods );
343 
344                 for ( LdapSyntax syntax : syntaxes )
345                 {
346                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, syntax );
347                 }
348 
349                 break;
350 
351             case ( MATCHING_RULE_INDEX ):
352                 MatchingRule[] mrs = parsers.parseMatchingRules( mods );
353 
354                 for ( MatchingRule mr : mrs )
355                 {
356                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, mr );
357                 }
358 
359                 break;
360 
361             case ( ATTRIBUTE_TYPE_INDEX ):
362                 AttributeType[] ats = parsers.parseAttributeTypes( mods );
363 
364                 for ( AttributeType at : ats )
365                 {
366                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, at );
367                 }
368 
369                 break;
370 
371             case ( OBJECT_CLASS_INDEX ):
372                 ObjectClass[] ocs = parsers.parseObjectClasses( mods );
373 
374                 for ( ObjectClass oc : ocs )
375                 {
376                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, oc );
377                 }
378 
379                 break;
380 
381             case ( MATCHING_RULE_USE_INDEX ):
382                 MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
383 
384                 for ( MatchingRuleUse mru : mrus )
385                 {
386                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, mru );
387                 }
388 
389                 break;
390 
391             case ( DIT_STRUCTURE_RULE_INDEX ):
392                 DitStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
393 
394                 for ( DitStructureRule dsr : dsrs )
395                 {
396                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, dsr );
397                 }
398 
399                 break;
400 
401             case ( DIT_CONTENT_RULE_INDEX ):
402                 DitContentRule[] dcrs = parsers.parseDitContentRules( mods );
403 
404                 for ( DitContentRule dcr : dcrs )
405                 {
406                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, dcr );
407                 }
408 
409                 break;
410 
411             case ( NAME_FORM_INDEX ):
412                 NameForm[] nfs = parsers.parseNameForms( mods );
413 
414                 for ( NameForm nf : nfs )
415                 {
416                     subentryModifier.deleteSchemaObject( nextInterceptor, position, modifyContext, nf );
417                 }
418 
419                 break;
420 
421             default:
422                 throw new IllegalStateException( I18n.err( I18n.ERR_285, index ) );
423         }
424     }
425 
426 
427     /**
428      * Handles the modify add operation on the subschemaSubentry for schema entities.
429      * 
430      * @param opAttrOid the numeric id of the operational attribute modified
431      * @param mods the attribute with the modifications
432      * @param doCascadeModify determines if a cascading operation should be performed
433      * to effect all dependents on the changed entity
434      * @throws Exception if there are problems updating the registries and the
435      * schema partition
436      */
437     private void modifyAddOperation( Interceptor nextInterceptor, int position, ModifyOperationContext modifyContext,
438         String opAttrOid,
439         Attribute mods, boolean doCascadeModify ) throws LdapException
440     {
441         if ( doCascadeModify )
442         {
443             LOG.error( CASCADING_ERROR );
444         }
445 
446         int index = opAttr2handlerIndex.get( opAttrOid );
447 
448         switch ( index )
449         {
450             case ( COMPARATOR_INDEX ):
451                 LdapComparatorDescription[] comparatorDescriptions = parsers.parseComparators( mods );
452 
453                 for ( LdapComparatorDescription comparatorDescription : comparatorDescriptions )
454                 {
455                     subentryModifier.add( nextInterceptor, position, modifyContext, comparatorDescription );
456                 }
457 
458                 break;
459 
460             case ( NORMALIZER_INDEX ):
461                 NormalizerDescription[] normalizerDescriptions = parsers.parseNormalizers( mods );
462 
463                 for ( NormalizerDescription normalizerDescription : normalizerDescriptions )
464                 {
465                     subentryModifier.add( nextInterceptor, position, modifyContext, normalizerDescription );
466                 }
467 
468                 break;
469 
470             case ( SYNTAX_CHECKER_INDEX ):
471                 SyntaxCheckerDescription[] syntaxCheckerDescriptions = parsers.parseSyntaxCheckers( mods );
472 
473                 for ( SyntaxCheckerDescription syntaxCheckerDescription : syntaxCheckerDescriptions )
474                 {
475                     subentryModifier.add( nextInterceptor, position, modifyContext, syntaxCheckerDescription );
476                 }
477 
478                 break;
479 
480             case ( SYNTAX_INDEX ):
481                 LdapSyntax[] syntaxes = parsers.parseLdapSyntaxes( mods );
482 
483                 for ( LdapSyntax syntax : syntaxes )
484                 {
485                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, syntax );
486                 }
487 
488                 break;
489 
490             case ( MATCHING_RULE_INDEX ):
491                 MatchingRule[] mrs = parsers.parseMatchingRules( mods );
492 
493                 for ( MatchingRule mr : mrs )
494                 {
495                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, mr );
496                 }
497 
498                 break;
499 
500             case ( ATTRIBUTE_TYPE_INDEX ):
501                 AttributeType[] ats = parsers.parseAttributeTypes( mods );
502 
503                 for ( AttributeType at : ats )
504                 {
505                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, at );
506                 }
507 
508                 break;
509 
510             case ( OBJECT_CLASS_INDEX ):
511                 ObjectClass[] ocs = parsers.parseObjectClasses( mods );
512 
513                 for ( ObjectClass oc : ocs )
514                 {
515                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, oc );
516                 }
517 
518                 break;
519 
520             case ( MATCHING_RULE_USE_INDEX ):
521                 MatchingRuleUse[] mrus = parsers.parseMatchingRuleUses( mods );
522 
523                 for ( MatchingRuleUse mru : mrus )
524                 {
525                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, mru );
526                 }
527 
528                 break;
529 
530             case ( DIT_STRUCTURE_RULE_INDEX ):
531                 DitStructureRule[] dsrs = parsers.parseDitStructureRules( mods );
532 
533                 for ( DitStructureRule dsr : dsrs )
534                 {
535                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, dsr );
536                 }
537 
538                 break;
539 
540             case ( DIT_CONTENT_RULE_INDEX ):
541                 DitContentRule[] dcrs = parsers.parseDitContentRules( mods );
542 
543                 for ( DitContentRule dcr : dcrs )
544                 {
545                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, dcr );
546                 }
547 
548                 break;
549 
550             case ( NAME_FORM_INDEX ):
551                 NameForm[] nfs = parsers.parseNameForms( mods );
552 
553                 for ( NameForm nf : nfs )
554                 {
555                     subentryModifier.addSchemaObject( nextInterceptor, position, modifyContext, nf );
556                 }
557 
558                 break;
559 
560             default:
561                 throw new IllegalStateException( I18n.err( I18n.ERR_285, index ) );
562         }
563     }
564 }