1 /*
   2  * Copyright (c) 1999, 2011, 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      */
  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 ? reqCtls : null);
 121     }
 122 
 123     /**
 124      * Gets a context at which to continue processing.
 125      * The current environment properties are re-used.
 126      */
 127     public Context getReferralContext() throws NamingException {
 128         return getReferralContext(envprops, null);
 129     }
 130 
 131     /**
 132      * Gets a context at which to continue processing.
 133      * The supplied environment properties are used.
 134      */
 135     public Context getReferralContext(Hashtable<?,?> newProps) throws
 136         NamingException {
 137         return getReferralContext(newProps, null);
 138     }
 139 
 140     /**
 141      * Gets a context at which to continue processing.
 142      * The supplied environment properties and connection controls are used.
 143      */
 144     public Context getReferralContext(Hashtable<?,?> newProps, Control[] connCtls)
 145         throws NamingException {
 146 
 147         if (debug)
 148             System.out.println("LdapReferralException.getReferralContext");
 149 
 150         LdapReferralContext refCtx = new LdapReferralContext(
 151             this, newProps, connCtls, reqCtls,
 152             nextName, skipThisReferral, handleReferrals);
 153 
 154         refCtx.setHopCount(hopCount + 1);
 155 
 156         if (skipThisReferral) {
 157             skipThisReferral = false; // reset
 158         }
 159         return (Context)refCtx;
 160     }
 161 
 162     /**
 163       * Gets referral information.
 164       */
 165     public Object getReferralInfo() {
 166         if (debug) {
 167             System.out.println("LdapReferralException.getReferralInfo");
 168             System.out.println("  referralIndex=" + referralIndex);
 169         }
 170 
 171         if (hasMoreReferrals()) {
 172             return referrals.elementAt(referralIndex);
 173         } else {
 174             return null;
 175         }
 176     }
 177 
 178     /**
 179      * Marks the current referral as one to be retried.
 180      */
 181     public void retryReferral() {
 182         if (debug)
 183             System.out.println("LdapReferralException.retryReferral");
 184 
 185         if (referralIndex > 0)
 186             referralIndex--; // decrement index
 187     }
 188 
 189     /**
 190      * Marks the current referral as one to be ignored.
 191      * Returns false when there are no referrals remaining to be processed.
 192      */
 193     public boolean skipReferral() {
 194         if (debug)
 195             System.out.println("LdapReferralException.skipReferral");
 196 
 197         skipThisReferral = true;
 198 
 199         // advance to next referral
 200         try {
 201             getNextReferral();
 202         } catch (ReferralException e) {
 203             // mask the referral exception
 204         }
 205 
 206         return (hasMoreReferrals() || hasMoreReferralExceptions());
 207     }
 208 
 209 
 210     /**
 211      * Sets referral information.
 212      */
 213     void setReferralInfo(Vector<?> referrals, boolean continuationRef) {
 214         // %%% continuationRef is currently ignored
 215 
 216         if (debug)
 217             System.out.println("LdapReferralException.setReferralInfo");
 218 
 219         this.referrals = referrals;
 220         referralCount = (referrals == null) ? 0 : referrals.size();
 221 
 222         if (debug) {
 223             if (referrals != null) {
 224                 for (int i = 0; i < referralCount; i++) {
 225                     System.out.println("  [" + i + "] " + referrals.elementAt(i));
 226                 }
 227             } else {
 228                 System.out.println("setReferralInfo : referrals == null");
 229             }
 230         }
 231     }
 232 
 233     /**
 234      * Gets the next referral. When the current set of referrals have
 235      * been exhausted then the next referral exception is thrown, if available.
 236      */
 237     String getNextReferral() throws ReferralException {
 238 
 239         if (debug)
 240             System.out.println("LdapReferralException.getNextReferral");
 241 
 242         if (hasMoreReferrals()) {
 243             return (String)referrals.elementAt(referralIndex++);
 244         } else if (hasMoreReferralExceptions()) {
 245             throw nextReferralEx;
 246         } else {
 247             return null;
 248         }
 249     }
 250 
 251     /**
 252      * Appends the supplied (chain of) referral exception onto the end of
 253      * the current (chain of) referral exception. Spent referral exceptions
 254      * are trimmed off.
 255      */
 256     LdapReferralException
 257         appendUnprocessedReferrals(LdapReferralException back) {
 258 
 259         if (debug) {
 260             System.out.println(
 261                 "LdapReferralException.appendUnprocessedReferrals");
 262             dump();
 263             if (back != null) {
 264                 back.dump();
 265             }
 266         }
 267 
 268         LdapReferralException front = this;
 269 
 270         if (! front.hasMoreReferrals()) {
 271             front = nextReferralEx; // trim
 272 
 273             if ((errorEx != null) && (front != null)) {
 274                 front.setNamingException(errorEx); //advance the saved exception
 275             }
 276         }
 277 
 278         // don't append onto itself
 279         if (this == back) {
 280             return front;
 281         }
 282 
 283         if ((back != null) && (! back.hasMoreReferrals())) {
 284             back = back.nextReferralEx; // trim
 285         }
 286 
 287         if (back == null) {
 288             return front;
 289         }
 290 
 291         // Locate the end of the current chain
 292         LdapReferralException ptr = front;
 293         while (ptr.nextReferralEx != null) {
 294             ptr = ptr.nextReferralEx;
 295         }
 296         ptr.nextReferralEx = back; // append
 297 
 298         return front;
 299     }
 300 
 301     /**
 302      * Tests if there are any referrals remaining to be processed.
 303      * If name resolution has already completed then any remaining
 304      * referrals (in the current referral exception) will be ignored.
 305      */
 306     boolean hasMoreReferrals() {
 307         if (debug)
 308             System.out.println("LdapReferralException.hasMoreReferrals");
 309 
 310         return (! foundEntry) && (referralIndex < referralCount);
 311     }
 312 
 313     /**
 314      * Tests if there are any referral exceptions remaining to be processed.
 315      */
 316     boolean hasMoreReferralExceptions() {
 317         if (debug)
 318             System.out.println(
 319                 "LdapReferralException.hasMoreReferralExceptions");
 320 
 321         return (nextReferralEx != null);
 322     }
 323 
 324     /**
 325      * Sets the counter which records the number of hops that result
 326      * from following a sequence of referrals.
 327      */
 328     void setHopCount(int hopCount) {
 329         if (debug)
 330             System.out.println("LdapReferralException.setHopCount");
 331 
 332         this.hopCount = hopCount;
 333     }
 334 
 335     /**
 336      * Sets the flag to indicate that the target name has been resolved.
 337      */
 338     void setNameResolved(boolean resolved) {
 339         if (debug)
 340             System.out.println("LdapReferralException.setNameResolved");
 341 
 342         foundEntry = resolved;
 343     }
 344 
 345     /**
 346      * Sets the exception generated while processing a referral.
 347      * Only the first exception is recorded.
 348      */
 349     void setNamingException(NamingException e) {
 350         if (debug)
 351             System.out.println("LdapReferralException.setNamingException");
 352 
 353         if (errorEx == null) {
 354             e.setRootCause(this); //record the referral exception that caused it
 355             errorEx = e;
 356         }
 357     }
 358 
 359     /**
 360      * Gets the new RDN name.
 361      */
 362     String getNewRdn() {
 363         if (debug)
 364             System.out.println("LdapReferralException.getNewRdn");
 365 
 366         return newRdn;
 367     }
 368 
 369     /**
 370      * Sets the new RDN name so that the rename operation can be completed
 371      * (when a referral is being followed).
 372      */
 373     void setNewRdn(String newRdn) {
 374         if (debug)
 375             System.out.println("LdapReferralException.setNewRdn");
 376 
 377         this.newRdn = newRdn;
 378     }
 379 
 380     /**
 381      * Gets the exception generated while processing a referral.
 382      */
 383     NamingException getNamingException() {
 384         if (debug)
 385             System.out.println("LdapReferralException.getNamingException");
 386 
 387         return errorEx;
 388     }
 389 
 390     /**
 391      * Display the state of each element in a chain of LdapReferralException
 392      * objects.
 393      */
 394     void dump() {
 395 
 396         System.out.println();
 397         System.out.println("LdapReferralException.dump");
 398         LdapReferralException ptr = this;
 399         while (ptr != null) {
 400             ptr.dumpState();
 401             ptr = ptr.nextReferralEx;
 402         }
 403     }
 404 
 405     /**
 406      * Display the state of this LdapReferralException object.
 407      */
 408     private void dumpState() {
 409         System.out.println("LdapReferralException.dumpState");
 410         System.out.println("  hashCode=" + hashCode());
 411         System.out.println("  foundEntry=" + foundEntry);
 412         System.out.println("  skipThisReferral=" + skipThisReferral);
 413         System.out.println("  referralIndex=" + referralIndex);
 414 
 415         if (referrals != null) {
 416             System.out.println("  referrals:");
 417             for (int i = 0; i < referralCount; i++) {
 418                 System.out.println("    [" + i + "] " + referrals.elementAt(i));
 419             }
 420         } else {
 421             System.out.println("  referrals=null");
 422         }
 423 
 424         System.out.println("  errorEx=" + errorEx);
 425 
 426         if (nextReferralEx == null) {
 427             System.out.println("  nextRefEx=null");
 428         } else {
 429             System.out.println("  nextRefEx=" + nextReferralEx.hashCode());
 430         }
 431         System.out.println();
 432     }
 433 }