11

首先,我正在学习 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
  }

}

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

4

3 回答 3

3

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

因此,您面临的挑战是在您想要停止控制台服务时使该阻塞调用返回。请参阅http://www.javaspecialists.eu/archive/Issue153.html是否可以从 InputStream 中读取超时?一些想法......一旦你弄清楚了,当你的控制台服务停止时read返回-1,这样就ConsoleReader认为它已经完成了。您需要ConsoleReader使用该调用的版本:

  • 如果您在 Windows 上,您可能需要覆盖tools.jline.AnsiWindowsTerminal并使用ConsoleReader采用 a 的构造函数Terminal(否则AnsiWindowsTerminal将直接使用 WindowsSupport.readByte`)
  • 在 unix 上,有一个ConsoleReader构造函数需要一个InputStream,您可以提供自己的包装器System.in

还有一些想法:

  • 已经有一个scala.Console对象了,所以为了减少混淆,你可以用不同的名字来命名。
  • System.in是一种独特的资源,因此您可能需要确保一次只有一个调用者使用Console.readLine。现在start将直接调用readLine,多个调用者可以调用start。控制台服务可能可以readLine并维护一个处理程序列表。
于 2011-07-17T15:49:09.507 回答
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()
  }

}
于 2011-07-18T18:37:52.047 回答
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()
  }
}

}

PS我尝试使用NIO - System.in(尤其是跨平台)有很多麻烦。我回到了这个变种。CPU 负载接近 0%。这适用于这种交互式应用。

于 2012-11-23T13:15:26.610 回答