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 配下は、あまり触らない方が良いのかもしれないけど…。