Как отменить 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%. Это подходит для такого интерактивного приложения.