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