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
26package com.sun.jndi.ldap;
27
28import javax.naming.*;
29import javax.naming.ldap.Control;
30
31import java.util.Hashtable;
32import 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  */
67final 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     */
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     */
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     */
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     */
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      */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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