| Adding Federation Support

## Resolving Through Subinterfaces

> The procedures described so far work fine when all of the context implementations involved in completing a method through a federation implement the same interfaces. What happens if some intermediate context implementations do not support all of the subinterfaces of `Context`? You do not want to require that an intermediate naming system support all subinterfaces in order for federation to work. This issue is more important for nonstandard subinterfaces because expecting wide support for those is unreasonable.

> Suppose that you invoke a method in the `DirContext` interface on a federation consisting of five naming systems, only the first and last of which support `DirContext`.

> For the method to be invoked successfully on the target context, the intermediate naming systems must be involved in the resolution phase of the operation and be able to indicate which operation to ultimately invoke. At first glance, achieving the resolution step by using `lookup()` might seem possible. This is problematic because you want the target context and not the named object. For example, if you are creating a subcontext, then you want the penultimate context named by the input name and not the named object.

> The JNDI supports resolution through intermediate contexts by requiring that intermediate context implementations support the `Resolver` interface. This interface contains two overloaded forms of `resolveToClass()`: one form accepts a string name and a `Class` and the other accepts a `Name` and a `Class`. The JNDI uses `resolveToClass()` to partially resolve the input name, stopping at the first context that is an instance of the specified interface/class. A context implementation needs to implement the `Resolver` interface and provide implementations for this method.

> Here is a sample implementation.

> >     public ResolveResult resolveToClass(Name name, Class ctxType)
> >         throws NamingException {

> >         // If you're it, you quit right now
> >         if (ctxType.isInstance(this)) {
> >     	return new ResolveResult(this, name);
> >         }

> >         try {
> >     	Name[] nm = parseComponents(name);
> >     	Name mine = nm[0];
> >     	Name rest = nm[1];
> >     	Object nxt = null;

> >     	if (rest == null || rest.isEmpty()) {
> >     	    // Terminal; just use head
> >     	    nxt = lookup_internal(mine);
> >     	} else if (rest.get(0).equals("") && rest.size() == 1) {
> >     	    // Terminal nns
> >     	    nxt = lookup_nns(mine);
> >     	} else if (mine.isEmpty() || isAllEmpty(rest)) {
> >     	    // Intermediate; resolve current components as intermediate
> >     	    Object obj = lookup_nns(mine);

> >     	    // Skip leading forward slash
> >     	    throw fillInCPE(obj, mine, rest.getSuffix(1));
> >     	} else {
> >     	    // Intermediate; resolve current components as intermediate
> >     	    Object obj = resolveIntermediate_nns(mine, rest);

> >     	    throw fillInCPE(obj, mine, rest);
> >     	}

> >     	if (ctxType.isInstance(nxt)) {
> >     	    return new ResolveResult(nxt, "");
> >     	} else {
> >     	    // Have resolved the entire composite name but
> >     	    // cannot find the requested context type
> >     	    throw new NotContextException(
> >     		"Not instanceof " + ctxType);
> >     	}
> >         } catch (CannotProceedException e) {
> >     	Context cctx = NamingManager.getContinuationContext(e);
> >     	if (cctx instanceof Resolver) {
> >     	    return cctx.resolveToClass(e.getRemainingName(), ctxType);
> >     	} else {
> >     	    // Have hit a nonResolver; give up
> >     	    e.fillInStackTrace();
> >     	    throw e;
> >             }
> >         }
> >     }

> In this method, you first check whether the current context implements the requested type. If so, you need to go no further. Otherwise, you proceed as with other context operations described in an earlier section of this lesson. That is, you determine whether the current context is the terminal, terminal nns, or intermediate context. For the two terminal cases, if the answer is an instance of the requested type, then you return the answer and an empty remaining name in a `ResolveResult`. If you get a `CannotProceedException`, then use it to find a continuation context. This context must implement the `Resolver` interface. Otherwise, you can't use it to find the target context.

> #### Continuation Contexts for Subinterfaces

> The following JNDI methods use `resolveToClass()` to find the continuation context of the appropriate type.

> - `NamingManager.getContinuationContext()`
> - `DirectoryManager.getContinuationDirContext()`
> If your context implementation supports another subinterface, then it should define a similar method for that subinterface. For example, suppose that you need to support the `BarContext` subinterface. Then you would define a method called `getContinuationBarContext()`. Here is an example.

> >     public BarContext getContinuationBarContext(CannotProceedException cpe) {
> >         return new ContinuationBarCtx(cpe);
> >     }

> You then define an implementation for the continuation context, `ContinuationBarCtx`. This class must implement both `Resolver` and the subinterface `BarContext`. The following utility methods are defined for getting the target context for `Context` and `BarContext` methods.

> >     // Gets the default target context, and caches it
> >     protected Context getTargetContext() throws NamingException {
> >         if (cctx == null) {
> >     	cctx = NamingManager.getContinuationContext(cpe);
> >         }
> >         return cctx;
> >     }

> >     protected ResolveResult getTargetContext(Name name)
> >         throws NamingException {

> >         Context ctx = getTargetContext();

> >         if (ctx instanceof BarContext)
> >     	return new ResolveResult(ctx, name);

> >         // Have found the resolver; ask it to find BarContext
> >         if (ctx instanceof Resolver) {
> >     	Resolver res = (Resolver)ctx;
> >     	return res.resolveToClass(name, BarContext.class);
> >         }

> >         // Resolve all of the way by using lookup()
> >         // This may allow the operation
> >         // to succeed if it doesn't require the penultimate context
> >         Object ultimate = ctx.lookup(name);
> >         if (ultimate instanceof BarContext) {
> >     	return (new ResultResult(ultimate, new CompositeName()));
> >         }

> >         throw (NamingException)cpe.fillInStackTrace();
> >     }

> The `BarContext` methods can then be implemented as follows in the continuation context.

> >     public void BarMethod(Name name) throws NamingException {
> >         ResolveResult res = getTargetContext(name);
> >         return ((BarContext)res.getResolvedObj()).barMethod(res.getRemainingName());
> >     }

> The method uses the resolved `BarContext` and the remaining name to continue the operation.

> All of the `Context` methods are implemented by using a `getTargetContext()` method that accepts no argument. These implementations exist to satisfy the `Context` interface. Typically, the context implementation would use `NamingManager.getContinuationContext()` directly instead the subinterface's continuation context for methods in the `Context` interface, as shown in the previous section of this lesson.

> Here is a sample implementation of `lookup()` in the continuation context.

> >     public Object lookup(Name name) throws NamingException {
> >         Context ctx = getTargetContext();
> >         return ctx.lookup(name);
> >     }

> A context implementation that implements the subinterface would use `getContinuationBarContext()` in the same way that it uses `NamingManager.getContinuationContext()`. Here is an example.

> >     public Object barMethod(Name name) throws NamingException {
> >        try {
> >            ...
> >         } catch (CannotProceedException e) {
> >       	BarContext cctx = ContinuationBarCtx.getContinuationBarContext(e);
> >     	return cctx.barMethod(e.getRemainingName());
> >         }
> >     }

| Adding Federation Support