Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to cancel ConsoleReader.readLine()

Tags:

scala

actor

jline

first of all, i'm learning scala and new to the java world. I want to create a console and run this console as a service that you could start and stop. I was able to run a ConsoleReader into an Actor but i don't know how to stop properly the ConsoleReader. Here is the code :

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
  }

}

I'm also looking for any advice concerning this code !

like image 448
OXMO456 Avatar asked Jul 17 '11 09:07

OXMO456


1 Answers

The underlying call to read a characters from the input is blocking. On non-Windows platform, it will use System.in.read() and on Windows it will use org.fusesource.jansi.internal.WindowsSupport.readByte.

So your challenge is to cause that blocking call to return when you want to stop your console service. See http://www.javaspecialists.eu/archive/Issue153.html and Is it possible to read from a InputStream with a timeout? for some ideas... Once you figure that out, have read return -1 when your console service stops, so that ConsoleReader thinks it's done. You'll need ConsoleReader to use your version of that call:

  • If you are on Windows, you'll probably need to override tools.jline.AnsiWindowsTerminal and use the ConsoleReader constructor that takes a Terminal (otherwise AnsiWindowsTerminal will just use WindowsSupport.readByte` directly)
  • On unix, there is one ConsoleReader constructor that takes an InputStream, you could provide your own wrapper around System.in

A few more thoughts:

  • There is a scala.Console object already, so for less confusion name yours differently.
  • System.in is a unique resource, so you probably need to ensure that only one caller uses Console.readLine at a time. Right now start will directly call readLine and multiple callers can call start. Probably the console service can readLine and maintain a list of handlers.
like image 106
huynhjl Avatar answered Sep 20 '22 10:09

huynhjl