SWT - Use Text or StyledText as output for a Process

我只是一个虾纸丫 提交于 2019-12-25 06:54:59

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!