I want to select different implementations of classes dynamically, based on a runtime condition. Let's say I have a class with fully qualified class name C. My running system may have many definitions of class C, each of which is in its own jar. I have a runtime condition (held in a ThreadLocal) that tells which definition should be chosen.
I was asked in a comment to clarify the original requirement, so I will clarify the requirement as best I can. There are multiple teams writing software to contribute to this system - something like 4000 classes in many independent modules. What's more they can change over time. They are currently running in separate JVMs so there is no issue with classes overlapping. Now we are considering running them in the same JVM with multiple releases running simultaneously on the same JVM; which specific set of implementations used being differentiated by the ThreadLocal. So the original problem was how to allow a thread to at one time run one set of implementations and at another time run another.
I have a tomcat application that is currently using OpenJDK 8.
I believe I can write a custom ClassLoader that manipulates the classpath to choose the definition of C differently based on the ThreadLocal. But I'm afraid the results will be cached somewhere such as JVM Code Cache. Unless I can override that behavior too, the next time the class is needed, the runtime condition may have changed and the version in the cache would be wrong.
Is there any way to do what I need to do?
Well, the simple solution would be to NOT have multiple definitions of class C, but instead have 'class C1', 'class C2', etc (i.e. they don't overlap and can be simultaneously loaded) and then your runtime property just picks the right one as appropriate. That is way-way the easiest solution, so strongly consider it first. But it may not meet your needs.
If you truly need to have multiple separate implementations of a single 'class C', then what you are effectively talking about is a 'hot swap' scenario. Fortunately, Tomcat and other tools got good (with limitations) at hotswapping long-time-back. You likely already know this, but 'hot swapping' meets the need of a developer who codes 'class C', deploys it to a container, tries it out, realizes it has a glitch, makes a quick code edit and wants to run the modified code without relaunching the container. Hotswapping does this by basically overlaying the new implementation in the JVM. It only works up to a point because each 'redeploy' pollutes the 'class space' of the JVM and you eventually run of of 'class space memory' and/or the JVM starts going unstable. Depending on your needs and tolerances though, hotswapping might work.