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