Question

So we want to use the bog-standard keytool utility that ships with a JRE. But rather than going through the trouble of finding the correct path and executable extension, spawning a subprocess, and running the executable, we collectively had the bright idea ("remember, none of us is as dumb as all of us!") to just call KeyTool's main() directly. It's implemented in Java code and also shipped with the JRE, and contains the standard "classpath" exception to the GPL so we can link against it.

Looking at the KeyTool source, there's even some provision made for this sort of thing: there are comments like "if you're calling KeyTool.main() directly in your own Java program, then [helpful reminder]" and the top-level main() is capable of propagating exceptions to calling code instead of just dying with System.exit(). Being able to just build the same command-line argument array and run KeyTool.main(stuff) instead of having to mess with platform differences seems like a very Java-esque thing to do, right?

In practice, weird things happen and we don't know why.

We want to capture any output from running KeyTool, which starts off like this:

// jdk/src/share/classes/sun/security/tools/KeyTool.java, line 331:
public static void main(String[] args) throws Exception {
    KeyTool kt = new KeyTool();
    kt.run(args, System.out);
}

private void run(String[] args, PrintStream out) throws Exception {
    // real code here, sends to 'out'
}

The KeyTool entry points don't allow us to pass a PrintStream, it's hardcoded to use System.out. That should be okay thanks to System.setOut. We have an OutputStream subclass which feeds to a JTextComponent, but for initial coding, redirecting to a text file is fine. So our code does

PrintStream orig = System.out;
try {
    System.out.println("This is the last visible console line");
    System.setOut(new PrintStream("redirect_test.txt"));
    System.out.println("This is now redirected!");
    KeyTool.main(keytool_argv);  // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
    System.setOut(orig);
    System.out.println("Back to normal console output");
}

But when we run the code, the redirect_test.txt file contains only "This is now redirected!". The output from keytool's "-help" still shows up on the console, along with the before-and-after println calls.

There are some other oddities in calling KeyTool directly, like the package and class name has changed between Java 7 and Java 8, but that's easy to deal with via reflection. (The comments in the KeyTool source in Java 8 still refer to the Java 7 name, heh.) The only thing just freaky weird is how its "System.out" is strangely not affected by the same redirection that works everywhere else. (No, there are no weird import statements bringing in a special System replacement.)

Here's an online copy of Java 7's KeyTool.java if you don't happen to have OpenJDK sitting around.

Was it helpful?

Solution

You just need to redirect both System.out and System.err, since the usage instructions get printed to the standard error stream instead of the standard output stream. Try this:

PrintStream original = System.out;
PrintStream redirected = new PrintStream("redirect_test.txt")
try {
    System.out.println("This is the last visible console line");
    System.setOut(redirected);
    System.setErr(redirected);
    System.out.println("This is now redirected!");
    KeyTool.main(keytool_argv);  // "-help" and "-debug" for now
}
catch all the myriad ways things might go wrong { ... }
finally {
    System.setOut(original);
    System.setErr(original);
    System.out.println("Back to normal console output");
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top