All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.loveoneanother.schale.Proc.scala Maven / Gradle / Ivy

The newest version!
package at.loveoneanother.schale

import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter

import scala.util.control.Breaks.break
import scala.util.control.Breaks.breakable

import akka.actor.ActorDSL.Act
import akka.actor.ActorDSL.actor
import akka.actor.ActorRef
import akka.actor.actorRef2Scala
/**
 * An operating system process.
 */
class Proc(args: String*)(env: Env) extends Traversable[String] {
  protected val pb = new ProcessBuilder(args: _*)
  protected var proc: Process = null
  protected var inputWriter: BufferedWriter = null
  protected var outputReader: BufferedReader = null
  protected var errorReader: BufferedReader = null

  /**
   * Start process and traverse lines in standard output.
   */
  def stdout = new Traversable[String] {
    def foreach[U](fun: String => U) {
      startProc()
      collectOutput(fun, outputReader)
    }
  }

  /**
   * Start process and traverse lines in standard error.
   */
  def stderr = new Traversable[String] {
    def foreach[U](fun: String => U) {
      startProc()
      collectOutput(fun, errorReader)
    }
  }

  /**
   * Start process and traverse lines in both standard output and error.
   */
  def foreach[U](fun: String => U) {
    pb.redirectErrorStream(true)
    startProc()
    collectOutput(fun, outputReader)
  }

  /**
   * Start process and return all output in standard output and error.
   */
  override def toString = {
    val output = (collect { case s: String => s }).mkString(String format "%n")
    waitFor()
    output
  }

  /**
   * Feed data to standard input.
   */
  def input(lines: String*) = {
    startProc()
    try {
      for (s <- lines) {
        inputWriter.write(s)
      }
    } finally {
      inputWriter.close()
    }
    this
  }

  /**
   * Wait for process to finish and return its exit code.
   * If process has not been started, it will be started and then waited.
   */
  def waitFor(): Int = {
    if (proc == null)
      startProc()
    try {
      if (inputWriter != null)
        inputWriter.close()
      // there is no need to close output readers
    } finally {
    }
    proc.waitFor()
  }

  /**
   * Start this process in background (does not block main thread).
   */
  def bg() = this.startProc()

  /**
   * Destroy the process and return its exit value.
   * If process has not been started, an IllegalStateException is thrown.
   */
  def destroy(): Int = proc match {
    case null => throw new IllegalStateException("Process has not started")
    case _ => proc.destroy(); waitFor()
  }

  /**
   * Start this process in background and commence interactive IO with it.
   */
  def interact(fun: ActorRef => Unit) = {
    startProc()
    fun(actor(new Act {
      become {
        // Output control
        case ProcStdoutReadLine =>
          sender ! outputReader.readLine()
        case ProcStdoutReadChar =>
          sender ! outputReader.read()
        case ProcStderrReadLine =>
          sender ! errorReader.readLine()
        case ProcStderrReadChar =>
          sender ! errorReader.read()
        // Input control
        case s: String =>
          inputWriter.write(s)
        case c: Char =>
          inputWriter.write(c.toInt)
        case ProcStdinFlush =>
          inputWriter.flush()
        case ProcStdinClose =>
          inputWriter.close(); inputWriter = null
      }
    }))
    this
  }

  /**
   * Start the process.
   */
  protected def startProc() {
    if (proc == null) {
      env.applyTo(pb)
      proc = pb.start()
      inputWriter = new BufferedWriter(new OutputStreamWriter(proc getOutputStream))
      outputReader = new BufferedReader(new InputStreamReader(proc getInputStream))
      errorReader = new BufferedReader(new InputStreamReader(proc getErrorStream))
    }
  }

  /**
   * Collect process output from the reader, and feel them to the function.
   */
  protected def collectOutput[U](fun: String => U, reader: BufferedReader) {
    try {
      breakable {
        while (true) {
          val line = reader.readLine()
          if (line == null)
            break
          fun(line)
        }
      }
    } finally {
      reader close
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy