| Referrals

## Manually Following Referrals

> If you set the `Context.REFERRAL` environment property to `"throw"`, then each referral encountered results in a `ReferralException`. A `ReferralException` contains referral information--information that describes the referral (such as a list of URLs)--and a referral context--the context to which the referral refers.

> Here are the steps that a program usually follows when manually handling referrals.

> 1. Catch the exception.
> 2. Examine the referral information by using `ReferralException.getReferralInfo()`. For example, ask the user whether the referral should be followed.
> 3. If the referral is to be followed, then get the referral context by using `ReferralException.getReferralContext()` and reinvoke the original context method using the same arguments supplied to the original invocation.
> 4. If the referral is not to be followed, then invoke `ReferralException.skipReferral()`. If this method returns `true` (which means that there are more referrals to be followed), then invoke `ReferralException.getReferralContext()` to continue. When you invoke a context method on the result, it will again throw a `ReferralException` for the next referral to be processed. Return to Step 1 to process it. If the method returns `false`, then there are no more referrals and this procedure can be terminated.

> Here's an example.

> >     // Set the referral property to throw ReferralException
> >     env.put(Context.REFERRAL, "throw");

> >     // Create the initial context
> >     DirContext ctx = new InitialDirContext(env);

> >     // Set the controls for performing a subtree search
> >     SearchControls ctls = new SearchControls();
> >     ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);

> >     // Do this in a loop because you don't know how
> >     // many referrals there will be
> >     for (boolean moreReferrals = true; moreReferrals;) {
> >         try {
> >             // Perform the search
> >             NamingEnumeration answer = ctx.search("", "(objectclass=*)", ctls);

> >             // Print the answer
> >             while (answer.hasMore()) {
> >       	    System.out.println(">>>" + ((SearchResult)answer.next()).getName());
> >             }
> >             // The search completes with no more referrals
> >     	moreReferrals = false;

> >         } catch (ReferralException e) {

> >             if (! followReferral(e.getReferralInfo())) {
> >     	    moreReferrals = e.skipReferral();
> >             }

> >             // Point to the new context
> >     	if (moreReferrals) {
> >                 ctx = (DirContext) e.getReferralContext();
> >     	}
> >         }
> >     }

> For methods that return an enumeration, such as `Context.list()` and `DirContext.search()`, you must place the `try`/`catch` for the `ReferralException` around both the initial invocation and the `while` loop that iterates through the results. When the `ReferralException` is thrown, the existing enumeration becomes invalid and you must reinvoke the original context method to get a new enumeration. Notice also that the outer loop encloses both the method invocation on the context and the iteration of the results.

> #### Authenticating to a Referral Context

> By default, when you invoke `ReferralException.getReferralContext()`, the method uses the original context's environment properties, including its security-related properties, to create a connection to the referred server. Sometimes, upon examining the referral information, you might want to follow the referral by using different authentication information.

> You can do this by using `ReferralException.getReferralContext(env)`:

> >     ...
> >     } catch (ReferralException e) {
> >     ...
> >         env.put(Context.SECURITY_PRINCIPAL, "newuser");
> >         env.put(Context.SECURITY_CREDENTIALS, "newpasswd");
> >         ctx = e.getReferralContext(env);
> >     }

> If the authentication fails, that is, `getReferralContext(env)` throws an exception, then you can reauthenticate by first calling `ReferralException.retryReferral()`and then repeating the `getReferralContext(env)` call with updated environment properties. If you do not want to retry, then invoke `ReferralException.skipReferral()` before calling `getReferralContext(env)`.

> Here is an example.

> >     ...
> >     } catch (ReferralException e) {
> >         if (!ask("Follow referral " + e.getReferralInfo())) {
> >     	moreReferrals = e.skipReferral();
> >         } else {
> >     	// Get credentials for the referral being followed
> >     	getCreds(env);
> >         }

> >         // Do this in a loop in case getReferralContext()
> >         // fails with bad authentication info.
> >         while (moreReferrals) {
> >     	try {
> >     	    ctx = (DirContext)e.getReferralContext(env);
> >     	    break;	// Success: got context
> >     	} catch (AuthenticationException ne) {
> >     	    if (ask("Authentication failed. Retry")) {
> >     		getCreds(env);
> >     		e.retryReferral();
> >     	    } else {
> >     		// Give up and go on to the next referral
> >     		moreReferrals = e.skipReferral();
> >     	    }
> >     	} catch (NamingException ne) {
> >     	    System.out.println("Referral failed: " + ne);
> >     	    // Give up and go on to the next referral
> >     	    moreReferrals = e.skipReferral();
> >     	}
> >         }
> >     }

> In this example, the `e.getReferralContext(env)` call is placed inside of a loop so that if the call fails, it can be retried by using different credentials. The example defines a local method, `getCreds()`, for getting the principal name and credentials from Standard Input to update the environment properties, `env`, that are being used to get the referral context. When `e.getReferralContext(env)` fails, the user of the application can either choose to retry by using different credentials or skip the bad referral.

> #### Passing Request Controls to a Referral Context

> See the Controls and Extensions lesson for details on how to set and change request controls of a referral context.

| Referrals