问题
I have a class that extends OutputStream and is used to make the output of said OutputStream into a Text or StyledText. The class works great, however it introduces a problem.
I am working on an SWT application that includes a fake console. I'm not using it to run sytem commands, but rather a few different Minecraft servers that I use for slightly different development environments/purposes.
From what I've read about ProcessBuilder, inheritIO() is supposed to make the resulting Process' IO streams the same as the Java process that created it:
From the Oracle Docs:
public ProcessBuilder inheritIO() Sets the source and destination for subprocess standard I/O to be the same as those of the current Java process.
This is a convenience method. An invocation of the form
pb.inheritIO()
behaves in exactly the same way as the invocation
pb.redirectInput(Redirect.INHERIT) .redirectOutput(Redirect.INHERIT) .redirectError(Redirect.INHERIT)
What I've tried to do is redirect System.out
(via System.setOut()
), and then create the process:
private static Process start(PrintStream out, Server server, String version) {
try {
System.setOut(out);
return new ProcessBuilder("java", "-jar", version + ".jar").inheritIO().directory(server.getDirectory()).start();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
If you are unfamilair with a Minecraft server structure, it looks a bit like this:
root/
├--plugins/ (if using Bukkit, as in my case)
├-- PluginName/
└-- PluginName.jar
└-- minecraft-server.jar (or some version of craftbukkit, in my case)
I typically have more than one version of the server software within the server's root directory, in case I need to use a specific version. (That's my reasoning behind the variables in the method)
The Process runs fine; I can see the output. But the output is in the wrong place. It's almost as if my System.setOut()
call was ignored, because I still see the output in the console, rather than in the GUI textbox. If I do a System.out.println
call, it outputs as I expect it to: on the GUI. So I am unsure how the 'inheriting' of the IO streams work.
Is there a way to listen/redirect/pipe it so I can print it to the GUI?
回答1:
The I/O redirection of ProcessBuilder
does not redirect streams but file descriptors. Even setting System.out
to another stream does not change the file descriptor of the initial standard output. ProcessBuilder.inheritIO()
inherits the parent process' standard output/input/error file descriptors.
The PrintStream
instance is not passed to the started operating system process, but rather something like (it is pseudo code, I do not think it actually works):
((FileOutputStream) System.out.out).getFD()
Or, more correct:
FileDescriptor.out
which is static final
indicating that it will not change, even if you set System.out
to something different.
So if you want to have your child process's output appear in the fake console you mentioned, you have to use the StreamGobbler proposed here or find a different way of starting your child process (not via the operating system but maybe by dynamically loading your JAR at runtime).
回答2:
Here's what I decided on doing
It's not really a workaround, but I've decided on the following process for running each server:
- Choose server version
Load the required jar into the classpath:
try { String path = "file:///path/to/jar.jar"; URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); Class<?> sysclass = URLClassLoader.class; Class<?>[] parameters = new Class<?>[]{URL.class}; Method method = sysclass.getDeclaredMethod("addURL", parameters); method.setAccessible(true); method.invoke(sysLoader, new URL(path)); sysLoader.close(); } catch (Exception e) { e.printStackTrace(); }
- When the server stops running, only allow that server to be run again
- If another server is desired, an application restart is required
The last step is to ensure needless class names aren't added. I say this because Bukkit tends to have the following structure for its classes:
org.bukkit/
├--craftbukkit.{SERVER_VERSION}/
├--(api classes and packages, most of which are Interfaces, the implementations are in the craftbukkit package)
I'm not really fond of the structure, and I'm not really fond of needing an app restart, but I doubt that specific situation will arise that often.
来源:https://stackoverflow.com/questions/32342477/swt-use-text-or-styledtext-as-output-for-a-process