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