The basic approach is to:
- create the classloader,
- read the manifest to find the entry point class
- load the entry point class,
- use reflection to get its
public static void main(String[])
method, and
- call the
main
method from a new thread, passing the command line arguments for the command.
The problem in this particular case is going to be in setting up the System.in and System.out streams so that you can talk to the "child application" running in the same JVM. The problem is that the "child application" expects System.out to be the stream that it writes it output to and that the "parent application" will read from. But the "parent application" expects System.out to be the output stream for the console (or whatever). And that won't work, because System.in/out/err are class fields that inevitably have the same value for both "applications".
The solution is to change the code of the parent application classes to not use the System.in/out/err fields. Before the "parent" calls the main
method on the child, it must use System.setOut(...)
to set it to a custom output stream instance that will write to a buffer. Then you need a matching input stream instance that the "parent" can read from ... in place of bis
in your code.
Another thing you need to address is the possibility that the "child" application will call System.exit()
. That will have the unfortunate effect of pulling the plug on the parent.
And there are all sorts of other complexities, like cleaning up threads and other stuff created by the "child". Some of these are basically unsolvable unless your JVM implements the Isolates JSR (which no mainstream JVMs do, AFAIK).
In short, doing this in the general case is really hard, and there are some things that you simply cannot get completely right. Unless you can eliminate / avoid these problems, it is much simpler to implement an alternative entrypoint, and alternative ways of communicating.