KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > naming > DXOps


1 package com.ca.commons.naming;
2
3
4 import com.ca.commons.cbutil.*;
5 import com.ca.commons.jndi.AdvancedOps;
6 import com.ca.commons.jndi.ConnectionData;
7
8 import javax.naming.*;
9 import javax.naming.directory.*;
10 import java.io.UnsupportedEncodingException JavaDoc;
11 import java.net.URLDecoder JavaDoc;
12 import java.util.logging.Level JavaDoc;
13 import java.util.logging.Logger JavaDoc;
14
15 /**
16  * A wrapper for BasicOps that converts the jndi primative names
17  * into com.ca.commons.naming objects...
18  */

19
20 public class DXOps extends AdvancedOps
21 {
22
23     private final static Logger JavaDoc log = Logger.getLogger(DXOps.class.getName());
24
25     /**
26      * Initialise with the directory context.
27      */

28
29
30     public DXOps(DirContext ctx)
31             throws NamingException
32     {
33         super(ctx);
34     }
35
36     public DXOps(ConnectionData cData)
37             throws NamingException
38     {
39         super(cData);
40     }
41
42
43     /**
44      * This preparses a name, preparitory to passing to the jndi operation.
45      * Usefull to over-ride if a Name needs to be escaped or re-formatted.
46      *
47      * @param name the pre jndi operation name.
48      * @return the version used by the operation.
49      */

50
51     public Name preParse(Name name)
52     {
53         //(assuming jndi doesn't mess with the names it's given, we don't need this...)
54
//DN newName = (name instanceof DN)?new DN((DN)name):new DN(name);
55
return name;
56     }
57
58     /**
59      * This postparses a name, after it has been returned from the jndi operation.
60      * Usefull to over-ride if the name needs to be unescaped or reformatted.
61      *
62      * @param name the post jndi operation name.
63      * @return the re-formatted version used by the application.
64      */

65
66     public Name postParse(Name name)
67     {
68         return postParse(name.toString());
69     }
70
71     /**
72      * This postparses a name, after it has been returned from the jndi operation.
73      * It assumes that it has got a jndi <i>CompositeName</i> that needs to be
74      * converted to a legal ldap dn (i.e. an ldap <i>CompoundName</i>). If this
75      * is *not* the case, there will be trouble...
76      *
77      * @param name the post jndi operation name.
78      * @return the re-formatted version used by the application, as a DN object.
79      */

80 //TODO: figure out if this is needed still.
81
//TODO: can we avoid the horrible, horrible, horrible JNDI Composite name disaster?
82
// (Ans: I don't think so; jndi gives us back strings that are composite names, even though
83
// we are never using federated name spaces. So we have to warp the whole program to cope with
84
// them)
85

86     public Name postParse(String JavaDoc name)
87     {
88         /* EMERGENCY HACK
89          * (JNDI apparently does not handle terminating spaces correctly - it
90          * retains the escape characters, but trims the actual space, resulting
91          * in an illegal ldap dn)
92          */

93         if (name.charAt(name.length() - 1) == '\\')
94         {
95             name = NameUtility.checkEndSpaces(name);
96         }
97
98
99         try
100         {
101             Name cn = new CompositeName(name);
102             if (cn.size() == 0) // if the name is empty ...
103
return new DN(); // ... just return an empty DN
104

105             return new DN(cn.get(cn.size() - 1)); // get the last element of the composite name, which will be the ldap compound name, and init the DN with that.
106
}
107         catch (NamingException e) // should never happen :-) (ROTFL)
108
{
109             log.log(Level.WARNING, "unexpected error: bad name back from jndi ftn in CBOps.postParse(" + name + ")?", e);
110             e.printStackTrace();
111             //System.exit(-1);
112
return new DN(name); // bad server response? return (possibly) corrupt name anyway...
113
}
114     }
115
116     /**
117      * This postparses a name, after it has been returned from the jndi operation.
118      * It assumes that it has got a jndi <i>CompositeName</i> that needs to be
119      * converted to a legal ldap dn (i.e. an ldap <i>CompoundName</i>). If this
120      * is *not* the case, there will be trouble...
121      *
122      * @param name the post jndi operation name.
123      * @return the re-formatted version used by the application as an ldap String.
124      */

125     public String JavaDoc postParseString(String JavaDoc name)
126     {
127         /* EMERGENCY HACK
128          * (JNDI apparently does not handle terminating spaces correctly - it
129          * retains the escape characters, but trims the actual space, resulting
130          * in an illegal ldap dn)
131          */

132
133         if (name.length() == 0)
134             return name;
135
136         if (name.charAt(name.length() - 1) == '\\')
137         {
138             name = NameUtility.checkEndSpaces(name);
139         }
140         if (name.startsWith("ldap://"))
141         {
142             try
143             {
144                 name = URLDecoder.decode(name, "UTF8");
145             }
146             catch (UnsupportedEncodingException JavaDoc e)
147             {
148                 log.severe("unexpected error: couldn't URL decode in CBOps.postParseString(" + name + ")?\n" + e.toString());
149                 e.printStackTrace();
150                 return name;
151             }
152         }
153
154         //if (name.indexOf('\\')>0 || name.indexOf('/')>0)
155
// System.out.println("PRE postParseName: " + name);
156

157
158         try
159         {
160             Name cn = new CompositeName(name); // If it isn't a composite name, this will fail horribly. But since it's JNDI, and therefore not object oriented, we have no way of telling...
161
if (cn.size() == 0) // if the name is empty ...
162
return ""; // ... just return an empty DN
163

164             name = cn.get(cn.size() - 1);
165             // if (name.indexOf('\\')>0 || name.indexOf('/')>0)
166
// System.out.println("POST postParseName: " + name);
167

168             return name; // get the last element of the composite name, which will be the ldap compound name, and init the DN with that.
169
}
170         catch (NamingException e) // should never happen :-) (ROTFL)
171
{
172             log.log(Level.WARNING, "unexpected error: bad name back from jndi ftn in CBOps.postParseString(" + name + ")?", e);
173             e.printStackTrace();
174             return name; // bad server response? return (possibly) corrupt name anyway...
175
}
176
177     }
178
179
180     /**
181      * This postparses a namingEnumeration of NameClassPairs, after it has been returned from the jndi operation.
182      * It returns a DXNamingEnumeration, and sets all the names to be the complete, full dn, rather than simply
183      * the dn relative to the base.
184      *
185      * @param names the post jndi operation namingEnumeration.
186      * @param base the 'base' dn from which the names in the enumeration (may) be relative.
187      * If the Names in
188      * the enumeration are suffixed by the searchBase, they are unaltered, otherwise the searchBase
189      * is added to the names to give the full DN in the namespace.
190      * @return the re-formatted version used by the application.
191      */

192     public NamingEnumeration postParseNameClassPairs(NamingEnumeration names, Name base)
193     {
194         log.finer("parsing with base :" + base.toString());
195         DXNamingEnumeration dxe = new DXNamingEnumeration();
196
197         String JavaDoc baseString = null;
198
199         if (base != null && base.isEmpty() == false)
200             baseString = base.toString();
201
202         try
203         {
204             while (names.hasMore())
205             {
206                 NameClassPair ncp = (NameClassPair) names.next();
207
208                 String JavaDoc rawName = postParseString(ncp.getName()).toString();
209
210                 // IMPORTANT!
211
// This appends the 'base' DN to the enumerated DNs in order to get absolute DNs...
212

213                 if (ncp.isRelative() && baseString != null)
214                 {
215                     if (rawName.length() != 0)
216                         rawName = rawName + "," + baseString;
217                     else
218                         rawName = baseString;
219                 }
220
221                 log.finer("ended up with: '" + rawName + "'");
222                 ncp.setName(rawName);
223                 dxe.add(ncp);
224             }
225         }
226         catch (NamingException ex)
227         {
228             CBUtility.error(CBIntText.get("Search partially failed! - only {0} entries returned", new Integer JavaDoc[]{new Integer JavaDoc(dxe.size())}), ex);
229         }
230
231         return dxe;
232     }
233
234
235     /*
236      * <p>Takes a NamingEnumeration and converts any names from being
237      * relative to the base to being full (i.e. including the base).</p>
238      * @param
239      * /
240
241     protected NamingEnumeration setFullDNs(NamingEnumeration ne, Name base)
242         throws NamingException
243     {
244 System.out.println("setting full dns for " + base);
245
246         DXNamingEnumeration dxne = new DXNamingEnumeration();
247
248         while (ne.hasMore())
249         {
250             NameClassPair ncp = (NameClassPair)ne.next();
251
252
253             // IMPORTANT!
254             // This appends the 'base' DN to the enumerated DNs in order to get absolute DNs...
255
256             if (ncp.isRelative() && base != null && base.size() > 0)
257             {
258                 String rawName = postParseString(ncp.getName());
259
260 System.out.println("modifying: " + rawName + " + " + base);
261
262                 if (rawName.length() != 0)
263                     rawName = rawName + "," + base.toString();
264                 else
265                     rawName = base.toString();
266
267 System.out.println("ended up with: '" + rawName + "'");
268
269                 ncp.setName(rawName); // I *think* this only needs to be done if we're changing stuff...
270             }
271
272             dxne.add(ncp);
273         }
274
275         return dxne;
276     }
277 */

278
279     /**
280      * overload the corresponding method in JNDIOps so that we can play silly buggers with parsing
281      * the stupid composite names that jndi inflicts on us.
282      *
283      * @param searchbase
284      * @param filter
285      * @param limit
286      * @param timeout
287      * @param returnAttributes
288      * @return
289      * @throws NamingException
290      */

291     protected NamingEnumeration rawSearchBaseEntry(Name searchbase, String JavaDoc filter, int limit,
292                                                    int timeout, String JavaDoc[] returnAttributes)
293             throws NamingException
294     {
295         return postParseNameClassPairs(super.rawSearchBaseEntry(searchbase, filter, limit, timeout, returnAttributes), searchbase);
296     }
297
298     /**
299      * overload the corresponding method in JNDIOps so that we can play silly buggers with parsing
300      * the stupid composite names that jndi inflicts on us.
301      *
302      * @param searchbase
303      * @param filter
304      * @param limit
305      * @param timeout
306      * @param returnAttributes
307      * @return
308      * @throws NamingException
309      */

310     protected NamingEnumeration rawSearchOneLevel(Name searchbase, String JavaDoc filter, int limit,
311                                                   int timeout, String JavaDoc[] returnAttributes) throws NamingException
312     {
313         return postParseNameClassPairs(super.rawSearchOneLevel(searchbase, filter, limit, timeout, returnAttributes), searchbase);
314     }
315
316     /**
317      * overload the corresponding method in JNDIOps so that we can play silly buggers with parsing
318      * the stupid composite names that jndi inflicts on us.
319      *
320      * @param searchbase
321      * @param filter
322      * @param limit
323      * @param timeout
324      * @param returnAttributes
325      * @return
326      * @throws NamingException
327      */

328     protected NamingEnumeration rawSearchSubTree(Name searchbase, String JavaDoc filter, int limit,
329                                                  int timeout, String JavaDoc[] returnAttributes) throws NamingException
330     {
331         return postParseNameClassPairs(super.rawSearchSubTree(searchbase, filter, limit, timeout, returnAttributes), searchbase);
332     }
333
334     /**
335      * Update an entry with the designated DN.
336      *
337      * @param oldEntry the old entry containing the old set of attributes.
338      * @param newEntry the new entry containing the replacement set of attributes.
339      */

340
341     public void modifyEntry(DXEntry oldEntry, DXEntry newEntry)
342             throws NamingException
343     {
344         if (oldEntry != null) oldEntry.removeEmptyAttributes();
345         if (newEntry != null) newEntry.removeEmptyAttributes();
346
347         if (oldEntry == null && newEntry == null) // nothing to do.
348
{
349         }
350         else if (oldEntry == null || (newEntry != null) && (newEntry.getStatus() == DXEntry.NEW)) // add
351
{
352             addEntryToDirectory(newEntry);
353         }
354         else if (newEntry == null) // delete
355
{
356             deleteTree(oldEntry.getDN());
357         }
358         else if (oldEntry.getDN() == null || newEntry.getDN() == null)
359         {
360             throw new NamingException("Internal Error: Entry with null DN passed to JNDIBroker unthreadedModify. Modify Request Cancelled.");
361         }
362         else
363         {
364             // see if the name has changed, and modify it if it has
365
handleAnyNameChange(oldEntry, newEntry);
366
367             // check for change of attributes done in modify()
368
updateEntry(oldEntry, newEntry);
369         }
370     }
371
372     /**
373      * Add the new entry to the directory & sets the status.
374      *
375      * @param newEntry the new entry containing the replacement set of attributes.
376      */

377
378     private void addEntryToDirectory(DXEntry newEntry)
379             throws NamingException
380     {
381         addEntry(newEntry);
382
383         newEntry.setStatus(DXEntry.NEW_WRITTEN); // once it's been added, it's no longer new...
384
}
385
386     /**
387      * Add the new entry to the directory.
388      *
389      * @param newEntry the new entry containing the replacement set of attributes.
390      */

391
392     public void addEntry(DXEntry newEntry)
393             throws NamingException
394     {
395         if (newEntry == null)
396             throw new NamingException("Internal Error: null Entry passed to DXOps addEntry");
397
398         if (newEntry.getDN() == null)
399             throw new NamingException("Internal Error: Entry with null DN passed to DXOps addEntry");
400
401         addEntry(newEntry.getDN(), newEntry);
402     }
403
404
405     /**
406      * If the entry has changed its name, make the required calls to set up the
407      * display tree and make the directory changes.
408      *
409      * @param oldEntry the old entry containing teh old set of attributes.
410      * @param newEntry the new entry containing the replacement set of attributes.
411      */

412
413     private void handleAnyNameChange(DXEntry oldEntry, DXEntry newEntry)
414             throws NamingException
415     {
416         // check for 'simple' rename from the tree, with no attributes involved.
417
RDN oldRDN = oldEntry.getRDN();
418         RDN newRDN = newEntry.getRDN();
419
420         DN oldDN = oldEntry.getDN();
421         DN newDN = newEntry.getDN();
422
423         //System.out.println("oldDN & newDN " + oldDN.toString() + " : " + newDN.toString());
424

425         if (oldDN.equals(newDN))
426             return; // nothing to see here, just move along.
427

428         if (oldEntry.size() == 0 && newEntry.size() == 0) // a very simple rename, probably from the tree
429
{
430             moveTree(oldDN, newDN);
431         }
432         else if (oldRDN.isMultiValued() == false && newRDN.isMultiValued() == false)
433         {
434             //System.out.println("renaming: " + oldDN.toString() + " to " + newDN.toString());
435
renameSingleValuedRDNS(oldEntry, newEntry);
436         }
437         else
438         {
439             renameComplexMultiValuedRDNs(oldEntry, newEntry);
440         }
441     }
442
443     /**
444      * do complex multi-valued RDN rename
445      * WARNING - this assumes that the size and the values of the RDNs will not
446      * BOTH CHANGE AT THE SAME TIME!
447      */

448     private void renameComplexMultiValuedRDNs(DXEntry oldEntry, DXEntry newEntry)
449             throws NamingException
450     {
451         String JavaDoc[] oldRDNs = oldEntry.getRDN().getElements();
452         String JavaDoc[] newRDNs = newEntry.getRDN().getElements();
453
454         DN oldDN = oldEntry.getDN();
455         DN newDN = newEntry.getDN();
456
457         /*
458          * Create a list of 'lost RDNs' -> one's that are in the old RDN set but not in the new one.
459          */

460
461         Object JavaDoc[] temp = CBArray.difference(oldRDNs, newRDNs);
462         String JavaDoc[] lostRDNs = new String JavaDoc[temp.length];
463         for (int i = 0; i < temp.length; i++)
464             lostRDNs[i] = temp[i].toString();
465
466
467
468         /*
469          * Cycle through the list of 'lost' RDNs, working out whether
470          * they are *all* missing from the new Entry (in which case we
471          * can do a rename with 'deleteOldRDNs=true'), or they are *all*
472          * in the new Entry (in which case we can do a rename with
473          * 'deleteOldRDNs=false'). If some are and some aren't, throw
474          * a hopefully useful error message.
475          */

476         final int NOT_SET = 0;
477         final int IN_NEW_ENTRY = 1;
478         final int NOT_IN_NEW_ENTRY = -1;
479
480         int deleteRDNState = NOT_SET;
481
482         for (int i = 0; i < lostRDNs.length; i++)
483         {
484             String JavaDoc RDN = lostRDNs[i]; // get Attribute Value Assertions from lostRDNs
485
String JavaDoc type = RDN.substring(0, RDN.indexOf('='));
486             String JavaDoc value = RDN.substring(RDN.indexOf('=') + 1);
487
488             if (newEntry.get(type).contains(value) == true)
489             {
490                 if (deleteRDNState == NOT_SET) // first time through
491
deleteRDNState = IN_NEW_ENTRY;
492                 if (deleteRDNState != IN_NEW_ENTRY)
493                 {
494                     setWierdoRDNError(oldDN, newDN);
495                 }
496             }
497             else
498             {
499                 if (deleteRDNState == NOT_SET) // first time through
500
deleteRDNState = NOT_IN_NEW_ENTRY;
501
502
503                 if (deleteRDNState != NOT_IN_NEW_ENTRY)
504                 {
505                     setWierdoRDNError(oldDN, newDN);
506                 }
507             }
508         }
509
510         /*
511          * perform the actual rename operation, followed by any entry
512          * tweaking that is required to make everything consistant.
513          */

514
515         if (deleteRDNState == NOT_SET || deleteRDNState == IN_NEW_ENTRY)
516         {
517             renameEntry(oldDN, newDN, false);
518         }
519         else
520         {
521             renameEntry(oldDN, newDN, true);
522             for (int i = 0; i < lostRDNs.length; i++)
523             {
524                 String JavaDoc RDN = lostRDNs[i]; // get Attribute Value Assertions from lostRDNs
525
String JavaDoc type = RDN.substring(0, RDN.indexOf('='));
526                 String JavaDoc value = RDN.substring(RDN.indexOf('=') + 1);
527                 oldEntry.get(type).remove(value); // remove old value so it doesn't get double deleted...
528
}
529         }
530     }
531
532
533     private void renameSingleValuedRDNS(DXEntry oldEntry, DXEntry newEntry)
534             throws NamingException
535     {
536         RDN oldRDN = oldEntry.getRDN();
537
538         String JavaDoc type = oldRDN.getAtt();
539         String JavaDoc value = oldRDN.getRawVal();
540
541         Attribute oldNamingAttInNewEntry = newEntry.get(type);
542         // if the old naming value does not exist in the new entry, drop it!
543
if (!oldNamingAttInNewEntry.contains(value))
544         {
545             renameEntry(oldEntry.getDN(), newEntry.getDN(), true);
546             oldEntry.get(type).remove(value); // remove old value so it doesn't get double deleted...
547
}
548         // if it *does* exist in the new entry, keep it.
549
else
550         {
551             renameEntry(oldEntry.getDN(), newEntry.getDN(), false);
552         }
553     }
554
555
556     /**
557      * This creates and throws a naming exception when the user has attempted a fiendishly complex
558      * and stoopid request that requires a mixture of 'deleteOldRDN=false' and
559      * 'deleteOldRDN=true'. Since JX does not handle indeterminate quantum
560      * states, we throw an error instead.
561      *
562      * @param oldDN
563      * @param newDN
564      * @throws NamingException a detailed error - this is ALWAYS thrown when the method is called.
565      */

566
567     private void setWierdoRDNError(DN oldDN, DN newDN)
568             throws NamingException
569     {
570         throw new NamingException(CBIntText.get("The rename operation is too complex to proceed. Try to break it up into smaller stages.") +
571                 "\n " + oldDN.toString() + "\n => " + newDN.toString());
572     }
573
574
575
576     /*
577      * See if the name has changed, and make required mods if it has.
578      */

579
580     //XXX - note case sensitive string compare to allow user to change capitalization...
581
//TE: XXX can't change case in RDNs???? IF delete RDN = false changing case will fail b/c in the
582
//TE: XXX dir the cn's are case insensitive so renaming from cn=A to cn=a is asking the dir to
583
//TE: XXX have two cn's in the entry that are the same because the old cn will not be deleted i.e the
584
//TE: XXX can't rename to an existing entry...
585
/* if (oldEntry.getDN().equals(newEntry.getDN()) == false)
586         {
587             //Do the rename!
588
589             moveTree(oldEntry.getDN(), newEntry.getDN());
590         }
591     }
592 */

593
594     /**
595      * Update an entry with the designated DN.
596      *
597      * @param oldSet the old entry containing teh old set of attributes.
598      * @param newSet the new entry containing the replacement set of attributes.
599      * @throws NamingException if the operation fails.
600      */

601
602     public void updateEntry(DXEntry oldSet, DXEntry newSet)
603             throws NamingException
604     {
605
606         if (DXAttributes.attributesEqual(oldSet, newSet))
607             return; // nothing to do.
608

609         DN nodeDN = newSet.getDN();
610         RDN newRDN = nodeDN.getLowestRDN();
611
612         DXAttributes adds = null; // use modify-add for new attribute values (inc entirely new attribute)
613
DXAttributes reps = null; // use modify-replace for changed attribute values
614
DXAttributes dels = null; // use modify-delete for deleted attribute values (inc deleting entire attribute)
615

616         reps = DXAttributes.getReplacementSet(newRDN, oldSet, newSet);
617         dels = DXAttributes.getDeletionSet(newRDN, oldSet, newSet);
618         adds = DXAttributes.getAdditionSet(newRDN, oldSet, newSet);
619
620         if (false)
621             printDebug(oldSet, newSet, adds, reps, dels);
622
623         log.fine("updateNode: " + nodeDN);
624
625         ModificationItem[] mods;
626
627         mods = new ModificationItem[dels.size() + reps.size() + adds.size()];
628
629         int modIndex = 0;
630         modIndex = loadMods(mods, dels.getAll(), DirContext.REMOVE_ATTRIBUTE, modIndex);
631         modIndex = loadMods(mods, adds.getAll(), DirContext.ADD_ATTRIBUTE, modIndex);
632         modIndex = loadMods(mods, reps.getAll(), DirContext.REPLACE_ATTRIBUTE, modIndex);
633
634         modifyAttributes(nodeDN, mods); //TE: This may fail, returning false.
635
}
636
637
638     /**
639      * Utility ftn for updateNode - takes a list of attributes to modify, and
640      * the type of modification, and adds them to an array of modifications (starting
641      * at a particular index).
642      *
643      * @param mods the array of modification items
644      * @param atts an enumeration of attributes to add to the mod array
645      * @param TYPE the type of modification (DELETE,REPLACE,ADD)
646      * @param index the position in the modification array to start filling entries in
647      * @return return the final index position reached.
648      */

649
650     private int loadMods(ModificationItem[] mods, NamingEnumeration atts, int TYPE, int index)
651             throws NamingException
652     {
653         while (atts.hasMore())
654         {
655             Attribute temp = (Attribute) atts.next();
656             mods[index++] = new ModificationItem(TYPE, temp);
657         }
658         return index;
659     }
660
661
662     /**
663      * Optional debug code. Very useful. NEVER REMOVE!!!
664      *
665      * @param oldSet old entry.
666      * @param newSet new entry.
667      * @param adds list of attributes to add.
668      * @param reps list of attributes to replace.
669      * @param dels list of attributes to delete.
670      */

671
672     private void printDebug(DXEntry oldSet, DXEntry newSet, DXAttributes adds, DXAttributes reps, DXAttributes dels)
673     {
674
675
676         System.out.println("\n*** entries are ***\n\nold:\n" + oldSet.toString() + "\n\nnew:\n" + newSet.toString());
677         System.out.println("\n-----------------\nreps:\n" + reps.toString());
678         System.out.println("\n-----------------\ndels:\n" + dels.toString());
679         System.out.println("\n-----------------\nadds:\n" + adds.toString());
680         //Thread.currentThread().dumpStack();
681
}
682 }
Popular Tags