Wayne said:
[...]
All you need to do to start a Java program is invoke the static main()
method of its main class. You can do that from any Java code:
void startExternalApp(String[] args) {
ExternalApp.main(args);
}
ExternalApp is imported somehow?
In that example, yes. The external app's jar would need to be in the
launcher's classpath, and you would need to import the
package.name.ExternalApp class into the launcher. Do note that that is
a compile-time name resolution thing: it will end up putting the
fully-qualified name of the ExternalApp class into your launcher class,
but it does not bring in any of its code.
Note also that ClassLoader-based solution is completely different in
this regard: in that case you want to _avoid_ having the external app's
classes accessible via your launcher app's own ClassLoader; they should
only be accessible via the ClassLoader(s) that you configure for the
external app.
If you want to make the external app use its own ClassLoader (so as to
segregate its classes from your launcher app's own, and also from those of
any other instance your launcher starts, then you would need to get a bit
fancier. You would instantiate the ClassLoader (probably an instance of
URLClassLoader; make sure it points to a suitable parent ClassLoader) use
it to load the correct Class, then look up the correct main() Method in
the Class object and invoke it. The Class and Method classes are part of
the Java "Reflection" API -- you should generally avoid them unless you
need them, but this is a case where you would need them.
You can take one more step and assign the external app to its own thread.
The easiest way to do this would be something like:
Thread startExternalAppInThread(final String[] args) {
ClassLoader cl = new URLClassLoader(/* your args here*/);
Class appClass = cl.findClass("fully.qualified.name");
final Method mainMethod =
appClass.getMethod("main", new Class[] {args.getClass()});
return new Thread() {
public void run() {
mainMethod.invoke(null, new Object[] {args});
}
}
}
It would appear the idea of ClassLoading is a good one (still lots of docs
to read to find out how to make my situation fit), however my primary
questions are:
The external application is a console based java app, and is started from
this command line:
"java -cp C:/xxxxxx/lib/xxxxxxxxxx23.jar com.xxxxxxxx.remote.xxx XXX" (names
changed to protect the innocent). Does this still fit the ClassLoader
model?
Java uses a ClassLoader to load classes no matter what. In many cases
you don't have to do anything special about it to get what you want;
Java sets up a suitable ClassLoader for you and uses it to load your
classes. We're discussing using ClassLoaders to achieve somewhat
fancier effects that would require explicit use of a ClassLoader in your
code. The short answer is that every working Java application
necessarily fits the ClassLoader model because that's the _only_
Java-compliant way to run it.
Can I still perform the capture and redirection of the relevant i/o streams
in order to control the application?
Yes (see java.lang.System.setIn(), etc.). However, a JVM has only one
standard input stream, one standard output stream, and one standard
error stream, so if that's the only way to talk to the application then
your own application must forgo using those streams while the external
app is using them. (And you wouldn't be able to simultaneously run
multiple instances of the external app.)
If so, would how would the following code change to reflect it?
(ErrorStreamReader,StreamReader and StreamWriter are all threads for the
relevant streams).
Keep in mind that bringing the external app into your own app, you
reverse the sense of its streams. I'm guessing a bit on your
StreamReader, etc. classes, but here goes:
============================================
// any error message?
error = new ErrorStreamReader(proc.getErrorStream(), "ERROR", running); //
errorOut = new PipedOutputStream();
errorOutSink = new PipedInputStream(errorOut);
System.setErr(new PrintStream(errorOut));
error = new ErrorStreamReader(errorOutSink, "ERROR", running);
this should be picked up by the log reader
// any output?
pin = new PipedInputStream(); // open input pipe
pout = new PipedOutputStream(pin); // open output pipe and bind to input
pipe
dis = new DataInputStream(pin);
reader = new StreamReader(proc.getInputStream(), pout , "OUTPUT", running);
procOut = new PipedOutputStream();
procOutSink = new PipedInputStream(procOut);
System.setOut(new PrintStream(procOut));
dis = new DataInputStream(procOutSink);
/*
Then read the external app's output via "dis".
Comment: Do you really need a DataInputStream here? Is the external app
really writing raw Java primitives?
*/
// capture output stream, and send to pout
// prepare writer
writer = new StreamWriter(proc.getOutputStream(),running);
I'm not certain of the details of StreamWriter's operation, but you
probably want to do something based on this:
procIn = new PipedInputStream();
procInSource = new PipedOutputStream(procIn);
System.setIn(procIn);
/*
(Then write to procInSource to send data to the external app's standard
input.)
*/
==========================================
Note that in that example it may not be safe to write to "procIn" and
read from "dis" in the same thread. That is not materially different
from the situation in your own code, however, where it is not safe to
write to "writer" and read from "dis" in the same thread. Even though
you are using separate StreamWriters and StreamReaders, you are sending
the external proc's output (read from the Process' InputStream) through
a pipe, which can block if the pipe's internal buffer fills. The whole
StreamReader bit is therefore kind of pointless for that part -- it
introduces an additional thread that doesn't materially benefit you.
The thread that is handling each stream needs to do the appropriate
thing with it, more or less on its own. It must not be possible for any
of them to block on action by any of the others.
Is there not a 3rd party class that perform this activity for me as there
seems to be a lot to learn and not a lot of time to learn it in? Perhaps
the hurry that I am in is half my problem ..
The ClassLoading bit? I wouldn't be surprised to hear that there is
one, but I'm not specifically aware of any. An external process
manager? I am fairly certain that you could find such a thing, perhaps
even for free, but again, I don't have a link to give you.
John Bollinger
(e-mail address removed)