pathikrit / better-files
1
package better.files
2

3
import akka.actor._
4

5
/**
6
  * An actor that can watch a file or a directory
7
  * Instead of directly calling the constructor of this, call file.newWatcher to create the actor
8
  *
9
  * @param file     watch this file (or directory)
10
  * @param maxDepth In case of directories, how much depth should we watch
11
  */
12
class FileWatcher(file: File, maxDepth: Int) extends Actor {
13
  import FileWatcher._
14

15 1
  def this(file: File, recursive: Boolean = true) = this(file, if (recursive) Int.MaxValue else 0)
16

17 1
  protected[this] val callbacks = newMultiMap[Event, Callback]
18

19 1
  protected[this] val monitor: File.Monitor = new FileMonitor(file, maxDepth) {
20 1
    override def onEvent(event: Event, file: File, count: Int) = self ! Message.NewEvent(event, file, count)
21 0
    override def onException(exception: Throwable)             = self ! Status.Failure(exception)
22
  }
23

24 1
  override def preStart() = monitor.start()(executionContext = context.dispatcher)
25

26 1
  override def receive = {
27 1
    case Message.NewEvent(event, target, count) if callbacks.contains(event) =>
28 1
      callbacks(event).foreach(f => repeat(count)(f(event -> target)))
29 1
    case Message.RegisterCallback(events, callback) => events.foreach(event => callbacks.addBinding(event, callback))
30 0
    case Message.RemoveCallback(event, callback)    => callbacks.removeBinding(event, callback)
31
  }
32

33 1
  override def postStop() = monitor.stop()
34
}
35

36
object FileWatcher {
37
  import java.nio.file.{Path, WatchEvent}
38

39
  type Event    = WatchEvent.Kind[Path]
40
  type Callback = PartialFunction[(Event, File), Unit]
41

42
  sealed trait Message
43
  object Message {
44
    case class NewEvent(event: Event, file: File, count: Int)                   extends Message
45
    case class RegisterCallback(events: Traversable[Event], callback: Callback) extends Message
46
    case class RemoveCallback(event: Event, callback: Callback)                 extends Message
47
  }
48

49
  implicit val disposeActorSystem: Disposable[ActorSystem] =
50 1
    Disposable(_.terminate())
51

52
  implicit class FileWatcherOps(file: File) {
53
    def watcherProps(recursive: Boolean): Props =
54 1
      Props(new FileWatcher(file, recursive))
55

56
    def newWatcher(recursive: Boolean = true)(implicit system: ActorSystem): ActorRef =
57 1
      system.actorOf(watcherProps(recursive))
58
  }
59

60
  def when(events: Event*)(callback: Callback): Message =
61 1
    Message.RegisterCallback(events, callback)
62

63
  def on(event: Event)(callback: File => Unit): Message =
64 1
    when(event) { case (`event`, file) => callback(file) }
65

66
  def stop(event: Event, callback: Callback): Message =
67 0
    Message.RemoveCallback(event, callback)
68
}

Read our documentation on viewing source code .

Loading