KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > jndi > AdvancedOps


1 package com.ca.commons.jndi;
2
3 import javax.naming.*;
4 import javax.naming.directory.*;
5 import java.util.ArrayList JavaDoc;
6 import java.util.ListIterator JavaDoc;
7 import java.util.logging.Logger JavaDoc;
8
9 /**
10  * The AdvancedOps class extends BasicOps to allow for complex directory
11  * operations such as manipulating entire trees. <p>
12  *
13  * It requires initialisation with a Directory Context, through which
14  * all the low level directory calls are passed (basicOps is a wrapper
15  * to jndi).
16  *
17  * It contains a number of functions (pop(), push() and inc() )
18  * that may be over-ridden by
19  * classes derived from this that with to track progress.
20  *
21  */

22 public class AdvancedOps extends BasicOps
23 {
24 // protected BasicOps dirOp;
25

26     protected NameParser parser;
27
28     private final static Logger JavaDoc log = Logger.getLogger(AdvancedOps.class.getName());
29
30     /**
31      * Initialise the AdvancedOps object with a BasicOps object. <p>
32      * Warning: the basic ops object is used to obtain a Name Parser
33      * for the current context, which is asumed to be homogenous:
34      * AdvancedOps does *not* support tree operations across multiple
35      * name spaces.
36      */

37
38     public AdvancedOps(DirContext c)
39             throws NamingException
40     {
41         super(c);
42         parser = getBaseNameParser();
43     }
44
45     /**
46      * <p>Create a AdvancedOps object with a ConnectionData object. </p>
47      * <p>Warning: the basic ops object is used to obtain a Name Parser
48      * for the current context, which is asumed to be homogenous:
49      * AdvancedOps does *not* support tree operations across multiple
50      * name spaces.</p>
51      */

52
53     public AdvancedOps(ConnectionData cData)
54             throws NamingException
55     {
56         super(cData);
57         parser = getBaseNameParser();
58     }
59
60
61     /**
62      * Factory Method to create BasicOps objects, initialised
63      * with an ldap context created from the connectionData,
64      * and maintaining a reference to that connectionData.
65      *
66      * @param cData the details of the directory to connect to
67      * @return an AdvancedOps object (although it must be cast to
68      * this from the BasicOps required by the method sig - is there
69      * a better way of doing this?).
70      */

71
72     public static BasicOps getInstance(ConnectionData cData)
73             throws NamingException
74     {
75         AdvancedOps newObject = new AdvancedOps(openContext(cData));
76         return newObject;
77     }
78
79     /**
80      * overload this method for progress tracker.
81      */

82
83     public void startOperation(String JavaDoc heading, String JavaDoc operationName)
84     {
85     }
86
87     /**
88      * overload this method for progress tracker.
89      */

90
91     public void stopOperation()
92     {
93     }
94
95     /**
96      * overload this method for progress tracker.
97      */

98
99     public void pop()
100     {
101     }
102
103     /**
104      * overload this method for progress tracker. Note that elements
105      * is passed to allow determination of the number of objects - but
106      * the Enumeration must be returned without being reset, so be carefull
107      * when using it...
108      */

109
110     public NamingEnumeration push(NamingEnumeration elements)
111     {
112         return elements;
113     }
114
115     /**
116      * <p>New version of push is faster; doesn't require inheriting class to create
117      * intermediate enumeration.</p>
118       * @param elements
119      */

120     public void push(ArrayList JavaDoc elements)
121     {
122
123     }
124     /**
125      * overload this method for progress tracker.
126      */

127
128     public void inc()
129     {
130     }
131
132     /*
133      *
134      * TREE FUNCTIONS
135      *
136      */

137
138     public void deleteTree(Name nodeDN) // may be a single node.
139
throws NamingException
140     {
141
142         try
143         {
144             if (nodeDN == null)
145                 throw new NamingException("null DN passed to deleteTree.");
146
147             log.finer("recursively delete Tree " + nodeDN.toString());
148
149             startOperation("Deleting " + nodeDN.toString(), "deleted ");
150             recDeleteTree(nodeDN);
151         }
152         finally
153         {
154             stopOperation();
155         }
156
157     }
158
159     /**
160      * deletes a subtree by recursively deleting sub-sub trees.
161      *
162      * @param dn the distinguished name of the sub-tree apex to delete.
163      */

164
165     protected void recDeleteTree(Name dn)
166             throws NamingException
167     {
168         log.info("deleting " + dn);
169
170         ArrayList JavaDoc childArray = getChildren(dn);
171
172         push(childArray); // inform progress tracker that we're going down a level.
173

174         ListIterator JavaDoc children = childArray.listIterator();
175
176         while (children.hasNext())
177         {
178             recDeleteTree((Name) children.next());
179         }
180         pop(); // inform progress tracker that we've come up.
181

182         deleteEntry(dn);
183         inc(); // inform progress tracker that we've deleted an object.
184
}
185
186     /*
187      *
188      * MOVE TREE FUNCTIONS
189      *
190      */

191
192     /**
193      * Moves a DN to a new DN, including all subordinate entries.
194      * (nb it is up to the implementer how this is done; e.g. if it is an
195      * ldap broker, it may choose rename, or copy-and-delete, as appropriate)
196      *
197      * @param oldNodeDN the original DN of the sub tree root (may be a single
198      * entry).
199      * @param newNodeDN the new DN of the sub tree to modify the old tree to.
200      */

201
202     public void moveTree(Name oldNodeDN, Name newNodeDN) // may be a single node.
203
throws NamingException
204     {
205         try
206         {
207             if (oldNodeDN == null)
208                 throw new NamingException("the original DN passed to moveTree is null.");
209
210             if (newNodeDN == null)
211                 throw new NamingException("the destination DN passed to moveTree is null.");
212
213             log.finer("recursively move tree from " + oldNodeDN.toString() + " to " + newNodeDN.toString());
214
215             startOperation("Moving " + oldNodeDN.toString(), "moving");
216             recMoveTree(oldNodeDN, newNodeDN);
217         }
218         finally
219         {
220             stopOperation();
221         }
222     }
223
224     /**
225      * <p>Moves a tree. If the new position is a sibling of the current
226      * position a <i>rename</i> is performed, otherwise a new tree must
227      * be created, with all its children, and then the old tree deleted.<p>
228      *
229      * <p>If the new tree creation fails during creation, an attempt is made
230      * to delete the new tree, and the operation fails. If the new tree
231      * creation succeeds, but the old tree deletion fails, the operation
232      * fails, leaving the new tree and the partial old tree in existence.
233      * (This last should be unlikely.)</p>
234      *
235      * <p>This move *deletes* the old value of the RDN when the node is
236      * moved.</p>
237      *
238      * @param from the root DN of the tree to be moved
239      * @param to the root DN of the new tree position
240      */

241
242     protected void recMoveTree(Name from, Name to) // may be a single node.
243
throws NamingException
244     {
245         if (from.size() == to.size() && from.startsWith(to.getPrefix(to.size() - 1))) // DNs are siblings...
246
{
247             try
248             {
249                 renameEntry(from, to, true);
250             }
251             // special purpose hack to get around directories such as openldap that don't support rename of parents.
252
catch (javax.naming.ContextNotEmptyException JavaDoc e)
253             {
254                 if (e.getMessage().indexOf("subtree rename not supported") > -1)
255                 {
256                     recCopyAndDeleteTree(from, to);
257                 }
258             }
259         }
260         else // DNs are not siblings; so copy them
261
{ // from tree, and then delete the original
262
recCopyAndDeleteTree(from, to);
263         }
264     }
265
266     private void recCopyAndDeleteTree(Name from, Name to)
267             throws NamingException
268     {
269         //TE: does the 'from' DN exist? What if someone gets the DNs around the wrong way? For example
270
// in JXweb a user can enter the DN of where to move from & to...what if they, by mistake,
271
// make the 'to' field the 'from' field? The actual real data will be deleted b/c the copy will
272
// fail due to the 'from' DN not existing and this will fall through to recDeletTree(to)!
273
if (!exists(from))
274             throw new NamingException("The DN that you are trying to move does not exist.");
275
276         try
277         {
278             recCopyTree(from, to);
279         }
280         catch (NamingException e)
281         {
282             recDeleteTree(to); // Try to clean up
283
throw e; // then rethrow exception
284
}
285
286         recDeleteTree(from);
287     }
288
289     /*
290      *
291      * COPY TREE FUNCTIONS
292      *
293      */

294
295
296     /**
297      * Copies a DN representing a subtree to a new subtree, including
298      * copying all subordinate entries.
299      *
300      * @param oldNodeDN the original DN of the sub tree root
301      * to be copied (may be a single entry).
302      * @param newNodeDN the target DN for the tree to be moved to.
303      */

304
305     public void copyTree(Name oldNodeDN, Name newNodeDN) // may be a single node.
306
throws NamingException
307     {
308         try
309         {
310             if (oldNodeDN == null)
311                 throw new NamingException("the original DN passed to copyTree is null.");
312
313             if (newNodeDN == null)
314                 throw new NamingException("the destination DN passed to copyTree is null.");
315
316             log.finer("recursively copy tree from " + oldNodeDN.toString() + " to " + newNodeDN.toString());
317
318             startOperation("Copying " + oldNodeDN.toString(), "copying");
319             recCopyTree(oldNodeDN, newNodeDN);
320         }
321         finally
322         {
323             stopOperation();
324         }
325     }
326
327
328     /**
329      * Takes two DNs, and goes through the first, copying each element
330      * from the top down to the new DN.
331      *
332      * @param from the ldap Name dn to copy the tree from
333      * @param to the ldap Name dn to copy the tree to
334      */

335
336     protected void recCopyTree(Name from, Name to)
337             throws NamingException
338     {
339         copyEntry(from, to); // where the work finally gets done
340

341         inc();
342
343         ArrayList JavaDoc childArray = getChildren(from);
344
345         push(childArray);
346
347         ListIterator JavaDoc children = childArray.listIterator();
348
349         while (children.hasNext())
350         {
351             Name childDN = (Name)children.next();
352
353             Name destinationDN = (Name)to.clone();
354             destinationDN.add(childDN.get(childDN.size()-1));
355
356             recCopyTree(childDN, destinationDN);
357         }
358         pop();
359     }
360
361     /**
362      * <p>This searches for all the children of the given named entry, and returns them as an
363      * ArrayList.</p>
364      * @param base the base entry to search from
365      * @return an ArrayList of [Name] objects representing children.
366      */

367     protected ArrayList JavaDoc getChildren(Name base)
368         throws NamingException
369     {
370         ArrayList JavaDoc children = new ArrayList JavaDoc();
371         NamingEnumeration rawList = list(base);
372
373           if (rawList.hasMoreElements())
374           {
375               while (rawList.hasMoreElements())
376               {
377                 NameClassPair child = (NameClassPair)rawList.next();
378 // if (child.isRelative()) not 'isRelative' appears unreliable as of java 1.4
379

380 // XXX Because of apparent short comings in jndi (or maybe I'm using it wrong?) it seems impossible to
381
// tell whether a DN is relative to the search base or not. This is particularly bad when it comes
382
// to dealing with aliases...! So we check its size instead :-/
383
//
384
Name childDN = parser.parse(child.getName());
385
386                 if (childDN.size() == 1)
387                 {
388                      childDN = ((Name)base.clone()).add(child.getName());
389                 }
390
391                 children.add(childDN);
392               }
393           }
394         return children;
395     }
396 }
Popular Tags