Previous | Next | Trail Map | Building a Service Provider | 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(in the API reference documentation)? 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(in the API reference documentation) interface on a federation consisting of five naming systems, only the first and last of which support DirContext.

Resolver

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()(in the API reference documentation) 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(in the API reference documentation) interface. This interface contains two overloaded forms of resolveToClass()(in the API reference documentation): one form accepts a string name and a Class and the other accepts a Name(in the API reference documentation) 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(in the API reference documentation). If you get a CannotProceedException(in the API reference documentation), 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.

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());
    }
}


Previous | Next | Trail Map | Building a Service Provider | Adding Federation Support