tags:

views:

366

answers:

1

Hello everyone.

Groovy has a nice process execution engine and I'd like to use it.
There is a method consumeProcessOutput that does all the necessary work. But what I want is to inject my additional functionality every time when consumeProcessOutput call append or something on the out instance.

public class ProcessExecutor {
   static public void execute(IOutput output, String processName) {
      def process = processName.execute()

      def out = new StringBuffer()
      process.consumeProcessOutput(out, out)

      process.waitFor()
   }
}

I am aware of

use (MyStringBufferIntercept) {
   process.consumeProcessOutput(out, out)
}

But they seems work only for current thread and consumeProcessOutput creates additional threads.

So is there any solution to listen to lines adding and call output.addLine(line) and output.addErrorLine(line)?

So far I have only one solution which doesn't work good when error output goes along with normal output quite fast: order changes.

  def process = processName.execute()

  def inThread = Thread.start {
     process.in.eachLine{ line -> output.addLine(line)}
  }

  def errThread = Thread.start {
     process.err.eachLine{ line -> output.addErrorLine(line)}
  }

  inThread.join()
  errThread.join()

  process.waitFor()

I will agree with a Java solution. But I tend to think that groovy's one has to be more elegant.

+1  A: 

I don't like the convention Groovy uses of reading from process.in and process.err and writing to process.out. The Javadocs should be more clear on it. It would seem that err and out would be two of a kind and in the odd one out.

Since you are multiplexing the process' stdout and stderr into your single output string, you are right that individual lines may get garbled. I do not believe you can use the synchronized keyword with closures, so perhaps do something similar to the code below but use a locking mechanism or perhaps wrap the outline closure in synchronized method.

class P {
 static public void execute( String processName) {
  def process = processName.execute()
  def output = new StringBuffer()

  def outline = { line -> output += line + '\n' }

 def inThread = Thread.start {
         process.in.eachLine{ outline it }
   }

 def errThread = Thread.start {
          process.err.eachLine{ outline it }
   }

       inThread.join()
       errThread.join()
       process.waitFor()
       println output
    }
 }

 def p = new P();
 p.execute("ls -l");
Sean A.O. Harney
The problem is with this solution lines are not going in the right order. If you execute some script process where error line come after normal line like a zebra, custom solution will print not the same result as consumeProcessOutput.
Mykola Golubyev