Regarding sharing the map, I think you should use a prototype pattern that creates a new Processor from an existing one maybe via clone()? In the InvocationDispatcher case we should maybe use a concurrent hash map and share the map across instances for the same dispatcher class.
I don't quite follow you. Currently DispatchingQueryProcessor caches InvocationDispatchers by concrete impl type. So we only create a new InvocationDispatcher when we have a new implementation (which means InvocationDispatchers are shared between segments, searches, everything). In that regard DispatchingQueryProcessor#dispatcherByClass should be a ConcurrentHashMap. But otherwise, I think we're okay?
The process implementation in DefaultQueryProcessor executes query.rewrite before passing the query to the dispatcher which is no good since some QueryProcessor impls might not want to rewrite that query at all. In
LUCENE-2868 karl tries to find a way to prevent lucene to rewrite one and the same FuzzyQuery since he has them in multiple clauses somewhere down the BQ tree. This is a super expensive operation in his case to rewriting it only once makes sense. I think this should be left to the actual implementation.
This is super tricky. The question is how to define a base case in #process(Query). Lets assume DefaultQueryProcessor#process(Query) just dispatched immediately. It might be a receiver of the same dispatch (perhaps the query is a TermQuery and no #process(TermQuery) is provided, so #process(Query) is chosen). It then just dispatches again, receives again.. and we're in a loop.
Any thoughts on how to avoid that?