1 /*
2  * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.jndi.ldap;
27 
28 import javax.naming.*;
29 import javax.naming.ldap.Control;
30 
31 import java.util.Hashtable;
32 import java.util.Vector;
33 
34 /**
35   * This exception is raised when a referral to an alternative context
36   * is encountered.
37   * <p>
38   * An {@code LdapReferralException} object contains one or more referrals.
39   * Each referral is an alternative location for the same target entry.
40   * For example, a referral may be an LDAP URL.
41   * The referrals are attempted in sequence until one is successful or
42   * all have failed. In the case of the latter then the exception generated
43   * by the final referral is recorded and presented later.
44   * <p>
45   * A referral may be skipped or may be retried. For example, in the case
46   * of an authentication error, a referral may be retried with different
47   * environment properties.
48   * <p>
49   * An {@code LdapReferralException} object may also contain a reference
50   * to a chain of unprocessed {@code LdapReferralException} objects.
51   * Once the current set of referrals have been exhausted and unprocessed
52   * {@code LdapReferralException} objects remain, then the
53   * {@code LdapReferralException} object referenced by the current
54   * object is thrown and the cycle continues.
55   * <p>
56   * If new {@code LdapReferralException} objects are generated while
57   * following an existing referral then these new objects are appended
58   * to the end of the chain of unprocessed {@code LdapReferralException}
59   * objects.
60   * <p>
61   * If an exception was recorded while processing a chain of
62   * {@code LdapReferralException} objects then it is throw once
63   * processing has completed.
64   *
65   * @author Vincent Ryan
66   */
67 final public class LdapReferralException extends
68     javax.naming.ldap.LdapReferralException {
69     private static final long serialVersionUID = 627059076356906399L;
70 
71         // ----------- fields initialized in constructor ---------------
72     private int handleReferrals;
73     private Hashtable<?,?> envprops;
74     private String nextName;
75     private Control[] reqCtls;
76 
77         // ----------- fields that have defaults -----------------------
78     private Vector<?> referrals = null; // alternatives,set by setReferralInfo()
79     private int referralIndex = 0;      // index into referrals
80     private int referralCount = 0;      // count of referrals
81     private boolean foundEntry = false; // will stop when entry is found
82     private boolean skipThisReferral = false;
83     private int hopCount = 1;
84     private NamingException errorEx = null;
85     private String newRdn = null;
86     private boolean debug = false;
87             LdapReferralException nextReferralEx = null; // referral ex. chain
88 
89     /**
90      * Constructs a new instance of LdapReferralException.
91      * @param   resolvedName    The part of the name that has been successfully
92      *                          resolved.
93      * @param   resolvedObj     The object to which resolution was successful.
94      * @param   remainingName   The remaining unresolved portion of the name.
95      * @param   explanation     Additional detail about this exception.
96      */
LdapReferralException(Name resolvedName, Object resolvedObj, Name remainingName, String explanation, Hashtable<?,?> envprops, String nextName, int handleReferrals, Control[] reqCtls)97     LdapReferralException(Name resolvedName,
98         Object resolvedObj,
99         Name remainingName,
100         String explanation,
101         Hashtable<?,?> envprops,
102         String nextName,
103         int handleReferrals,
104         Control[] reqCtls) {
105 
106         super(explanation);
107 
108         if (debug)
109             System.out.println("LdapReferralException constructor");
110 
111         setResolvedName(resolvedName);
112         setResolvedObj(resolvedObj);
113         setRemainingName(remainingName);
114         this.envprops = envprops;
115         this.nextName = nextName;
116         this.handleReferrals = handleReferrals;
117 
118         // If following referral, request controls are passed to referral ctx
119         this.reqCtls =
120             (handleReferrals == LdapClient.LDAP_REF_FOLLOW ||
121                     handleReferrals == LdapClient.LDAP_REF_FOLLOW_SCHEME ? reqCtls : null);
122     }
123 
124     /**
125      * Gets a context at which to continue processing.
126      * The current environment properties are re-used.
127      */
getReferralContext()128     public Context getReferralContext() throws NamingException {
129         return getReferralContext(envprops, null);
130     }
131 
132     /**
133      * Gets a context at which to continue processing.
134      * The supplied environment properties are used.
135      */
getReferralContext(Hashtable<?,?> newProps)136     public Context getReferralContext(Hashtable<?,?> newProps) throws
137         NamingException {
138         return getReferralContext(newProps, null);
139     }
140 
141     /**
142      * Gets a context at which to continue processing.
143      * The supplied environment properties and connection controls are used.
144      */
getReferralContext(Hashtable<?,?> newProps, Control[] connCtls)145     public Context getReferralContext(Hashtable<?,?> newProps, Control[] connCtls)
146         throws NamingException {
147 
148         if (debug)
149             System.out.println("LdapReferralException.getReferralContext");
150 
151         LdapReferralContext refCtx = new LdapReferralContext(
152             this, newProps, connCtls, reqCtls,
153             nextName, skipThisReferral, handleReferrals);
154 
155         refCtx.setHopCount(hopCount + 1);
156 
157         if (skipThisReferral) {
158             skipThisReferral = false; // reset
159         }
160         return (Context)refCtx;
161     }
162 
163     /**
164       * Gets referral information.
165       */
getReferralInfo()166     public Object getReferralInfo() {
167         if (debug) {
168             System.out.println("LdapReferralException.getReferralInfo");
169             System.out.println("  referralIndex=" + referralIndex);
170         }
171 
172         if (hasMoreReferrals()) {
173             return referrals.elementAt(referralIndex);
174         } else {
175             return null;
176         }
177     }
178 
179     /**
180      * Marks the current referral as one to be retried.
181      */
retryReferral()182     public void retryReferral() {
183         if (debug)
184             System.out.println("LdapReferralException.retryReferral");
185 
186         if (referralIndex > 0)
187             referralIndex--; // decrement index
188     }
189 
190     /**
191      * Marks the current referral as one to be ignored.
192      * Returns false when there are no referrals remaining to be processed.
193      */
skipReferral()194     public boolean skipReferral() {
195         if (debug)
196             System.out.println("LdapReferralException.skipReferral");
197 
198         skipThisReferral = true;
199 
200         // advance to next referral
201         try {
202             getNextReferral();
203         } catch (ReferralException e) {
204             // mask the referral exception
205         }
206 
207         return (hasMoreReferrals() || hasMoreReferralExceptions());
208     }
209 
210 
211     /**
212      * Sets referral information.
213      */
setReferralInfo(Vector<?> referrals, boolean continuationRef)214     void setReferralInfo(Vector<?> referrals, boolean continuationRef) {
215         // %%% continuationRef is currently ignored
216 
217         if (debug)
218             System.out.println("LdapReferralException.setReferralInfo");
219 
220         this.referrals = referrals;
221         referralCount = (referrals == null) ? 0 : referrals.size();
222 
223         if (debug) {
224             if (referrals != null) {
225                 for (int i = 0; i < referralCount; i++) {
226                     System.out.println("  [" + i + "] " + referrals.elementAt(i));
227                 }
228             } else {
229                 System.out.println("setReferralInfo : referrals == null");
230             }
231         }
232     }
233 
234     /**
235      * Gets the next referral. When the current set of referrals have
236      * been exhausted then the next referral exception is thrown, if available.
237      */
getNextReferral()238     String getNextReferral() throws ReferralException {
239 
240         if (debug)
241             System.out.println("LdapReferralException.getNextReferral");
242 
243         if (hasMoreReferrals()) {
244             return (String)referrals.elementAt(referralIndex++);
245         } else if (hasMoreReferralExceptions()) {
246             throw nextReferralEx;
247         } else {
248             return null;
249         }
250     }
251 
252     /**
253      * Appends the supplied (chain of) referral exception onto the end of
254      * the current (chain of) referral exception. Spent referral exceptions
255      * are trimmed off.
256      */
257     LdapReferralException
appendUnprocessedReferrals(LdapReferralException back)258         appendUnprocessedReferrals(LdapReferralException back) {
259 
260         if (debug) {
261             System.out.println(
262                 "LdapReferralException.appendUnprocessedReferrals");
263             dump();
264             if (back != null) {
265                 back.dump();
266             }
267         }
268 
269         LdapReferralException front = this;
270 
271         if (! front.hasMoreReferrals()) {
272             front = nextReferralEx; // trim
273 
274             if ((errorEx != null) && (front != null)) {
275                 front.setNamingException(errorEx); //advance the saved exception
276             }
277         }
278 
279         // don't append onto itself
280         if (this == back) {
281             return front;
282         }
283 
284         if ((back != null) && (! back.hasMoreReferrals())) {
285             back = back.nextReferralEx; // trim
286         }
287 
288         if (back == null) {
289             return front;
290         }
291 
292         // Locate the end of the current chain
293         LdapReferralException ptr = front;
294         while (ptr.nextReferralEx != null) {
295             ptr = ptr.nextReferralEx;
296         }
297         ptr.nextReferralEx = back; // append
298 
299         return front;
300     }
301 
302     /**
303      * Tests if there are any referrals remaining to be processed.
304      * If name resolution has already completed then any remaining
305      * referrals (in the current referral exception) will be ignored.
306      */
hasMoreReferrals()307     boolean hasMoreReferrals() {
308         if (debug)
309             System.out.println("LdapReferralException.hasMoreReferrals");
310 
311         return (! foundEntry) && (referralIndex < referralCount);
312     }
313 
314     /**
315      * Tests if there are any referral exceptions remaining to be processed.
316      */
hasMoreReferralExceptions()317     boolean hasMoreReferralExceptions() {
318         if (debug)
319             System.out.println(
320                 "LdapReferralException.hasMoreReferralExceptions");
321 
322         return (nextReferralEx != null);
323     }
324 
325     /**
326      * Sets the counter which records the number of hops that result
327      * from following a sequence of referrals.
328      */
setHopCount(int hopCount)329     void setHopCount(int hopCount) {
330         if (debug)
331             System.out.println("LdapReferralException.setHopCount");
332 
333         this.hopCount = hopCount;
334     }
335 
336     /**
337      * Sets the flag to indicate that the target name has been resolved.
338      */
setNameResolved(boolean resolved)339     void setNameResolved(boolean resolved) {
340         if (debug)
341             System.out.println("LdapReferralException.setNameResolved");
342 
343         foundEntry = resolved;
344     }
345 
346     /**
347      * Sets the exception generated while processing a referral.
348      * Only the first exception is recorded.
349      */
setNamingException(NamingException e)350     void setNamingException(NamingException e) {
351         if (debug)
352             System.out.println("LdapReferralException.setNamingException");
353 
354         if (errorEx == null) {
355             e.setRootCause(this); //record the referral exception that caused it
356             errorEx = e;
357         }
358     }
359 
360     /**
361      * Gets the new RDN name.
362      */
getNewRdn()363     String getNewRdn() {
364         if (debug)
365             System.out.println("LdapReferralException.getNewRdn");
366 
367         return newRdn;
368     }
369 
370     /**
371      * Sets the new RDN name so that the rename operation can be completed
372      * (when a referral is being followed).
373      */
setNewRdn(String newRdn)374     void setNewRdn(String newRdn) {
375         if (debug)
376             System.out.println("LdapReferralException.setNewRdn");
377 
378         this.newRdn = newRdn;
379     }
380 
381     /**
382      * Gets the exception generated while processing a referral.
383      */
getNamingException()384     NamingException getNamingException() {
385         if (debug)
386             System.out.println("LdapReferralException.getNamingException");
387 
388         return errorEx;
389     }
390 
391     /**
392      * Display the state of each element in a chain of LdapReferralException
393      * objects.
394      */
dump()395     void dump() {
396 
397         System.out.println();
398         System.out.println("LdapReferralException.dump");
399         LdapReferralException ptr = this;
400         while (ptr != null) {
401             ptr.dumpState();
402             ptr = ptr.nextReferralEx;
403         }
404     }
405 
406     /**
407      * Display the state of this LdapReferralException object.
408      */
dumpState()409     private void dumpState() {
410         System.out.println("LdapReferralException.dumpState");
411         System.out.println("  hashCode=" + hashCode());
412         System.out.println("  foundEntry=" + foundEntry);
413         System.out.println("  skipThisReferral=" + skipThisReferral);
414         System.out.println("  referralIndex=" + referralIndex);
415 
416         if (referrals != null) {
417             System.out.println("  referrals:");
418             for (int i = 0; i < referralCount; i++) {
419                 System.out.println("    [" + i + "] " + referrals.elementAt(i));
420             }
421         } else {
422             System.out.println("  referrals=null");
423         }
424 
425         System.out.println("  errorEx=" + errorEx);
426 
427         if (nextReferralEx == null) {
428             System.out.println("  nextRefEx=null");
429         } else {
430             System.out.println("  nextRefEx=" + nextReferralEx.hashCode());
431         }
432         System.out.println();
433     }
434 }
435