src/Pure/Tools/server.scala
changeset 75393 87ebf5a50283
parent 74306 a117c076aa22
child 75394 42267c650205
equal deleted inserted replaced
75388:b3ca4a6ed74b 75393:87ebf5a50283
    22 import java.io.{BufferedInputStream, BufferedOutputStream, InputStreamReader, OutputStreamWriter,
    22 import java.io.{BufferedInputStream, BufferedOutputStream, InputStreamReader, OutputStreamWriter,
    23   IOException}
    23   IOException}
    24 import java.net.{Socket, SocketException, SocketTimeoutException, ServerSocket, InetAddress}
    24 import java.net.{Socket, SocketException, SocketTimeoutException, ServerSocket, InetAddress}
    25 
    25 
    26 
    26 
    27 object Server
    27 object Server {
    28 {
       
    29   /* message argument */
    28   /* message argument */
    30 
    29 
    31   object Argument
    30   object Argument {
    32   {
       
    33     def is_name_char(c: Char): Boolean =
    31     def is_name_char(c: Char): Boolean =
    34       Symbol.is_ascii_letter(c) || Symbol.is_ascii_digit(c) || c == '_' || c == '.'
    32       Symbol.is_ascii_letter(c) || Symbol.is_ascii_digit(c) || c == '_' || c == '.'
    35 
    33 
    36     def split(msg: String): (String, String) =
    34     def split(msg: String): (String, String) = {
    37     {
       
    38       val name = msg.takeWhile(is_name_char)
    35       val name = msg.takeWhile(is_name_char)
    39       val argument = msg.substring(name.length).dropWhile(Symbol.is_ascii_blank)
    36       val argument = msg.substring(name.length).dropWhile(Symbol.is_ascii_blank)
    40       (name, argument)
    37       (name, argument)
    41     }
    38     }
    42 
    39 
    60 
    57 
    61   /* input command */
    58   /* input command */
    62 
    59 
    63   type Command_Body = PartialFunction[(Context, Any), Any]
    60   type Command_Body = PartialFunction[(Context, Any), Any]
    64 
    61 
    65   abstract class Command(val command_name: String)
    62   abstract class Command(val command_name: String) {
    66   {
       
    67     def command_body: Command_Body
    63     def command_body: Command_Body
    68     override def toString: String = command_name
    64     override def toString: String = command_name
    69   }
    65   }
    70 
    66 
    71   class Commands(commands: Command*) extends Isabelle_System.Service
    67   class Commands(commands: Command*) extends Isabelle_System.Service {
    72   {
       
    73     def entries: List[Command] = commands.toList
    68     def entries: List[Command] = commands.toList
    74   }
    69   }
    75 
    70 
    76   private lazy val command_table: Map[String, Command] =
    71   private lazy val command_table: Map[String, Command] =
    77     Isabelle_System.make_services(classOf[Commands]).flatMap(_.entries).
    72     Isabelle_System.make_services(classOf[Commands]).flatMap(_.entries).
    94       case ERROR(msg) => Reply.error_message(msg)
    89       case ERROR(msg) => Reply.error_message(msg)
    95       case _ if Exn.is_interrupt(exn) => Reply.error_message(Exn.message(exn))
    90       case _ if Exn.is_interrupt(exn) => Reply.error_message(Exn.message(exn))
    96       case _ => JSON.Object.empty
    91       case _ => JSON.Object.empty
    97     }
    92     }
    98 
    93 
    99   object Reply extends Enumeration
    94   object Reply extends Enumeration {
   100   {
       
   101     val OK, ERROR, FINISHED, FAILED, NOTE = Value
    95     val OK, ERROR, FINISHED, FAILED, NOTE = Value
   102 
    96 
   103     def message(msg: String, kind: String = ""): JSON.Object.T =
    97     def message(msg: String, kind: String = ""): JSON.Object.T =
   104       JSON.Object(Markup.KIND -> proper_string(kind).getOrElse(Markup.WRITELN), "message" -> msg)
    98       JSON.Object(Markup.KIND -> proper_string(kind).getOrElse(Markup.WRITELN), "message" -> msg)
   105 
    99 
   106     def error_message(msg: String): JSON.Object.T =
   100     def error_message(msg: String): JSON.Object.T =
   107       message(msg, kind = Markup.ERROR)
   101       message(msg, kind = Markup.ERROR)
   108 
   102 
   109     def unapply(msg: String): Option[(Reply.Value, Any)] =
   103     def unapply(msg: String): Option[(Reply.Value, Any)] = {
   110     {
       
   111       if (msg == "") None
   104       if (msg == "") None
   112       else {
   105       else {
   113         val (name, argument) = Argument.split(msg)
   106         val (name, argument) = Argument.split(msg)
   114         for {
   107         for {
   115           reply <-
   108           reply <-
   122   }
   115   }
   123 
   116 
   124 
   117 
   125   /* handler: port, password, thread */
   118   /* handler: port, password, thread */
   126 
   119 
   127   abstract class Handler(port0: Int)
   120   abstract class Handler(port0: Int) {
   128   {
       
   129     val socket: ServerSocket = new ServerSocket(port0, 50, Server.localhost)
   121     val socket: ServerSocket = new ServerSocket(port0, 50, Server.localhost)
   130     def port: Int = socket.getLocalPort
   122     def port: Int = socket.getLocalPort
   131     def address: String = print_address(port)
   123     def address: String = print_address(port)
   132     val password: String = UUID.random().toString
   124     val password: String = UUID.random().toString
   133 
   125 
   156   }
   148   }
   157 
   149 
   158 
   150 
   159   /* socket connection */
   151   /* socket connection */
   160 
   152 
   161   object Connection
   153   object Connection {
   162   {
       
   163     def apply(socket: Socket): Connection =
   154     def apply(socket: Socket): Connection =
   164       new Connection(socket)
   155       new Connection(socket)
   165   }
   156   }
   166 
   157 
   167   class Connection private(socket: Socket) extends AutoCloseable
   158   class Connection private(socket: Socket) extends AutoCloseable {
   168   {
       
   169     override def toString: String = socket.toString
   159     override def toString: String = socket.toString
   170 
   160 
   171     def close(): Unit = socket.close()
   161     def close(): Unit = socket.close()
   172 
   162 
   173     def set_timeout(t: Time): Unit = socket.setSoTimeout(t.ms.toInt)
   163     def set_timeout(t: Time): Unit = socket.setSoTimeout(t.ms.toInt)
   202       out_lock.synchronized { Byte_Message.write_line_message(out, Bytes(UTF8.bytes(msg))) }
   192       out_lock.synchronized { Byte_Message.write_line_message(out, Bytes(UTF8.bytes(msg))) }
   203 
   193 
   204     def write_byte_message(chunks: List[Bytes]): Unit =
   194     def write_byte_message(chunks: List[Bytes]): Unit =
   205       out_lock.synchronized { Byte_Message.write_message(out, chunks) }
   195       out_lock.synchronized { Byte_Message.write_message(out, chunks) }
   206 
   196 
   207     def reply(r: Reply.Value, arg: Any): Unit =
   197     def reply(r: Reply.Value, arg: Any): Unit = {
   208     {
       
   209       val argument = Argument.print(arg)
   198       val argument = Argument.print(arg)
   210       write_line_message(if (argument == "") r.toString else r.toString + " " + argument)
   199       write_line_message(if (argument == "") r.toString else r.toString + " " + argument)
   211     }
   200     }
   212 
   201 
   213     def reply_ok(arg: Any): Unit = reply(Reply.OK, arg)
   202     def reply_ok(arg: Any): Unit = reply(Reply.OK, arg)
   220 
   209 
   221 
   210 
   222   /* context with output channels */
   211   /* context with output channels */
   223 
   212 
   224   class Context private[Server](val server: Server, connection: Connection)
   213   class Context private[Server](val server: Server, connection: Connection)
   225     extends AutoCloseable
   214   extends AutoCloseable {
   226   {
       
   227     context =>
   215     context =>
   228 
   216 
   229     def command_list: List[String] = command_table.keys.toList.sorted
   217     def command_list: List[String] = command_table.keys.toList.sorted
   230 
   218 
   231     def reply(r: Reply.Value, arg: Any): Unit = connection.reply(r, arg)
   219     def reply(r: Reply.Value, arg: Any): Unit = connection.reply(r, arg)
   245 
   233 
   246     /* asynchronous tasks */
   234     /* asynchronous tasks */
   247 
   235 
   248     private val _tasks = Synchronized(Set.empty[Task])
   236     private val _tasks = Synchronized(Set.empty[Task])
   249 
   237 
   250     def make_task(body: Task => JSON.Object.T): Task =
   238     def make_task(body: Task => JSON.Object.T): Task = {
   251     {
       
   252       val task = new Task(context, body)
   239       val task = new Task(context, body)
   253       _tasks.change(_ + task)
   240       _tasks.change(_ + task)
   254       task
   241       task
   255     }
   242     }
   256 
   243 
   258       _tasks.change(_ - task)
   245       _tasks.change(_ - task)
   259 
   246 
   260     def cancel_task(id: UUID.T): Unit =
   247     def cancel_task(id: UUID.T): Unit =
   261       _tasks.change(tasks => { tasks.find(task => task.id == id).foreach(_.cancel()); tasks })
   248       _tasks.change(tasks => { tasks.find(task => task.id == id).foreach(_.cancel()); tasks })
   262 
   249 
   263     def close(): Unit =
   250     def close(): Unit = {
   264     {
   251       while(_tasks.change_result(tasks => { tasks.foreach(_.cancel()); (tasks.nonEmpty, tasks) })) {
   265       while(_tasks.change_result(tasks => { tasks.foreach(_.cancel()); (tasks.nonEmpty, tasks) }))
   252         _tasks.value.foreach(_.join())
   266       { _tasks.value.foreach(_.join()) }
   253       }
   267     }
   254     }
   268   }
   255   }
   269 
   256 
   270   class Connection_Progress private[Server](context: Context, more: JSON.Object.Entry*)
   257   class Connection_Progress private[Server](context: Context, more: JSON.Object.Entry*)
   271     extends Progress
   258   extends Progress {
   272   {
       
   273     override def echo(msg: String): Unit = context.writeln(msg, more:_*)
   259     override def echo(msg: String): Unit = context.writeln(msg, more:_*)
   274     override def echo_warning(msg: String): Unit = context.warning(msg, more:_*)
   260     override def echo_warning(msg: String): Unit = context.warning(msg, more:_*)
   275     override def echo_error_message(msg: String): Unit = context.error_message(msg, more:_*)
   261     override def echo_error_message(msg: String): Unit = context.error_message(msg, more:_*)
   276 
   262 
   277     override def theory(theory: Progress.Theory): Unit =
   263     override def theory(theory: Progress.Theory): Unit = {
   278     {
       
   279       val entries: List[JSON.Object.Entry] =
   264       val entries: List[JSON.Object.Entry] =
   280         List("theory" -> theory.theory, "session" -> theory.session) :::
   265         List("theory" -> theory.theory, "session" -> theory.session) :::
   281           (theory.percentage match { case None => Nil case Some(p) => List("percentage" -> p) })
   266           (theory.percentage match { case None => Nil case Some(p) => List("percentage" -> p) })
   282       context.writeln(theory.message, entries ::: more.toList:_*)
   267       context.writeln(theory.message, entries ::: more.toList:_*)
   283     }
   268     }
   284 
   269 
   285     override def nodes_status(nodes_status: Document_Status.Nodes_Status): Unit =
   270     override def nodes_status(nodes_status: Document_Status.Nodes_Status): Unit = {
   286     {
       
   287       val json =
   271       val json =
   288         for ((name, node_status) <- nodes_status.present)
   272         for ((name, node_status) <- nodes_status.present)
   289           yield name.json + ("status" -> node_status.json)
   273           yield name.json + ("status" -> node_status.json)
   290       context.notify(JSON.Object(Markup.KIND -> Markup.NODES_STATUS, Markup.NODES_STATUS -> json))
   274       context.notify(JSON.Object(Markup.KIND -> Markup.NODES_STATUS, Markup.NODES_STATUS -> json))
   291     }
   275     }
   292 
   276 
   293     override def toString: String = context.toString
   277     override def toString: String = context.toString
   294   }
   278   }
   295 
   279 
   296   class Task private[Server](val context: Context, body: Task => JSON.Object.T)
   280   class Task private[Server](val context: Context, body: Task => JSON.Object.T) {
   297   {
       
   298     task =>
   281     task =>
   299 
   282 
   300     val id: UUID.T = UUID.random()
   283     val id: UUID.T = UUID.random()
   301     val ident: JSON.Object.Entry = ("task" -> id.toString)
   284     val ident: JSON.Object.Entry = ("task" -> id.toString)
   302 
   285 
   303     val progress: Connection_Progress = context.progress(ident)
   286     val progress: Connection_Progress = context.progress(ident)
   304     def cancel(): Unit = progress.stop()
   287     def cancel(): Unit = progress.stop()
   305 
   288 
   306     private lazy val thread = Isabelle_Thread.fork(name = "server_task")
   289     private lazy val thread = Isabelle_Thread.fork(name = "server_task") {
   307     {
       
   308       Exn.capture { body(task) } match {
   290       Exn.capture { body(task) } match {
   309         case Exn.Res(res) =>
   291         case Exn.Res(res) =>
   310           context.reply(Reply.FINISHED, res + ident)
   292           context.reply(Reply.FINISHED, res + ident)
   311         case Exn.Exn(exn) =>
   293         case Exn.Exn(exn) =>
   312           val err = json_error(exn)
   294           val err = json_error(exn)
   328   def print_address(port: Int): String = localhost_name + ":" + port
   310   def print_address(port: Int): String = localhost_name + ":" + port
   329 
   311 
   330   def print(port: Int, password: String): String =
   312   def print(port: Int, password: String): String =
   331     print_address(port) + " (password " + quote(password) + ")"
   313     print_address(port) + " (password " + quote(password) + ")"
   332 
   314 
   333   object Info
   315   object Info {
   334   {
       
   335     private val Pattern =
   316     private val Pattern =
   336       ("""server "([^"]*)" = \Q""" + localhost_name + """\E:(\d+) \(password "([^"]*)"\)""").r
   317       ("""server "([^"]*)" = \Q""" + localhost_name + """\E:(\d+) \(password "([^"]*)"\)""").r
   337 
   318 
   338     def parse(s: String): Option[Info] =
   319     def parse(s: String): Option[Info] =
   339       s match {
   320       s match {
   343 
   324 
   344     def apply(name: String, port: Int, password: String): Info =
   325     def apply(name: String, port: Int, password: String): Info =
   345       new Info(name, port, password)
   326       new Info(name, port, password)
   346   }
   327   }
   347 
   328 
   348   class Info private(val name: String, val port: Int, val password: String)
   329   class Info private(val name: String, val port: Int, val password: String) {
   349   {
       
   350     def address: String = print_address(port)
   330     def address: String = print_address(port)
   351 
   331 
   352     override def toString: String =
   332     override def toString: String =
   353       "server " + quote(name) + " = " + print(port, password)
   333       "server " + quote(name) + " = " + print(port, password)
   354 
   334 
   355     def connection(): Connection =
   335     def connection(): Connection = {
   356     {
       
   357       val connection = Connection(new Socket(localhost, port))
   336       val connection = Connection(new Socket(localhost, port))
   358       connection.write_line_message(password)
   337       connection.write_line_message(password)
   359       connection
   338       connection
   360     }
   339     }
   361 
   340 
   362     def active: Boolean =
   341     def active: Boolean =
   363       try {
   342       try {
   364         using(connection())(connection =>
   343         using(connection())(connection => {
   365           {
   344           connection.set_timeout(Time.seconds(2.0))
   366             connection.set_timeout(Time.seconds(2.0))
   345           connection.read_line_message() match {
   367             connection.read_line_message() match {
   346             case Some(Reply(Reply.OK, _)) => true
   368               case Some(Reply(Reply.OK, _)) => true
   347             case _ => false
   369               case _ => false
   348           }
   370             }
   349         })
   371           })
       
   372       }
   350       }
   373       catch {
   351       catch {
   374         case _: IOException => false
   352         case _: IOException => false
   375         case _: SocketException => false
   353         case _: SocketException => false
   376         case _: SocketTimeoutException => false
   354         case _: SocketTimeoutException => false
   380 
   358 
   381   /* per-user servers */
   359   /* per-user servers */
   382 
   360 
   383   val default_name = "isabelle"
   361   val default_name = "isabelle"
   384 
   362 
   385   object Data
   363   object Data {
   386   {
       
   387     val database = Path.explode("$ISABELLE_HOME_USER/servers.db")
   364     val database = Path.explode("$ISABELLE_HOME_USER/servers.db")
   388 
   365 
   389     val name = SQL.Column.string("name").make_primary_key
   366     val name = SQL.Column.string("name").make_primary_key
   390     val port = SQL.Column.int("port")
   367     val port = SQL.Column.int("port")
   391     val password = SQL.Column.string("password")
   368     val password = SQL.Column.string("password")
   408 
   385 
   409   def init(
   386   def init(
   410     name: String = default_name,
   387     name: String = default_name,
   411     port: Int = 0,
   388     port: Int = 0,
   412     existing_server: Boolean = false,
   389     existing_server: Boolean = false,
   413     log: Logger = No_Logger): (Info, Option[Server]) =
   390     log: Logger = No_Logger
   414   {
   391   ): (Info, Option[Server]) = {
   415     using(SQLite.open_database(Data.database))(db =>
   392     using(SQLite.open_database(Data.database))(db => {
   416       {
       
   417         db.transaction {
   393         db.transaction {
   418           Isabelle_System.chmod("600", Data.database)
   394           Isabelle_System.chmod("600", Data.database)
   419           db.create_table(Data.table)
   395           db.create_table(Data.table)
   420           list(db).filterNot(_.active).foreach(server_info =>
   396           list(db).filterNot(_.active).foreach(server_info =>
   421             db.using_statement(Data.table.delete(Data.name.where_equal(server_info.name)))(
   397             db.using_statement(Data.table.delete(Data.name.where_equal(server_info.name)))(
   429 
   405 
   430               val server = new Server(port, log)
   406               val server = new Server(port, log)
   431               val server_info = Info(name, server.port, server.password)
   407               val server_info = Info(name, server.port, server.password)
   432 
   408 
   433               db.using_statement(Data.table.delete(Data.name.where_equal(name)))(_.execute())
   409               db.using_statement(Data.table.delete(Data.name.where_equal(name)))(_.execute())
   434               db.using_statement(Data.table.insert())(stmt =>
   410               db.using_statement(Data.table.insert())(stmt => {
   435               {
       
   436                 stmt.string(1) = server_info.name
   411                 stmt.string(1) = server_info.name
   437                 stmt.int(2) = server_info.port
   412                 stmt.int(2) = server_info.port
   438                 stmt.string(3) = server_info.password
   413                 stmt.string(3) = server_info.password
   439                 stmt.execute()
   414                 stmt.execute()
   440               })
   415               })
   444           }
   419           }
   445         }
   420         }
   446       })
   421       })
   447   }
   422   }
   448 
   423 
   449   def exit(name: String = default_name): Boolean =
   424   def exit(name: String = default_name): Boolean = {
   450   {
       
   451     using(SQLite.open_database(Data.database))(db =>
   425     using(SQLite.open_database(Data.database))(db =>
   452       db.transaction {
   426       db.transaction {
   453         find(db, name) match {
   427         find(db, name) match {
   454           case Some(server_info) =>
   428           case Some(server_info) =>
   455             using(server_info.connection())(_.write_line_message("shutdown"))
   429             using(server_info.connection())(_.write_line_message("shutdown"))
   462 
   436 
   463 
   437 
   464   /* Isabelle tool wrapper */
   438   /* Isabelle tool wrapper */
   465 
   439 
   466   val isabelle_tool =
   440   val isabelle_tool =
   467     Isabelle_Tool("server", "manage resident Isabelle servers", Scala_Project.here, args =>
   441     Isabelle_Tool("server", "manage resident Isabelle servers", Scala_Project.here,
   468     {
   442       args => {
   469       var console = false
   443       var console = false
   470       var log_file: Option[Path] = None
   444       var log_file: Option[Path] = None
   471       var operation_list = false
   445       var operation_list = false
   472       var operation_exit = false
   446       var operation_exit = false
   473       var name = default_name
   447       var name = default_name
   521         server.foreach(_.join())
   495         server.foreach(_.join())
   522       }
   496       }
   523     })
   497     })
   524 }
   498 }
   525 
   499 
   526 class Server private(port0: Int, val log: Logger) extends Server.Handler(port0)
   500 class Server private(port0: Int, val log: Logger) extends Server.Handler(port0) {
   527 {
       
   528   server =>
   501   server =>
   529 
   502 
   530   private val _sessions = Synchronized(Map.empty[UUID.T, Headless.Session])
   503   private val _sessions = Synchronized(Map.empty[UUID.T, Headless.Session])
   531   def err_session(id: UUID.T): Nothing = error("No session " + Library.single_quote(id.toString))
   504   def err_session(id: UUID.T): Nothing = error("No session " + Library.single_quote(id.toString))
   532   def the_session(id: UUID.T): Headless.Session = _sessions.value.getOrElse(id, err_session(id))
   505   def the_session(id: UUID.T): Headless.Session = _sessions.value.getOrElse(id, err_session(id))
   533   def add_session(entry: (UUID.T, Headless.Session)): Unit = _sessions.change(_ + entry)
   506   def add_session(entry: (UUID.T, Headless.Session)): Unit = _sessions.change(_ + entry)
   534   def remove_session(id: UUID.T): Headless.Session =
   507   def remove_session(id: UUID.T): Headless.Session = {
   535   {
       
   536     _sessions.change_result(sessions =>
   508     _sessions.change_result(sessions =>
   537       sessions.get(id) match {
   509       sessions.get(id) match {
   538         case Some(session) => (session, sessions - id)
   510         case Some(session) => (session, sessions - id)
   539         case None => err_session(id)
   511         case None => err_session(id)
   540       })
   512       })
   541   }
   513   }
   542 
   514 
   543   def shutdown(): Unit =
   515   def shutdown(): Unit = {
   544   {
       
   545     server.socket.close()
   516     server.socket.close()
   546 
   517 
   547     val sessions = _sessions.change_result(sessions => (sessions, Map.empty))
   518     val sessions = _sessions.change_result(sessions => (sessions, Map.empty))
   548     for ((_, session) <- sessions) {
   519     for ((_, session) <- sessions) {
   549       try {
   520       try {
   554     }
   525     }
   555   }
   526   }
   556 
   527 
   557   override def join(): Unit = { super.join(); shutdown() }
   528   override def join(): Unit = { super.join(); shutdown() }
   558 
   529 
   559   override def handle(connection: Server.Connection): Unit =
   530   override def handle(connection: Server.Connection): Unit = {
   560   {
   531     using(new Server.Context(server, connection))(context => {
   561     using(new Server.Context(server, connection))(context =>
       
   562     {
       
   563       connection.reply_ok(
   532       connection.reply_ok(
   564         JSON.Object(
   533         JSON.Object(
   565           "isabelle_id" -> Isabelle_System.isabelle_id(),
   534           "isabelle_id" -> Isabelle_System.isabelle_id(),
   566           "isabelle_name" -> Isabelle_System.isabelle_name()))
   535           "isabelle_name" -> Isabelle_System.isabelle_name()))
   567 
   536