All Downloads are FREE. Search and download functionalities are using the official Maven repository.

better.files.FileMonitor.scala Maven / Gradle / Ivy

The newest version!
package better.files

import java.nio.file._

import scala.concurrent.{blocking, ExecutionContext}
import scala.util.Try
import scala.util.control.NonFatal

/**
  * Implementation of File.Monitor
  *
  * @param root
  * @param maxDepth
  */
abstract class FileMonitor(val root: File, maxDepth: Int) extends File.Monitor {
  protected[this] val service = root.newWatchService

  def this(root: File, recursive: Boolean = true) = this(root, if (recursive) Int.MaxValue else 0)

  /**
    * If watching non-directory, don't react to siblings
    * @param target
    * @return
    */
  protected[this] def reactTo(target: File) = root.isDirectory || root.isSamePathAs(target)

  protected[this] def process(key: WatchKey) = {
    val path = key.watchable().asInstanceOf[Path]

    import scala.collection.JavaConverters._
    key.pollEvents().asScala foreach {
      case event: WatchEvent[Path] @unchecked =>
        val target: File = path.resolve(event.context())
        if (reactTo(target)) {
          if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
            val depth = root.relativize(target).getNameCount
            watch(target, (maxDepth - depth) max 0) // auto-watch new files in a directory
          }
          onEvent(event.kind(), target, event.count())
        }
      case event => if (reactTo(path)) onUnknownEvent(event)
    }
    key.reset()
  }

  protected[this] def watch(file: File, depth: Int): Unit = {
    def toWatch: Iterator[File] =
      if (file.isDirectory) {
        file.walk(depth).filter(f => f.isDirectory && f.exists)
      } else {
        when(file.exists)(file.parent).iterator // There is no way to watch a regular file; so watch its parent instead
      }
    try {
      toWatch.foreach(f => Try[Unit](f.register(service)).recover { case e => onException(e) }.get)
    } catch {
      case NonFatal(e) => onException(e)
    }
  }

  override def start()(implicit executionContext: ExecutionContext) = {
    watch(root, maxDepth)
    executionContext.execute(new Runnable {
      override def run() = blocking { Iterator.continually(service.take()).foreach(process) }
    })
  }

  override def close() = service.close()

  // Although this class is abstract, we provide noop implementations so user can choose to implement a subset of these
  override def onCreate(file: File, count: Int)     = {}
  override def onModify(file: File, count: Int)     = {}
  override def onDelete(file: File, count: Int)     = {}
  override def onUnknownEvent(event: WatchEvent[_]) = {}
  override def onException(exception: Throwable)    = {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy