View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.schema;
18  
19  
20  import org.apache.ldap.common.util.NamespaceTools;
21  import org.apache.ldap.common.exception.LdapSchemaViolationException;
22  import org.apache.ldap.common.message.ResultCodeEnum;
23  import org.apache.ldap.common.schema.ObjectClass;
24  import org.apache.ldap.common.schema.ObjectClassTypeEnum;
25  
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import javax.naming.NamingException;
30  import javax.naming.Name;
31  import javax.naming.NamingEnumeration;
32  import javax.naming.directory.Attributes;
33  import javax.naming.directory.DirContext;
34  import javax.naming.directory.Attribute;
35  import java.util.Set;
36  import java.util.HashSet;
37  
38  
39  /***
40   * Performs schema checks on behalf of the SchemaService.
41   *
42   * @todo we really need to refactor this code since there's much duplication
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   * @version $Rev$, $Date$
45   */
46  public class SchemaChecker
47  {
48      /*** the SLF4J logger for this class */
49      private static Logger log = LoggerFactory.getLogger( SchemaChecker.class );
50  
51      /***
52       * Makes sure modify operations do not leave the entry without a STRUCTURAL
53       * objectClass.  At least one STRUCTURAL objectClass must be specified for
54       * the entry after modifications take effect.
55       *
56       * @param registry the objectClass registry to lookup ObjectClass specifications
57       * @param name the name of the entry being modified
58       * @param mod the type of modification operation being performed (should be
59       * REMOVE_ATTRIBUTE)
60       * @param attribute the attribute being modified
61       * @throws NamingException if modify operations leave the entry inconsistent
62       * without a STRUCTURAL objectClass
63       */
64      public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, Name name, int mod,
65                                                                       Attribute attribute )
66              throws NamingException
67      {
68          if ( mod != DirContext.REPLACE_ATTRIBUTE )
69          {
70              return;
71          }
72  
73          if ( ! "objectclass".equalsIgnoreCase( attribute.getID() ) )
74          {
75              return;
76          }
77  
78          // whoever issued the modify operation is insane they want to delete
79          // all the objectClass values in which case we must throw an exception
80          if ( attribute.size() == 0 )
81          {
82              String msg = "Modify operation leaves no structural objectClass for entry " + name;
83              if ( log.isInfoEnabled() )
84              {
85                  log.info( msg + ".  Raising LdapSchemaViolationException." );
86              }
87              throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
88          }
89  
90          // check that there is at least one structural objectClass in the replacement set
91          for ( int ii = 0; ii < attribute.size(); ii++ )
92          {
93              ObjectClass ocType = registry.lookup( ( String ) attribute.get( ii ) );
94              if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
95              {
96                  return;
97              }
98          }
99  
100         // no structural object classes exist for the entry in the replacement
101         // set for the objectClass attribute so we need to complain about that
102         String msg = "Modify operation leaves no structural objectClass for entry " + name;
103         if ( log.isInfoEnabled() )
104         {
105             log.info( msg + ".  Raising LdapSchemaViolationException." );
106         }
107         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
108     }
109 
110 
111     /***
112      * Makes sure modify operations do not leave the entry without a STRUCTURAL
113      * objectClass.  At least one STRUCTURAL objectClass must be specified for
114      * the entry after modifications take effect.
115      *
116      * @param registry the objectClass registry to lookup ObjectClass specifications
117      * @param name the name of the entry being modified
118      * @param mod the type of modification operation being performed (should be
119      * REMOVE_ATTRIBUTE)
120      * @param attributes the attributes being modified
121      * @throws NamingException if modify operations leave the entry inconsistent
122      * without a STRUCTURAL objectClass
123      */
124     public static void preventStructuralClassRemovalOnModifyReplace( ObjectClassRegistry registry, Name name, int mod,
125                                                                      Attributes attributes )
126             throws NamingException
127     {
128         if ( mod != DirContext.REPLACE_ATTRIBUTE )
129         {
130             return;
131         }
132 
133         Attribute objectClass = attributes.get( "objectClass" );
134         if ( objectClass == null )
135         {
136             return;
137         }
138 
139         // whoever issued the modify operation is insane they want to delete
140         // all the objectClass values in which case we must throw an exception
141         if ( objectClass.size() == 0 )
142         {
143             String msg = "Modify operation leaves no structural objectClass for entry " + name;
144             if ( log.isInfoEnabled() )
145             {
146                 log.info( msg + ".  Raising LdapSchemaViolationException." );
147             }
148             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
149         }
150 
151         // check that there is at least one structural objectClass in the replacement set
152         for ( int ii = 0; ii < objectClass.size(); ii++ )
153         {
154             ObjectClass ocType = registry.lookup( ( String ) objectClass.get( ii ) );
155             if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
156             {
157                 return;
158             }
159         }
160 
161         // no structural object classes exist for the entry in the replacement
162         // set for the objectClass attribute so we need to complain about that
163         String msg = "Modify operation leaves no structural objectClass for entry " + name;
164         if ( log.isInfoEnabled() )
165         {
166             log.info( msg + ".  Raising LdapSchemaViolationException." );
167         }
168         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
169     }
170 
171 
172     /***
173      * Makes sure modify operations do not leave the entry without a STRUCTURAL
174      * objectClass.  At least one STRUCTURAL objectClass must be specified for
175      * the entry after modifications take effect.
176      *
177      * @param registry the objectClass registry to lookup ObjectClass specifications
178      * @param name the name of the entry being modified
179      * @param mod the type of modification operation being performed (should be
180      * REMOVE_ATTRIBUTE)
181      * @param attribute the attribute being modified
182      * @param entryObjectClasses the entry being modified
183      * @throws NamingException if modify operations leave the entry inconsistent
184      * without a STRUCTURAL objectClass
185      */
186     public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
187                                                                     Attribute attribute, Attribute entryObjectClasses )
188             throws NamingException
189     {
190         if ( mod != DirContext.REMOVE_ATTRIBUTE )
191         {
192             return;
193         }
194 
195         if ( ! "objectclass".equalsIgnoreCase( attribute.getID() ) )
196         {
197             return;
198         }
199 
200         // whoever issued the modify operation is insane they want to delete
201         // all the objectClass values in which case we must throw an exception
202         if ( attribute.size() == 0 )
203         {
204             String msg = "Modify operation leaves no structural objectClass for entry " + name;
205             if ( log.isInfoEnabled() )
206             {
207                 log.info( msg + ".  Raising LdapSchemaViolationException." );
208             }
209             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
210         }
211 
212         // remove all the objectClass attribute values from a cloned copy and then
213         // we can analyze what remains in this attribute to make sure a structural
214         // objectClass is present for the entry
215 
216         Attribute cloned = ( Attribute ) entryObjectClasses.clone();
217         for ( int ii = 0; ii < attribute.size(); ii++ )
218         {
219             cloned.remove( attribute.get( ii ) );
220         }
221 
222         // check resultant set of objectClass values for a structural objectClass
223         for ( int ii = 0; ii < cloned.size(); ii++ )
224         {
225             ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
226             if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
227             {
228                 return;
229             }
230         }
231 
232         // no structural object classes exist for the entry after the modifications
233         // to the objectClass attribute so we need to complain about that
234         String msg = "Modify operation leaves no structural objectClass for entry " + name;
235         if ( log.isInfoEnabled() )
236         {
237             log.info( msg + ".  Raising LdapSchemaViolationException." );
238         }
239         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
240     }
241 
242 
243     /***
244      * Makes sure modify operations do not leave the entry without a STRUCTURAL
245      * objectClass.  At least one STRUCTURAL objectClass must be specified for
246      * the entry after modifications take effect.
247      *
248      * @param registry the objectClass registry to lookup ObjectClass specifications
249      * @param name the name of the entry being modified
250      * @param mod the type of modification operation being performed (should be
251      * REMOVE_ATTRIBUTE)
252      * @param attributes the attributes being modified
253      * @param entryObjectClasses the entry being modified
254      * @throws NamingException if modify operations leave the entry inconsistent
255      * without a STRUCTURAL objectClass
256      */
257     public static void preventStructuralClassRemovalOnModifyRemove( ObjectClassRegistry registry, Name name, int mod,
258                                                                     Attributes attributes, Attribute entryObjectClasses )
259             throws NamingException
260     {
261         if ( mod != DirContext.REMOVE_ATTRIBUTE )
262         {
263             return;
264         }
265 
266         Attribute objectClass = attributes.get( "objectClass" );
267         if ( objectClass == null )
268         {
269             return;
270         }
271 
272         // whoever issued the modify operation is insane they want to delete
273         // all the objectClass values in which case we must throw an exception
274         if ( objectClass.size() == 0 )
275         {
276             String msg = "Modify operation leaves no structural objectClass for entry " + name;
277             if ( log.isInfoEnabled() )
278             {
279                 log.info( msg + ".  Raising LdapSchemaViolationException." );
280             }
281             throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
282         }
283 
284         // remove all the objectClass attribute values from a cloned copy and then
285         // we can analyze what remains in this attribute to make sure a structural
286         // objectClass is present for the entry
287 
288         Attribute cloned = ( Attribute ) entryObjectClasses.clone();
289         for ( int ii = 0; ii < objectClass.size(); ii++ )
290         {
291             cloned.remove( objectClass.get( ii ) );
292         }
293 
294         // check resultant set of objectClass values for a structural objectClass
295         for ( int ii = 0; ii < cloned.size(); ii++ )
296         {
297             ObjectClass ocType = registry.lookup( ( String ) cloned.get( ii ) );
298             if ( ocType.getType() == ObjectClassTypeEnum.STRUCTURAL )
299             {
300                 return;
301             }
302         }
303 
304         // no structural object classes exist for the entry after the modifications
305         // to the objectClass attribute so we need to complain about that
306         String msg = "Modify operation leaves no structural objectClass for entry " + name;
307         if ( log.isInfoEnabled() )
308         {
309             log.info( msg + ".  Raising LdapSchemaViolationException." );
310         }
311         throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSMODSPROHIBITED );
312     }
313 
314 
315     /***
316      * Makes sure a modify operation does not replace RDN attributes or their value.
317      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
318      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
319      * seen below:
320      * <p/>
321      * <pre>
322      *     The Modify Operation cannot be used to remove from an entry any of
323      *     its distinguished values, those values which form the entry's
324      *     relative distinguished name.  An attempt to do so will result in the
325      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
326      *     described in section 4.9 is used to rename an entry.
327      * </pre>
328      *
329      * @param name the distinguished name of the attribute being modified
330      * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
331      * @param attribute the attribute being modified
332      * @throws NamingException if the modify operation is removing an Rdn attribute
333      */
334     public static void preventRdnChangeOnModifyReplace( Name name, int mod, Attribute attribute )
335             throws NamingException
336     {
337         if ( mod != DirContext.REPLACE_ATTRIBUTE )
338         {
339             return;
340         }
341 
342         Set rdnAttributes = getRdnAttributes( name );
343         String id = ( String ) attribute.getID();
344 
345         if ( ! rdnAttributes.contains( id ) )
346         {
347             return;
348         }
349 
350         // if the attribute values to delete are not specified then all values
351         // for the attribute are to be deleted in which case we must just throw
352         // a schema violation exception with the notAllowedOnRdn result code
353         if ( attribute.size() == 0 )
354         {
355             String msg = "Modify operation attempts to delete RDN attribute ";
356             msg += id + " on entry " + name + " violates schema constraints";
357 
358             if ( log.isInfoEnabled() )
359             {
360                 log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
361             }
362             throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
363         }
364 
365         // from here on the modify operation replaces specific values
366         // of the Rdn attribute so we must check to make sure all the old
367         // rdn attribute values are present in the replacement set
368         String rdnValue = getRdnValue( id, name );
369         for ( int ii = 0; ii < attribute.size(); ii++ )
370         {
371             // if the old rdn value is not in the rdn attribute then
372             // we must complain with a schema violation
373             if ( ! attribute.contains( rdnValue ) )
374             {
375                 String msg = "Modify operation attempts to delete RDN attribute values in use for ";
376                 msg += id + " on entry " + name + " and violates schema constraints";
377 
378                 if ( log.isInfoEnabled() )
379                 {
380                     log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
381                 }
382                 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
383             }
384         }
385     }
386 
387 
388     /***
389      * Makes sure a modify operation does not replace RDN attributes or their value.
390      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
391      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
392      * seen below:
393      * <p/>
394      * <pre>
395      *     The Modify Operation cannot be used to remove from an entry any of
396      *     its distinguished values, those values which form the entry's
397      *     relative distinguished name.  An attempt to do so will result in the
398      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
399      *     described in section 4.9 is used to rename an entry.
400      * </pre>
401      *
402      * @param name the distinguished name of the attribute being modified
403      * @param mod the modification operation being performed (should be REPLACE_ATTRIBUTE )
404      * @param attributes the attributes being modified
405      * @throws NamingException if the modify operation is removing an Rdn attribute
406      */
407     public static void preventRdnChangeOnModifyReplace( Name name, int mod, Attributes attributes )
408             throws NamingException
409     {
410         if ( mod != DirContext.REPLACE_ATTRIBUTE )
411         {
412             return;
413         }
414 
415         Set rdnAttributes = getRdnAttributes( name );
416         NamingEnumeration list = attributes.getIDs();
417         while ( list.hasMore() )
418         {
419             String id = ( String ) list.next();
420 
421             if ( rdnAttributes.contains( id ) )
422             {
423                 // if the attribute values to delete are not specified then all values
424                 // for the attribute are to be deleted in which case we must just throw
425                 // a schema violation exception with the notAllowedOnRdn result code
426                 if ( attributes.get( id ).size() == 0 )
427                 {
428                     String msg = "Modify operation attempts to delete RDN attribute ";
429                     msg += id + " on entry " + name + " violates schema constraints";
430 
431                     if ( log.isInfoEnabled() )
432                     {
433                         log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
434                     }
435                     throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
436                 }
437 
438                 // from here on the modify operation replaces specific values
439                 // of the Rdn attribute so we must check to make sure all the old
440                 // rdn attribute values are present in the replacement set
441                 String rdnValue = getRdnValue( id, name );
442                 Attribute rdnAttr = attributes.get( id );
443                 for ( int ii = 0; ii < rdnAttr.size(); ii++ )
444                 {
445                     // if the old rdn value is not in the rdn attribute then
446                     // we must complain with a schema violation
447                     if ( ! rdnAttr.contains( rdnValue ) )
448                     {
449                         String msg = "Modify operation attempts to delete RDN attribute values in use for ";
450                         msg += id + " on entry " + name + " and violates schema constraints";
451 
452                         if ( log.isInfoEnabled() )
453                         {
454                             log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
455                         }
456                         throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
457                     }
458                 }
459             }
460         }
461     }
462 
463 
464     /***
465      * Makes sure a modify operation does not delete RDN attributes or their value.
466      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
467      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
468      * seen below:
469      * <p/>
470      * <pre>
471      *     The Modify Operation cannot be used to remove from an entry any of
472      *     its distinguished values, those values which form the entry's
473      *     relative distinguished name.  An attempt to do so will result in the
474      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
475      *     described in section 4.9 is used to rename an entry.
476      * </pre>
477      *
478      * @param name the distinguished name of the attribute being modified
479      * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
480      * @param attribute the attribute being modified
481      * @throws NamingException if the modify operation is removing an Rdn attribute
482      */
483     public static void preventRdnChangeOnModifyRemove( Name name, int mod, Attribute attribute )
484             throws NamingException
485     {
486         if ( mod != DirContext.REMOVE_ATTRIBUTE )
487         {
488             return;
489         }
490 
491         Set rdnAttributes = getRdnAttributes( name );
492         String id = attribute.getID();
493 
494         if ( ! rdnAttributes.contains( id ) )
495         {
496             return;
497         }
498 
499         // if the attribute values to delete are not specified then all values
500         // for the attribute are to be deleted in which case we must just throw
501         // a schema violation exception with the notAllowedOnRdn result code
502         if ( attribute.size() == 0 )
503         {
504             String msg = "Modify operation attempts to delete RDN attribute ";
505             msg += id + " on entry " + name + " violates schema constraints";
506 
507             if ( log.isInfoEnabled() )
508             {
509                 log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
510             }
511             throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
512         }
513 
514         // from here on the modify operation only deletes specific values
515         // of the Rdn attribute so we must check if one of those values
516         // are used by the Rdn attribute value pair for the name of the entry
517         String rdnValue = getRdnValue( id, name );
518         for ( int ii = 0; ii < attribute.size(); ii++ )
519         {
520             if ( rdnValue.equals( attribute.get( ii ) ) )
521             {
522                 String msg = "Modify operation attempts to delete RDN attribute values in use for ";
523                 msg += id + " on entry " + name + " and violates schema constraints";
524 
525                 if ( log.isInfoEnabled() )
526                 {
527                     log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
528                 }
529                 throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
530             }
531         }
532     }
533 
534 
535     /***
536      * Makes sure a modify operation does not delete RDN attributes or their value.
537      * According to section 4.6 of <a href="http://rfc.net/rfc2251.html#s4.6.">
538      * RFC 2251</a> a modify operation cannot be used to remove Rdn attributes as
539      * seen below:
540      * <p/>
541      * <pre>
542      *     The Modify Operation cannot be used to remove from an entry any of
543      *     its distinguished values, those values which form the entry's
544      *     relative distinguished name.  An attempt to do so will result in the
545      *     server returning the error notAllowedOnRDN.  The Modify DN Operation
546      *     described in section 4.9 is used to rename an entry.
547      * </pre>
548      *
549      * @param name the distinguished name of the attribute being modified
550      * @param mod the modification operation being performed (should be REMOVE_ATTRIBUTE )
551      * @param attributes the attributes being modified
552      * @throws NamingException if the modify operation is removing an Rdn attribute
553      */
554     public static void preventRdnChangeOnModifyRemove( Name name, int mod, Attributes attributes )
555             throws NamingException
556     {
557         if ( mod != DirContext.REMOVE_ATTRIBUTE )
558         {
559             return;
560         }
561 
562         Set rdnAttributes = getRdnAttributes( name );
563         NamingEnumeration list = attributes.getIDs();
564         while ( list.hasMore() )
565         {
566             String id = ( String ) list.next();
567 
568             if ( rdnAttributes.contains( id ) )
569             {
570                 // if the attribute values to delete are not specified then all values
571                 // for the attribute are to be deleted in which case we must just throw
572                 // a schema violation exception with the notAllowedOnRdn result code
573                 if ( attributes.get( id ).size() == 0 )
574                 {
575                     String msg = "Modify operation attempts to delete RDN attribute ";
576                     msg += id + " on entry " + name + " violates schema constraints";
577 
578                     if ( log.isInfoEnabled() )
579                     {
580                         log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
581                     }
582                     throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
583                 }
584 
585                 // from here on the modify operation only deletes specific values
586                 // of the Rdn attribute so we must check if one of those values
587                 // are used by the Rdn attribute value pair for the name of the entry
588                 String rdnValue = getRdnValue( id, name );
589                 Attribute rdnAttr = attributes.get( id );
590                 for ( int ii = 0; ii < rdnAttr.size(); ii++ )
591                 {
592                     if ( rdnValue.equals( rdnAttr.get( ii ) ) )
593                     {
594                         String msg = "Modify operation attempts to delete RDN attribute values in use for ";
595                         msg += id + " on entry " + name + " and violates schema constraints";
596 
597                         if ( log.isInfoEnabled() )
598                         {
599                             log.info( msg + ". SchemaChecker is throwing a schema violation exception." );
600                         }
601                         throw new LdapSchemaViolationException( msg, ResultCodeEnum.NOTALLOWEDONRDN );
602                     }
603                 }
604             }
605         }
606     }
607 
608 
609     /***
610      * Gets the Rdn attribute value. This method works even if the Rdn is
611      * composed of multiple attributes.
612      *
613      * @param id the attribute id of the Rdn attribute to return
614      * @param name the distinguished name of the entry
615      * @return the Rdn attribute value corresponding to the id, or null if the
616      * attribute is not an rdn attribute
617      * @throws NamingException if the name is malformed in any way
618      */
619     private static String getRdnValue( String id, Name name ) throws NamingException
620     {
621         String [] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
622 
623         for ( int ii = 0; ii < comps.length; ii++ )
624         {
625             String rdnAttrId = NamespaceTools.getRdnAttribute( comps[ii] );
626 
627             if ( rdnAttrId.equalsIgnoreCase( id ) )
628             {
629                 return NamespaceTools.getRdnValue( comps[ii] );
630             }
631         }
632 
633         return null;
634     }
635 
636 
637     /***
638      * Collects the set of Rdn attributes whether or not the Rdn is based on a
639      * single attribute or multiple attributes.
640      *
641      * @param name the distinguished name of an entry
642      * @return the set of attributes composing the Rdn for the name
643      * @throws NamingException if the syntax of the Rdn is incorrect
644      */
645     private static Set getRdnAttributes( Name name ) throws NamingException
646     {
647         String [] comps = NamespaceTools.getCompositeComponents( name.get( name.size() - 1 ) );
648         Set attributes = new HashSet();
649 
650         for ( int ii = 0; ii < comps.length; ii++ )
651         {
652             attributes.add( NamespaceTools.getRdnAttribute( comps[ii] ) );
653         }
654 
655         return attributes;
656     }
657 }