Scala で常駐する Daemon を作る方法

Scala で常駐する Daemon を作る用途があり、fsc が使っている方法をパクったのでメモを残す。例によって添削は熱烈大歓迎です。
ざくっと概要を説明すると ProcessBuilder 経由で別の Java VM を起動し、さっさと exit してしまえば Daemonize 完了となる。
という事で下記のような Helper を作り…

import java.io.{IOException, PrintStream}

import scala.tools.nsc.Properties
import scala.tools.nsc.io.{Process, Path, Directory, File}

object DaemonizeHelper {
  private lazy val tmpPath = Path(Properties.tmpDir)
  private lazy val userName = Option(Properties.userName) getOrElse "shared"
  private lazy val vmCmd: String = {
    val scalaCmd = Properties.scalaCmd
    Properties.scalaHome match {
      case ""      => scalaCmd
      case scalaHome => 
        val fullPath = File(scalaHome) / "bin" / scalaCmd
        if (fullPath.canRead) fullPath.path else scalaCmd
    }
  }

  def daemonize(
    logDir: String,
    outFile: String = "out.log",
    errFile: String = "err.log"
  ): Unit = {
    System.in.close()

    def redirect(setter: PrintStream => Unit, filename: String): Unit = {
      val redirectDir: Directory = {
        val dir = (tmpPath / logDir / userName).createDirectory()
        if (dir.isDirectory && dir.canWrite) dir
        else fatal("Could not find a directory for redirect files.")
      }
      println(redirectDir)
      setter(new PrintStream((redirectDir / filename).createFile().bufferedOutput()))
    }

    redirect(System.setOut, outFile)
    redirect(System.setErr, errFile)
  }

  def startDaemon(vmArgs: Seq[String] = Nil)(afterBlock: => Unit): Unit = {
    val cmd = Seq(vmCmd) ++ vmArgs filterNot (_ == "")
    try Process.exec(cmd) catch {
      case e: IOException => fatal("Cannot start daemon.\ntried command: %s".format(cmd))
    }
    afterBlock
    exit(0)
  }

  private def fatal(message: String) = {
    System.err.println(message)
    throw new Exception(message)
  }
}

下記のような感じで使う。

import DaemonizeHelper.startDaemon

object DaemonStarter {
  def main (args: Array[String]) {
    startDaemon (Seq("SampleDaemon")) {
      println("Start Server.")
    }
  }
}
import DaemonizeHelper.daemonize

object SampleDaemon {
  def main (args: Array[String]) {
    daemonize("sample_daemon")
    Thread.sleep(60000)
    exit(0)
  }
}

今回の例では、scala.tools.nsc.Properties は scala.util.Properties でも良い。用途に応じて PropertiesTrait を extends した object を作るのが作法だと思う(が今回は気にしない)。
scala.tools.nsc 配下は、Scala コンパイラ関連の諸々を配置するパッケージ空間らしいのだが、scala.tools.nsc.io 配下に Process, Path, Directory, File などが存在しており、これは、あまりにも便利であるため scala.io 直下でも良いのではないかと思う。Undocumented (なのかな?) な scala.tools.nsc 配下は、あまり触らない方が良いのかもしれないけど…。