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         if (referrals != null) {
 221             referralCount = referrals.size();
 222         }
 223 
 224         if (debug) {
 225             for (int i = 0; i < referralCount; i++) {
 226                 System.out.println("  [" + i + "] " + referrals.elementAt(i));
 227             }
 228         }
 229     }
 230 
 231     /**
 232      * Gets the next referral. When the current set of referrals have
 233      * been exhausted then the next referral exception is thrown, if available.
 234      */
 235     String getNextReferral() throws ReferralException {
 236 
 237         if (debug)
 238             System.out.println("LdapReferralException.getNextReferral");
 239 
 240         if (hasMoreReferrals()) {
 241             return (String)referrals.elementAt(referralIndex++);
 242         } else if (hasMoreReferralExceptions()) {
 243             throw nextReferralEx;
 244         } else {
 245             return null;
 246         }
 247     }
 248 
 249     /**
 250      * Appends the supplied (chain of) referral exception onto the end of
 251      * the current (chain of) referral exception. Spent referral exceptions
 252      * are trimmed off.
 253      */
 254     LdapReferralException
 255         appendUnprocessedReferrals(LdapReferralException back) {
 256 
 257         if (debug) {
 258             System.out.println(
 259                 "LdapReferralException.appendUnprocessedReferrals");
 260             dump();
 261             if (back != null) {
 262                 back.dump();
 263             }
 264         }
 265 
 266         LdapReferralException front = this;
 267 
 268         if (! front.hasMoreReferrals()) {
 269             front = nextReferralEx; // trim
 270 
 271             if ((errorEx != null) && (front != null)) {
 272                 front.setNamingException(errorEx); //advance the saved exception
 273             }
 274         }
 275 
 276         // don't append onto itself
 277         if (this == back) {
 278             return front;
 279         }
 280 
 281         if ((back != null) && (! back.hasMoreReferrals())) {
 282             back = back.nextReferralEx; // trim
 283         }
 284 
 285         if (back == null) {
 286             return front;
 287         }
 288 
 289         // Locate the end of the current chain
 290         LdapReferralException ptr = front;
 291         while (ptr.nextReferralEx != null) {
 292             ptr = ptr.nextReferralEx;
 293         }
 294         ptr.nextReferralEx = back; // append
 295 
 296         return front;
 297     }
 298 
 299     /**
 300      * Tests if there are any referrals remaining to be processed.
 301      * If name resolution has already completed then any remaining
 302      * referrals (in the current referral exception) will be ignored.
 303      */
 304     boolean hasMoreReferrals() {
 305         if (debug)
 306             System.out.println("LdapReferralException.hasMoreReferrals");
 307 
 308         return (! foundEntry) && (referralIndex < referralCount);
 309     }
 310 
 311     /**
 312      * Tests if there are any referral exceptions remaining to be processed.
 313      */
 314     boolean hasMoreReferralExceptions() {
 315         if (debug)
 316             System.out.println(
 317                 "LdapReferralException.hasMoreReferralExceptions");
 318 
 319         return (nextReferralEx != null);
 320     }
 321 
 322     /**
 323      * Sets the counter which records the number of hops that result
 324      * from following a sequence of referrals.
 325      */
 326     void setHopCount(int hopCount) {
 327         if (debug)
 328             System.out.println("LdapReferralException.setHopCount");
 329 
 330         this.hopCount = hopCount;
 331     }
 332 
 333     /**
 334      * Sets the flag to indicate that the target name has been resolved.
 335      */
 336     void setNameResolved(boolean resolved) {
 337         if (debug)
 338             System.out.println("LdapReferralException.setNameResolved");
 339 
 340         foundEntry = resolved;
 341     }
 342 
 343     /**
 344      * Sets the exception generated while processing a referral.
 345      * Only the first exception is recorded.
 346      */
 347     void setNamingException(NamingException e) {
 348         if (debug)
 349             System.out.println("LdapReferralException.setNamingException");
 350 
 351         if (errorEx == null) {
 352             e.setRootCause(this); //record the referral exception that caused it
 353             errorEx = e;
 354         }
 355     }
 356 
 357     /**
 358      * Gets the new RDN name.
 359      */
 360     String getNewRdn() {
 361         if (debug)
 362             System.out.println("LdapReferralException.getNewRdn");
 363 
 364         return newRdn;
 365     }
 366 
 367     /**
 368      * Sets the new RDN name so that the rename operation can be completed
 369      * (when a referral is being followed).
 370      */
 371     void setNewRdn(String newRdn) {
 372         if (debug)
 373             System.out.println("LdapReferralException.setNewRdn");
 374 
 375         this.newRdn = newRdn;
 376     }
 377 
 378     /**
 379      * Gets the exception generated while processing a referral.
 380      */
 381     NamingException getNamingException() {
 382         if (debug)
 383             System.out.println("LdapReferralException.getNamingException");
 384 
 385         return errorEx;
 386     }
 387 
 388     /**
 389      * Display the state of each element in a chain of LdapReferralException
 390      * objects.
 391      */
 392     void dump() {
 393 
 394         System.out.println();
 395         System.out.println("LdapReferralException.dump");
 396         LdapReferralException ptr = this;
 397         while (ptr != null) {
 398             ptr.dumpState();
 399             ptr = ptr.nextReferralEx;
 400         }
 401     }
 402 
 403     /**
 404      * Display the state of this LdapReferralException object.
 405      */
 406     private void dumpState() {
 407         System.out.println("LdapReferralException.dumpState");
 408         System.out.println("  hashCode=" + hashCode());
 409         System.out.println("  foundEntry=" + foundEntry);
 410         System.out.println("  skipThisReferral=" + skipThisReferral);
 411         System.out.println("  referralIndex=" + referralIndex);
 412 
 413         if (referrals != null) {
 414             System.out.println("  referrals:");
 415             for (int i = 0; i < referralCount; i++) {
 416                 System.out.println("    [" + i + "] " + referrals.elementAt(i));
 417             }
 418         } else {
 419             System.out.println("  referrals=null");
 420         }
 421 
 422         System.out.println("  errorEx=" + errorEx);
 423 
 424         if (nextReferralEx == null) {
 425             System.out.println("  nextRefEx=null");
 426         } else {
 427             System.out.println("  nextRefEx=" + nextReferralEx.hashCode());
 428         }
 429         System.out.println();
 430     }
 431 }