src/Pure/General/file_watcher.scala
author wenzelm
Fri, 30 Dec 2016 20:36:13 +0100
changeset 64710 72ca4e5f976e
parent 64700 14deaeaa6476
child 64782 3f0bbb60859b
permissions -rw-r--r--
manage changes of external files; tuned;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
64699
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     1
/*  Title:      Pure/General/file_watcher.scala
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     3
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     4
Watcher for file-system events.
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     5
*/
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     6
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     7
package isabelle
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     8
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
     9
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    10
import java.io.{File => JFile}
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    11
import java.nio.file.FileSystems
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    12
import java.nio.file.{WatchKey, WatchEvent, Path => JPath}
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    13
import java.nio.file.StandardWatchEventKinds.{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    14
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    15
import scala.collection.JavaConversions
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    16
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    17
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    18
object File_Watcher
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    19
{
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    20
  def apply(handle: Set[JFile] => Unit, delay: => Time = Time.seconds(0.5)): File_Watcher =
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    21
    new File_Watcher(handle, delay)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    22
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    23
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    24
  /* internal state */
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    25
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    26
  sealed case class State(
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    27
    dirs: Map[JFile, WatchKey] = Map.empty,
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    28
    changed: Set[JFile] = Set.empty)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    29
}
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    30
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    31
class File_Watcher private(handle: Set[JFile] => Unit, delay: Time)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    32
{
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    33
  private val state = Synchronized(File_Watcher.State())
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    34
  private val watcher = FileSystems.getDefault.newWatchService()
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    35
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    36
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    37
  /* registered directories */
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    38
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    39
  def register(dir: JFile): Unit =
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    40
    state.change(st =>
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    41
      st.dirs.get(dir) match {
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    42
        case Some(key) if key.isValid => st
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    43
        case _ =>
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    44
          val key = dir.toPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    45
          st.copy(dirs = st.dirs + (dir -> key))
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    46
      })
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    47
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    48
  def deregister(dir: JFile): Unit =
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    49
    state.change(st =>
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    50
      st.dirs.get(dir) match {
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    51
        case None => st
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    52
        case Some(key) =>
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    53
          key.cancel
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    54
          st.copy(dirs = st.dirs - dir)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    55
      })
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    56
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    57
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    58
  /* changed directory entries */
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    59
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    60
  private val delay_changed = Standard_Thread.delay_last(delay)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    61
  {
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    62
    val changed = state.change_result(st => (st.changed, st.copy(changed = Set.empty)))
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    63
    handle(changed)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    64
  }
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    65
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    66
  private val watcher_thread = Standard_Thread.fork("File_Watcher", daemon = true)
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    67
  {
64700
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    68
    try {
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    69
      while (true) {
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    70
        val key = watcher.take
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    71
        val has_changed =
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    72
          state.change_result(st =>
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    73
            {
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    74
              val (remove, changed) =
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    75
                st.dirs.collectFirst({ case (dir, key1) if key == key1 => dir }) match {
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    76
                  case Some(dir) =>
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    77
                    val events =
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    78
                      JavaConversions.collectionAsScalaIterable(
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    79
                        key.pollEvents.asInstanceOf[java.util.List[WatchEvent[JPath]]])
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    80
                    val remove = if (key.reset) None else Some(dir)
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    81
                    val changed =
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    82
                      (Set.empty[JFile] /: events.iterator) {
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    83
                        case (set, event) => set + dir.toPath.resolve(event.context).toFile
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    84
                      }
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    85
                    (remove, changed)
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    86
                  case None =>
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    87
                    key.pollEvents
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    88
                    key.reset
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    89
                    (None, Set.empty[JFile])
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    90
                }
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    91
              (changed.nonEmpty, st.copy(dirs = st.dirs -- remove, changed = st.changed ++ changed))
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    92
            })
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    93
        if (has_changed) delay_changed.invoke()
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    94
      }
64699
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    95
    }
64700
14deaeaa6476 more robust shutdown;
wenzelm
parents: 64699
diff changeset
    96
    catch { case Exn.Interrupt() => }
64699
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    97
  }
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    98
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
    99
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   100
  /* shutdown */
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   101
64710
72ca4e5f976e manage changes of external files;
wenzelm
parents: 64700
diff changeset
   102
  def shutdown()
64699
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   103
  {
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   104
    watcher_thread.interrupt
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   105
    watcher_thread.join
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   106
    delay_changed.revoke
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   107
  }
218c35908d5f watcher for file-system events;
wenzelm
parents:
diff changeset
   108
}