As mentioned in comments, Java doesn't support requested feature of getting all subclasses at runtime viaa reflection.
But it should be possible to inspect all classes in a project at it's compilation time, and generate a jaxb.index in your jar file.
An example (not complete, thus not directly working, but to demonstrate the idea) of such annotation processor can look like this:
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class JaxbProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
// Java 8 compiler plugin API to hook on compilation of every single class.
JavacTask.instance(env).addTaskListener(new TaskListener() {
// Prepare the writer
PrintWriter writer = new PrintWriter(env.getFiler().createResource(/* Details of output jaxb.index file */).openWriter());
Set<TypeElement> jaxbParents = new HashSet<>();
@Override public void started(TaskEvent taskEvent) {
// Nothing needs to be done here.
}
@Override public void finished(TaskEvent taskEvent) {
if(taskEvent.getKind() == ANALYZE) {
// This is where the compiler invokes our code.
// Side effect of this inspection is to collect all classes, that should be included in our jaxb.index
// into the jaxbParents set.
inspect(taskEvent.getTypeElement());
// Now simply write it to the file (output details to be provided).
// We should actually only write down difference from previous invocation. Let me fix it later.
jaxbParents.forEach(writer::println);
}
}
private void inspect(TypeElement type) {
// First inspect current class element
testForJaxbParent(type);
// Do not forget to inspect also inner classes.
type.getEnclosedElements().stream().filter(TypeElement.class::isInstance).map(TypeElement.class::cast).forEach(this::testForJaxbParent);
}
/**
* Test if the type should be added to JAXB index file.
*/
private boolean testForJaxbParent(TypeElement type) {
if(jaxbParents.contains(type)) {
// It's already in the set, so no need to bother with it.
return true;
}
if(type.getAnnotation(JaxbRoot.class) != null || testForJaxbParent((TypeElement) env.getTypeUtils().asElement(type.getSuperclass()))) {
// If our type is annotated with our special "extension" to JAXB - JaxbRoot, it means, that it is our
// root class, that needs to be added to the jaxb.index.
//
// If it is not annotated, then still test hierarchy of superclasses recursively, and if there is any
// superclass being the root, then add it including all children on the stack at the return from the
// recursion.
return jaxbParents.add(type);
}
return false;
}
});
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// No real annotation processing needed.
return false;
}
}
Once you have the annotation JaxbRoot
and this processor in a jar, and ideally also the service descriptor to let Java automatically find this processor in the jar, then simply add the jar to your classpath, annotate only your root class, and you'll get generated jaxb.index with all it's subclasses.
And even if you have your project split into multiple jars, and have your root class in one, and children in another, still the processor get's invoked and generates the index file per jar. Then you'll just have to merge them all together, which can be just one utility class delivered together with the processor.