remkop / picocli

Picocli is a modern framework for building powerful, user-friendly, GraalVM-enabled command line apps with ease. It supports colors, autocompletion, subcommands, and more. In 1 source file so apps can include as source & avoid adding a dependency. Written in Java, usable from Groovy, Kotlin, Scala, etc.

Home Page:https://picocli.info

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

If I close a OutputStreamWriter(System.out) in a subcommand, a second one will not produce output on stdout

lmmodesto opened this issue · comments

First of all, thank you for this nice and helpful project!!

I've been doing some tests with Apache PDFBox v3.0.0 source code to see if a simple modification could make it possible run repeatable subcommands.

--- tools/src/main/java/org/apache/pdfbox/tools/PDFBox.java     (revisão 1911871)
+++ tools/src/main/java/org/apache/pdfbox/tools/PDFBox.java     (cópia de trabalho)
@@ -30,6 +30,7 @@
  * Used as the main class in the runnable standalone PDFBox jar.
  */
 @Command(name="pdfbox",
+    subcommandsRepeatable = true,
     customSynopsis = "pdfbox [COMMAND] [OPTIONS]",
     footer = {
         "See 'pdfbox help <command>' to read about a specific subcommand"

Especially for the ExtractText tool to allow me processing more than one PDF file a time.

ExtractText has the -console and -o outfile options to direct the output of text extracted from a PDF file.

Using -console , OutputStreamWriter(System.out) is created.

Using -o <outfile> , OutputStreamWriter(FileOutputStream(<outfile>)) is created.

...
    // Expected for CLI app to write to System.out/System.err
    @SuppressWarnings("squid:S106")
    private static final PrintStream SYSOUT = System.out;
...
        try (PDDocument document = Loader.loadPDF(infile, password);
             Writer output = toConsole ? new OutputStreamWriter( SYSOUT, encoding ) : new OutputStreamWriter( new FileOutputStream( outfile ), encoding ))
        {
            long startTime = startProcessing("Loading PDF " + infile);

The second subcommand doesn't produce any output if the first one is -console as shown bellow:

$ java  -jar ./3.0.0/app/target/pdfbox-app-3.0.0.jar export:text -console -i ./3.0.0/pdfbox/src/test/resources/input/hello3.pdf export:text -console -i ./3.0.0/pdfbox/src/test/resources/input/hello3.pdf
Hello محمد World. 
$ java  -jar ./3.0.0/app/target/pdfbox-app-3.0.0.jar export:text -console -i ./3.0.0/pdfbox/src/test/resources/input/hello3.pdf export:text -o /dev/stdout -i ./3.0.0/pdfbox/src/test/resources/input/hello3.pdf
Hello محمد World. 
$ java  -jar ./3.0.0/app/target/pdfbox-app-3.0.0.jar export:text -o /dev/stdout -i ./3.0.0/pdfbox/src/test/resources/input/hello3.pdf export:text -o /dev/stdout -i ./3.0.0/pdfbox/src/test/resources/input/hello3.pdf
Hello محمد World. 
Hello محمد World. 

Could you help me to understand if this is a expected behaviour? I don't know Java very deeply...

I am attaching a simple application that reproduces on a smaller scale the use of the OutputStreamWriter that PDFBox does when -console is used.

You can try some like this:

$ java  -jar ./target/picocli-hello-1.0-SNAPSHOT.jar helloworld -callOpt 2 helloworld -callOpt 1
Hello world!
$ java  -jar ./target/picocli-hello-1.0-SNAPSHOT.jar helloworld -callOpt 1 helloworld -callOpt 2
Hello world!
Hello world!
$ java  -jar ./target/picocli-hello-1.0-SNAPSHOT.jar helloworld -callOpt 2 helloworldcopy -callOpt 1
Hello world!

Thank you!

picocli-hello.zip

Yes, seems like the expected behavior.

With this code I can no longer use System.out

        try (Writer w = new OutputStreamWriter(System.out,"UTF-8"))
        {
            w.write("Hello");
            w.flush();
        }
        catch (IOException e)
        {
            System.err.println( "Error [" + e.getClass().getSimpleName() + "]: " + e.getMessage());
            return 4;
        }
 
        try (Writer w = new OutputStreamWriter(System.out,"UTF-8"))
        {
            w.write(" world!");
            w.write(System.getProperty("line.separator"));
            w.flush();
        }
        catch (IOException e)
        {
            System.err.println( "Error [" + e.getClass().getSimpleName() + "]: " + e.getMessage());
            return 4;
        }

I'm sorry. You can ignore this.

Thank you!

It's not a issue

Glad you found a solution!