Yet another round of refactoring. This time around was able to remove one of the interfaces for callback behavior which simplified things a little bit.

I've also adopted a pattern by which we have "controller" objects that manage the state around a request/response/retry/etc cycle. Sucks a bit to have mutable state but I haven't been able to find a way around it and at least with the controllers, the state is managed by a single class that is tied to an understanding of how the request is managed.

The RequestDetailProvider interface was supposed to help in making the RequestSender have more responsibility and be the point of understanding how to get a target location based upon if the caller says they are retrying a request or just launching it normally. However, the retry semantics with multi actions and scanner behavior are making the abstraction look inadequate so it's likely on the way out but for now, it holds place.
The RequestDetailProvider functionality was really only applicable to the single request controller so changed things so that's the only place that it is used rather than use it everywhere and have edge cases where some of the contract did not apply.
Clean up the multi action stuff some by moving to just the single controller for the actions taken by a multi operation and having the parsing be a class that only deals with parsing.
More refactoring to cleanup the new reliance on the controller stuff for each type of action. Moved the creation of the controller (via static method call still) into "initiator" classes which then hides the static method calls from places like the client itself. The initiator classes are pretty closely tied to the actual controller classes but I'm pretty comfortable with it since the intiator classes are mostly just factories to create instances of the controllers and star the request processing going.
Started thinking a little bit about what happens in the case where the client locally has some sort of error sending/reading a request/response or something. Adding a separate error condition callback method for communication errors since I believe they will need to be retried and tried to add TODOs around the code for places where I think we'll need to deal with such errors somehow.
Think I finally came up with a scheme for retries of scanner operations when we get a retriable failure on a "next" call. With a little bit of refactoring, managed to change the "next" call to make use of the existing single action controller rather than having a controller specific to the method call which helped to reduce some complexity and allowed reuse of the retry mechanisms already in place there.

Note, really need to go through and evaluate if I'm properly dealing with exceptions on retries and that a write on a retry that fails gets caught propagated correctly.

Also, in testing, discovered that, as I had anticipated, if an exception is caught in the netty pipeline, that is not getting propagated to the future. Still need to figure out a way to keep track fo the requests tied to a channel so that if we close one, we force outstanding requests to be retried but it's not actually that easy because if the exception is a bug in one request - which is what I had in testing - that shouldn't cause continued failure/retries of other requests on that channel. That is, an exception like that ideally wouldn't result in closing the channel, but I'm not sure if we'll be able to distinguish...