Previous | Next | Trail Map | Tips for LDAP Users | Referrals

Manually Following Referrals

If you set the Context.REFERRAL(in the API reference documentation) environment property to "throw", then each referral encountered results in a ReferralException(in the API reference documentation). 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()(in the API reference documentation). 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()(in the API reference documentation) 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()(in the API reference documentation). 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()(in the API reference documentation) and DirContext.search()(in the API reference documentation), 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)(in the API reference documentation):

...
} 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()(in the API reference documentation)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 (in the Tips for LDAP Users trail) lesson for details on how to set and change request controls of a referral context.


Previous | Next | Trail Map | Tips for LDAP Users | Referrals