Как отменить ConsoleReader.readLine()

Прежде всего, я изучаю скала и новичок в мире 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 ответа

Решение

Основной вызов для чтения символов из ввода - блокирование. На платформе, отличной от Windows, он будет использовать System.in.read() и на винде он будет использовать org.fusesource.jansi.internal.WindowsSupport.readByte,

Таким образом, ваша задача состоит в том, чтобы заставить этот блокирующий вызов вернуться, когда вы хотите остановить консольную службу. См. http://www.javaspecialists.eu/archive/Issue153.html и возможно ли чтение из InputStream с таймаутом? для некоторых идей... Как только вы это выясните, есть read вернуть -1 когда ваша консольная служба останавливается, так что ConsoleReader думает, что сделано. Тебе понадобиться ConsoleReader использовать вашу версию этого звонка:

  • Если вы работаете в Windows, вам, вероятно, придется переопределить tools.jline.AnsiWindowsTerminal и использовать ConsoleReader конструктор, который принимает Terminal (иначе AnsiWindowsTerminal будет просто использовать WindowsSupport.readByte` напрямую)
  • На Unix есть один ConsoleReader конструктор, который принимает InputStream, вы могли бы предоставить свою собственную обертку вокруг System.in

Еще несколько мыслей:

  • E сть scala.Console объект уже, так что для меньшего беспорядка назовите свое по-другому.
  • System.in это уникальный ресурс, поэтому вам, вероятно, нужно убедиться, что только один вызывающий использует Console.readLine вовремя. Прямо сейчас start прямо позвоню readLine и несколько звонящих могут звонить start, Возможно консольный сервис может readLine и поддерживать список обработчиков.

Предполагая, что ConsoleReader.readLine реагирует на прерывание потока, вы можете переписать Консоль, чтобы использовать Поток, который затем можете прервать, чтобы остановить его.

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()
  }

}

Вы можете перезаписать ваш 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 (особенно кроссплатформенный). Я вернулся к этому варианту. Загрузка процессора составляет около 0%. Это подходит для такого интерактивного приложения.

Другие вопросы по тегам