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.api.ldap.model.schema.registries.helper;
21  
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Set;
25  
26  import org.apache.directory.api.i18n.I18n;
27  import org.apache.directory.api.ldap.model.exception.LdapException;
28  import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
29  import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
30  import org.apache.directory.api.ldap.model.schema.AttributeType;
31  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
32  import org.apache.directory.api.ldap.model.schema.MatchingRule;
33  import org.apache.directory.api.ldap.model.schema.MutableAttributeType;
34  import org.apache.directory.api.ldap.model.schema.UsageEnum;
35  import org.apache.directory.api.ldap.model.schema.registries.AttributeTypeRegistry;
36  import org.apache.directory.api.ldap.model.schema.registries.Registries;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * An helper class used to store all the methods associated with an AttributeType
42   * in relation with the Registries and SchemaManager.
43   * 
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public final class AttributeTypeHelper
47  {
48      /** A logger for this class */
49      private static final Logger LOG = LoggerFactory.getLogger( AttributeTypeHelper.class );
50  
51      private AttributeTypeHelper()
52      {
53      }
54  
55      /**
56       * Inject the AttributeType into the Registries, updating the references to
57       * other SchemaObject
58       *
59       * If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX),
60       * an exception is thrown.
61       * 
62       * @param attributeType The AttributeType to add to the Registries
63       * @param errors The errors we got while adding the AttributeType to the Registries
64       * @param registries The Registries
65       * @throws LdapException If the AttributeType is not valid
66       */
67      public static void addToRegistries( MutableAttributeType attributeType, List<Throwable> errors, Registries registries ) throws LdapException
68      {
69          if ( registries != null )
70          {
71              try
72              {
73                  attributeType.unlock();
74                  AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
75      
76                  // The superior
77                  if ( !buildSuperior( attributeType, errors, registries ) )
78                  {
79                      // We have had errors, let's stop here as we need a correct superior to continue
80                      return;
81                  }
82      
83                  // The Syntax
84                  buildSyntax( attributeType, errors, registries );
85      
86                  // The EQUALITY matching rule
87                  buildEquality( attributeType, errors, registries );
88      
89                  // The ORDERING matching rule
90                  buildOrdering( attributeType, errors, registries );
91      
92                  // The SUBSTR matching rule
93                  buildSubstring( attributeType, errors, registries );
94      
95                  // Check the USAGE
96                  checkUsage( attributeType, errors );
97      
98                  // Check the COLLECTIVE element
99                  checkCollective( attributeType, errors );
100     
101                 // Inject the attributeType into the oid/normalizer map
102                 attributeTypeRegistry.addMappingFor( attributeType );
103     
104                 // Register this AttributeType into the Descendant map
105                 attributeTypeRegistry.registerDescendants( attributeType, attributeType.getSuperior() );
106     
107                 /**
108                  * Add the AT references (using and usedBy) :
109                  * AT -> MR (for EQUALITY, ORDERING and SUBSTR)
110                  * AT -> S
111                  * AT -> AT
112                  */
113                 if ( attributeType.getEquality() != null )
114                 {
115                     registries.addReference( attributeType, attributeType.getEquality() );
116                 }
117     
118                 if ( attributeType.getOrdering() != null )
119                 {
120                     registries.addReference( attributeType, attributeType.getOrdering() );
121                 }
122     
123                 if ( attributeType.getSubstring() != null )
124                 {
125                     registries.addReference( attributeType, attributeType.getSubstring() );
126                 }
127     
128                 if ( attributeType.getSyntax() != null )
129                 {
130                     registries.addReference( attributeType, attributeType.getSyntax() );
131                 }
132     
133                 if ( attributeType.getSuperior() != null )
134                 {
135                     registries.addReference( attributeType, attributeType.getSuperior() );
136                 }
137             }
138             finally
139             {
140                 attributeType.lock();
141             }
142         }
143     }
144 
145 
146     /**
147      * Build the Superior AttributeType reference for an AttributeType
148      */
149     private static boolean buildSuperior( MutableAttributeType attributeType, List<Throwable> errors, Registries registries )
150     {
151         MutableAttributeType currentSuperior;
152         AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
153         
154         String superiorOid = attributeType.getSuperiorOid();
155 
156         if ( superiorOid != null )
157         {
158             // This AT has a superior
159             try
160             {
161                 currentSuperior = ( MutableAttributeType ) attributeTypeRegistry.lookup( superiorOid );
162             }
163             catch ( Exception e )
164             {
165                 // Not allowed.
166                 String msg = I18n.err( I18n.ERR_04303, superiorOid, attributeType.getName() );
167 
168                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
169                     LdapSchemaExceptionCodes.AT_NONEXISTENT_SUPERIOR, msg, e );
170                 ldapSchemaException.setSourceObject( attributeType );
171                 ldapSchemaException.setRelatedId( superiorOid );
172                 errors.add( ldapSchemaException );
173                 LOG.info( msg );
174 
175                 // Get out now
176                 return false;
177             }
178 
179             if ( currentSuperior != null )
180             {
181                 // a special case : if the superior is collective, this is an error
182                 if ( currentSuperior.isCollective() )
183                 {
184                     String msg = I18n.err( I18n.ERR_04482_CANNOT_SUBTYPE_COLLECTIVE,
185                         currentSuperior, attributeType.getName() );
186 
187                     LdapSchemaException ldapSchemaException = new LdapSchemaException(
188                         LdapSchemaExceptionCodes.AT_CANNOT_SUBTYPE_COLLECTIVE_AT, msg );
189                     ldapSchemaException.setSourceObject( attributeType );
190                     errors.add( ldapSchemaException );
191                     LOG.info( msg );
192                     
193                     return false;
194                 }
195 
196                 attributeType.setSuperior( currentSuperior );
197 
198                 // Recursively update the superior if not already done. We don't recurse
199                 // if the superior's superior is not null, as it means it has already been
200                 // handled.
201                 if ( currentSuperior.getSuperior() == null )
202                 {
203                     registries.buildReference( errors, currentSuperior );
204                 }
205 
206                 // Update the descendant MAP
207                 try
208                 {
209                     attributeTypeRegistry.registerDescendants( attributeType, currentSuperior );
210                 }
211                 catch ( LdapException ne )
212                 {
213                     errors.add( ne );
214                     LOG.info( ne.getMessage() );
215                     
216                     return false;
217                 }
218 
219                 // Check for cycles now
220                 Set<String> superiors = new HashSet<>();
221                 superiors.add( attributeType.getOid() );
222                 AttributeType tmp = currentSuperior;
223                 boolean isOk = true;
224 
225                 while ( tmp != null )
226                 {
227                     if ( superiors.contains( tmp.getOid() ) )
228                     {
229                         // There is a cycle : bad bad bad !
230                         // Not allowed.
231                         String msg = I18n.err( I18n.ERR_04304, attributeType.getName() );
232 
233                         LdapSchemaException ldapSchemaException = new LdapSchemaException(
234                             LdapSchemaExceptionCodes.AT_CYCLE_TYPE_HIERARCHY, msg );
235                         ldapSchemaException.setSourceObject( attributeType );
236                         errors.add( ldapSchemaException );
237                         LOG.info( msg );
238                         isOk = false;
239 
240                         break;
241                     }
242                     else
243                     {
244                         superiors.add( tmp.getOid() );
245                         tmp = tmp.getSuperior();
246                     }
247                 }
248 
249                 superiors.clear();
250 
251                 return isOk;
252             }
253             else
254             {
255                 // Not allowed.
256                 String msg = I18n.err( I18n.ERR_04305, superiorOid, attributeType.getName() );
257 
258                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
259                     LdapSchemaExceptionCodes.AT_NONEXISTENT_SUPERIOR, msg );
260                 ldapSchemaException.setSourceObject( attributeType );
261                 ldapSchemaException.setRelatedId( superiorOid );
262                 errors.add( ldapSchemaException );
263                 LOG.info( msg );
264 
265                 // Get out now
266                 return false;
267             }
268         }
269         else
270         {
271             // No superior, just return
272             return true;
273         }
274     }
275 
276 
277     /**
278      * Build the SYNTAX reference for an AttributeType
279      */
280     private static void buildSyntax( MutableAttributeType attributeType, List<Throwable> errors, Registries registries )
281     {
282         String syntaxOid = attributeType.getSyntaxOid();
283         
284         if ( syntaxOid != null )
285         {
286             LdapSyntax currentSyntax = null;
287 
288             try
289             {
290                 currentSyntax = registries.getLdapSyntaxRegistry().lookup( syntaxOid );
291             }
292             catch ( LdapException ne )
293             {
294                 // Not allowed.
295                 String msg = I18n.err( I18n.ERR_04306, syntaxOid, attributeType.getName() );
296 
297                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
298                     LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg, ne );
299                 ldapSchemaException.setSourceObject( attributeType );
300                 ldapSchemaException.setRelatedId( syntaxOid );
301                 errors.add( ldapSchemaException );
302                 LOG.info( msg );
303                 
304                 return;
305             }
306 
307             if ( currentSyntax != null )
308             {
309                 // Update the Syntax reference
310                 attributeType.setSyntax( currentSyntax );
311             }
312             else
313             {
314                 // Not allowed.
315                 String msg = I18n.err( I18n.ERR_04306, syntaxOid, attributeType.getName() );
316 
317                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
318                     LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg );
319                 ldapSchemaException.setSourceObject( attributeType );
320                 ldapSchemaException.setRelatedId( syntaxOid );
321                 errors.add( ldapSchemaException );
322                 LOG.info( msg );
323                 
324                 return;
325             }
326         }
327         else
328         {
329             // We inherit from the superior's syntax, if any
330             if ( attributeType.getSuperior() != null )
331             {
332                 if ( attributeType.getSuperior().getSyntax() != null )
333                 {
334                     attributeType.setSyntax( attributeType.getSuperior().getSyntax() );
335                 }
336                 else
337                 {
338                     String msg = I18n.err( I18n.ERR_04306, syntaxOid, attributeType.getName() );
339 
340                     LdapSchemaException ldapSchemaException = new LdapSchemaException(
341                         LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg );
342                     ldapSchemaException.setSourceObject( attributeType );
343                     ldapSchemaException.setRelatedId( syntaxOid );
344                     errors.add( ldapSchemaException );
345                     LOG.info( msg );
346                     
347                     return;
348                 }
349             }
350             else
351             {
352                 // Not allowed.
353                 String msg = I18n.err( I18n.ERR_04307, attributeType.getName() );
354 
355                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
356                     LdapSchemaExceptionCodes.AT_SYNTAX_OR_SUPERIOR_REQUIRED, msg );
357                 ldapSchemaException.setSourceObject( attributeType );
358                 errors.add( ldapSchemaException );
359                 LOG.info( msg );
360                 
361                 return;
362             }
363         }
364     }
365     
366     
367     /**
368      * Build the EQUALITY MR reference for an AttributeType
369      */
370     private static void buildEquality( MutableAttributeType attributeType, List<Throwable> errors, Registries registries )
371     {
372         String equalityOid = attributeType.getEqualityOid();
373         
374         // The equality MR. It can be null
375         if ( equalityOid != null )
376         {
377             MatchingRule currentEquality = null;
378 
379             try
380             {
381                 currentEquality = registries.getMatchingRuleRegistry().lookup( equalityOid );
382             }
383             catch ( LdapException ne )
384             {
385                 // Not allowed.
386                 String msg = I18n.err( I18n.ERR_04308, equalityOid, attributeType.getName() );
387 
388                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
389                     LdapSchemaExceptionCodes.AT_NONEXISTENT_EQUALITY_MATCHING_RULE, msg, ne );
390                 ldapSchemaException.setSourceObject( attributeType );
391                 ldapSchemaException.setRelatedId( equalityOid );
392                 errors.add( ldapSchemaException );
393                 LOG.info( msg );
394                 
395                 return;
396             }
397 
398             if ( currentEquality != null )
399             {
400                 attributeType.setEquality( currentEquality );
401                 
402                 // Restore the old equality OID to preserve the user's provided value
403                 attributeType.setEqualityOid( equalityOid );
404             }
405             else
406             {
407                 // Not allowed.
408                 String msg = I18n.err( I18n.ERR_04309, equalityOid, attributeType.getName() );
409 
410                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
411                     LdapSchemaExceptionCodes.AT_NONEXISTENT_EQUALITY_MATCHING_RULE, msg );
412                 ldapSchemaException.setSourceObject( attributeType );
413                 ldapSchemaException.setRelatedId( equalityOid );
414                 errors.add( ldapSchemaException );
415                 LOG.info( msg );
416             }
417         }
418         else
419         {
420             AttributeType superior = attributeType.getSuperior();
421             
422             // If the AT has a superior, take its Equality MR if any
423             if ( ( superior != null ) && ( superior.getEquality() != null ) )
424             {
425                 attributeType.setEquality( superior.getEquality() );
426             }
427         }
428     }
429 
430 
431     /**
432      * Build the SUBSTR MR reference for an AttributeType
433      */
434     private static void buildSubstring( MutableAttributeType attributeType, List<Throwable> errors, Registries registries )
435     {
436         String substringOid = attributeType.getSubstringOid();
437         
438         // The Substring MR. It can be null
439         if ( substringOid != null )
440         {
441             MatchingRule currentSubstring = null;
442 
443             try
444             {
445                 currentSubstring = registries.getMatchingRuleRegistry().lookup( substringOid );
446             }
447             catch ( LdapException ne )
448             {
449                 // Not allowed.
450                 String msg = I18n.err( I18n.ERR_04312, substringOid, attributeType.getName() );
451 
452                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
453                     LdapSchemaExceptionCodes.AT_NONEXISTENT_SUBSTRING_MATCHING_RULE, msg, ne );
454                 ldapSchemaException.setSourceObject( attributeType );
455                 ldapSchemaException.setRelatedId( substringOid );
456                 errors.add( ldapSchemaException );
457                 LOG.info( msg );
458                 
459                 return;
460             }
461 
462             if ( currentSubstring != null )
463             {
464                 attributeType.setSubstring( currentSubstring );
465             }
466             else
467             {
468                 // Not allowed.
469                 String msg = I18n.err( I18n.ERR_04313, substringOid, attributeType.getName() );
470 
471                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
472                     LdapSchemaExceptionCodes.AT_NONEXISTENT_SUBSTRING_MATCHING_RULE, msg );
473                 ldapSchemaException.setSourceObject( attributeType );
474                 ldapSchemaException.setRelatedId( substringOid );
475                 errors.add( ldapSchemaException );
476                 LOG.info( msg );
477                 
478                 return;
479             }
480         }
481         else
482         {
483             AttributeType superior = attributeType.getSuperior();
484             
485             // If the AT has a superior, take its Substring MR if any
486             if ( ( superior != null ) && ( superior.getSubstring() != null ) )
487             {
488                 attributeType.setSubstring( superior.getSubstring() );
489             }
490         }
491     }
492     
493     
494 
495 
496 
497 
498     /**
499      * Build the ORDERING MR reference for an AttributeType
500      */
501     private static void buildOrdering( MutableAttributeType attributeType, List<Throwable> errors, Registries registries )
502     {
503         String orderingOid = attributeType.getOrderingOid();
504         
505         if ( orderingOid != null )
506         {
507             MatchingRule currentOrdering = null;
508 
509             try
510             {
511                 currentOrdering = registries.getMatchingRuleRegistry().lookup( orderingOid );
512             }
513             catch ( LdapException ne )
514             {
515                 // Not allowed.
516                 String msg = I18n.err( I18n.ERR_04310, orderingOid, attributeType.getName() );
517 
518                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
519                     LdapSchemaExceptionCodes.AT_NONEXISTENT_ORDERING_MATCHING_RULE, msg, ne );
520                 ldapSchemaException.setSourceObject( attributeType );
521                 ldapSchemaException.setRelatedId( orderingOid );
522                 errors.add( ldapSchemaException );
523                 LOG.info( msg );
524                 
525                 return;
526             }
527 
528             if ( currentOrdering != null )
529             {
530                 attributeType.setOrdering( currentOrdering );
531             }
532             else
533             {
534                 // Not allowed.
535                 String msg = I18n.err( I18n.ERR_04311, orderingOid, attributeType.getName() );
536 
537                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
538                     LdapSchemaExceptionCodes.AT_NONEXISTENT_ORDERING_MATCHING_RULE, msg );
539                 ldapSchemaException.setSourceObject( attributeType );
540                 ldapSchemaException.setRelatedId( orderingOid );
541                 errors.add( ldapSchemaException );
542                 LOG.info( msg );
543             }
544         }
545         else
546         {
547             AttributeType superior = attributeType.getSuperior();
548             
549             // If the AT has a superior, take its Ordering MR if any
550             if ( ( superior != null ) && ( superior.getOrdering() != null ) )
551             {
552                 attributeType.setOrdering( superior.getOrdering() );
553             }
554         }
555     }
556 
557     
558     /**
559      * Check the constraints for the Usage field.
560      */
561     private static void checkUsage( AttributeType attributeType, List<Throwable> errors )
562     {
563         AttributeType superior = attributeType.getSuperior();
564         
565         // Check that the AT usage is the same that its superior
566         if ( ( superior != null ) && ( attributeType.getUsage() != superior.getUsage() ) )
567         {
568             // This is an error
569             String msg = I18n.err( I18n.ERR_04314, attributeType.getName() );
570 
571             LdapSchemaException ldapSchemaException = new LdapSchemaException(
572                 LdapSchemaExceptionCodes.AT_MUST_HAVE_SAME_USAGE_THAN_SUPERIOR, msg );
573             ldapSchemaException.setSourceObject( attributeType );
574             errors.add( ldapSchemaException );
575             LOG.info( msg );
576             
577             return;
578         }
579 
580         // Now, check that the AttributeType's USAGE does not conflict
581         if ( !attributeType.isUserModifiable() && ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS ) )
582         {
583             // Cannot have a not user modifiable AT which is not an operational AT
584             String msg = I18n.err( I18n.ERR_04315, attributeType.getName() );
585 
586             LdapSchemaException ldapSchemaException = new LdapSchemaException(
587                 LdapSchemaExceptionCodes.AT_USER_APPLICATIONS_USAGE_MUST_BE_USER_MODIFIABLE, msg );
588             ldapSchemaException.setSourceObject( attributeType );
589             errors.add( ldapSchemaException );
590             LOG.info( msg );
591         }
592     }
593 
594 
595     /**
596      * Check the constraints for the Collective field.
597      */
598     private static void checkCollective( MutableAttributeType attributeType, List<Throwable> errors )
599     {
600         AttributeType superior = attributeType.getSuperior();
601 
602         if ( ( superior != null ) && superior.isCollective() )
603         {
604             // An AttributeType will be collective if its superior is collective
605             attributeType.setCollective( true );
606         }
607 
608         if ( attributeType.isCollective() && ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS ) )
609         {
610             // An AttributeType which is collective must be a USER attributeType
611             String msg = I18n.err( I18n.ERR_04316, attributeType.getName() );
612 
613             LdapSchemaException ldapSchemaException = new LdapSchemaException(
614                 LdapSchemaExceptionCodes.AT_COLLECTIVE_MUST_HAVE_USER_APPLICATIONS_USAGE, msg );
615             ldapSchemaException.setSourceObject( attributeType );
616             errors.add( ldapSchemaException );
617             LOG.info( msg );
618         }
619 
620         if ( attributeType.isCollective() && attributeType.isSingleValued() )
621         {
622             // A collective attribute must be multi-valued
623             String msg = I18n.err( I18n.ERR_04483_COLLECTIVE_NOT_MULTI_VALUED, attributeType.getName() );
624 
625             LdapSchemaException ldapSchemaException = new LdapSchemaException(
626                 LdapSchemaExceptionCodes.AT_COLLECTIVE_CANNOT_BE_SINGLE_VALUED, msg );
627             ldapSchemaException.setSourceObject( attributeType );
628             errors.add( ldapSchemaException );
629             LOG.info( msg );
630         }
631     }
632     
633     
634     /**
635      * Remove the AttributeType from the registries, updating the references to
636      * other SchemaObject.
637      *
638      * If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX),
639      * an exception is thrown.
640      * 
641      * @param attributeType The AttributeType to remove from the Registries
642      * @param errors The errors we got while removing the AttributeType from the Registries
643      * @param registries The Registries
644      * @throws LdapException If the AttributeType is not valid
645      */
646     public static void removeFromRegistries( AttributeType attributeType, List<Throwable> errors, Registries registries ) throws LdapException
647     {
648         if ( registries != null )
649         {
650             AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
651 
652             // Remove the attributeType from the oid/normalizer map
653             attributeTypeRegistry.removeMappingFor( attributeType );
654 
655             // Unregister this AttributeType into the Descendant map
656             attributeTypeRegistry.unregisterDescendants( attributeType, attributeType.getSuperior() );
657 
658             /**
659              * Remove the AT references (using and usedBy) :
660              * AT -> MR (for EQUALITY, ORDERING and SUBSTR)
661              * AT -> S
662              * AT -> AT
663              */
664             if ( attributeType.getEquality() != null )
665             {
666                 registries.delReference( attributeType, attributeType.getEquality() );
667             }
668 
669             if ( attributeType.getOrdering() != null )
670             {
671                 registries.delReference( attributeType, attributeType.getOrdering() );
672             }
673 
674             if ( attributeType.getSubstring() != null )
675             {
676                 registries.delReference( attributeType, attributeType.getSubstring() );
677             }
678 
679             if ( attributeType.getSyntax() != null )
680             {
681                 registries.delReference( attributeType, attributeType.getSyntax() );
682             }
683 
684             if ( attributeType.getSuperior() != null )
685             {
686                 registries.delReference( attributeType, attributeType.getSuperior() );
687             }
688         }
689     }
690 }