如何取消ConsoleReader.readLine()

时间:2011-07-17 09:47:39

标签: scala actor jline

首先,我正在学习scala和java世界的新手。 我想创建一个控制台并将此控制台作为可以启动和停止的服务运行。 我能够将ConsoleReader运行到Actor中,但我不知道如何正确地停止ConsoleReader。 这是代码:

import eu.badmood.util.trace
import scala.actors.Actor._

import tools.jline.console.ConsoleReader

object Main {

  def main(args:Array[String]){
    //start the console
    Console.start(message => {
      //handle console inputs
      message match {
        case "exit" => Console.stop()
        case _ => trace(message)
      }
    })

    //try to stop the console after a time delay
    Thread.sleep(2000)
    Console.stop()

  }

}

object Console {

  private val consoleReader = new ConsoleReader()

  private var running = false

  def start(handler:(String)=>Unit){
    running = true
    actor{
      while (running){
        handler(consoleReader.readLine("\33[32m> \33[0m"))
      }
    }
  }

  def stop(){
    //how to cancel an active call to ConsoleReader.readLine ?
    running = false
  }

}

我也在寻找有关此代码的任何建议!

3 个答案:

答案 0 :(得分:3)

从输入中读取字符的基础调用是阻塞的。在非Windows平台上,它将使用System.in.read(),在Windows上,它将使用org.fusesource.jansi.internal.WindowsSupport.readByte

所以你的挑战是当你想要停止你的控制台服务时,让阻止调用返回。有关一些想法,请参阅http://www.javaspecialists.eu/archive/Issue153.htmlIs it possible to read from a InputStream with a timeout? ...一旦弄明白,当控制台服务停止时,让read返回-1,以便ConsoleReader认为完成。您需要ConsoleReader才能使用您的通话版本:

  • 如果您使用的是Windows,则可能需要覆盖tools.jline.AnsiWindowsTerminal并使用ConsoleReader的{​​{1}}构造函数(否则Terminal只会使用WindowsSupport。 readByte`直接)
  • 在unix上,有一个AnsiWindowsTerminal构造函数需要ConsoleReader,您可以在InputStream
  • 周围提供自己的包装器

还有一些想法:

  • 已经有一个System.in对象,所以为了减少混淆,请用不同的名称命名。
  • scala.Console是一种独特的资源,因此您可能需要确保一次只有一位来电者使用System.in。现在Console.readLine会直接拨打start,多个来电者可以拨打readLine。可能控制台服务可以start并维护一个处理程序列表。

答案 1 :(得分:1)

假设ConsoleReader.readLine响应线程中断,您可以重写Console以使用一个线程,然后您可以中断该线程以阻止它。

object Console {

  private val consoleReader = new ConsoleReader()
  private var thread : Thread = _

  def start(handler:(String)=>Unit) : Thread = {
    thread = new Thread(new Runnable {
      override def run() {
        try {
          while (true) {
            handler(consoleReader.readLine("\33[32m> \33[0m"))
          }
        } catch {
          case ie: InterruptedException =>
        }
      }
    })
    thread.start()
    thread
  }

  def stop() {
    thread.interrupt()
  }

}

答案 2 :(得分:0)

您可以覆盖您的ConsoleReader InputStream。恕我直言,这是合理的,因为STDIN是一个“慢”流。请根据您的需求改进示例。这只是草图,但它有效:

def createReader() =
terminal.synchronized {
  val reader = new ConsoleReader
  terminal.enableEcho()
  reader.setBellEnabled(false)
  reader.setInput(new InputStreamWrapper(reader.getInput())) // turn on InterruptedException for InputStream.read
  reader
}

使用InputStream包装器:

class InputStreamWrapper(is: InputStream, val timeout: Long = 50) extends FilterInputStream(is) {
@tailrec
final override def read(): Int = {
  if (is.available() != 0)
    is.read()
  else {
    Thread.sleep(timeout)
    read()
  }
}

}

P.S。我试图使用NIO - System.in(特别是跨平台)的许多麻烦。我回到了这个变种。 CPU负载接近0%。这适用于这种交互式应用程序。