Skip to content

Instantly share code, notes, and snippets.

@themillhousegroup
Last active May 21, 2018 20:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save themillhousegroup/9655205 to your computer and use it in GitHub Desktop.
Save themillhousegroup/9655205 to your computer and use it in GitHub Desktop.
Watching a directory with Scala Futures via Java nio
class DirectoryFileCreationWatcher(directoryToWatch:Path) {
import java.nio.file._
import java.nio.file.StandardWatchEventKinds._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.collection.JavaConversions._
val watcher = FileSystems.getDefault.newWatchService
// Work properly on Mac:
//http://stackoverflow.com/questions/9588737/is-java-7-watchservice-slow-for-anyone-else
val kinds:Array[Kind[_]] = Seq(StandardWatchEventKinds.ENTRY_CREATE).toArray
directoryToWatch.register(watcher, kinds, SensitivityWatchEventModifier.HIGH.asInstanceOf[WatchEvent.Modifier])
/**
*
* @param pathMatcher a function that returns true if this is the file we're looking for
* @return a Future holding a Path that represents the directory in its "new" state
*/
def awaitFile( pathMatcher: Path => Boolean):Future[Path] = Future[Path] {
var foundMatch = false
while (!foundMatch) {
val watchKey = watcher.take // Blocks
val events = watchKey.pollEvents
foundMatch = events.exists { event =>
val wep = event.asInstanceOf[WatchEvent[Path]]
pathMatcher(wep.context)
}
watchKey.reset
}
directoryToWatch
}
}
class DirectoryFileCreationWatcherSpec extends Specification {
import java.nio.file._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.collection.JavaConversions._
isolated
"DirectoryFileCreationWatcher" should {
val watchedDir = Files.createTempDirectory("watchedDir")
val testFileName = "testFile.txt"
val secondTestFileName = "testFile2.txt"
val waitToNoticeChange = 2000
val noticeDelay = scala.concurrent.duration.FiniteDuration(waitToNoticeChange, "millis")
val halfSecond = scala.concurrent.duration.FiniteDuration(500, "millis")
def addFileToWatchedDir(fileName:String):java.io.File = {
val f = new java.io.File(watchedDir.toFile, fileName)
f.createNewFile
println(s"added file ${f.getAbsolutePath}")
f
}
"not notify the caller until the dir contains a matching file - any file" in {
val future = new DirectoryFileCreationWatcher(watchedDir).awaitFile{ path =>
true
}
future.isCompleted must beFalse
addFileToWatchedDir(testFileName)
Thread.sleep(waitToNoticeChange)
future.isCompleted must beTrue
}
"notify the caller with the new contents of the dir - for any file" in {
val future = new DirectoryFileCreationWatcher(watchedDir).awaitFile{ path =>
true
}
watchedDir.toFile.list.contains(testFileName) must beFalse
addFileToWatchedDir(testFileName)
future.map { dirPath =>
println(s"dirPath: ${dirPath.toFile.list.mkString}" )
dirPath.toFile.list.contains(testFileName)
} must beTrue.await(timeout = halfSecond, retries=4)
}
"not notify the caller until the dir contains a matching file - exact match on file" in {
val future = new DirectoryFileCreationWatcher(watchedDir).awaitFile{ path =>
path.endsWith(testFileName)
}
future.isCompleted must beFalse
addFileToWatchedDir(secondTestFileName)
Thread.sleep(waitToNoticeChange)
future.isCompleted must beFalse
addFileToWatchedDir(testFileName)
Thread.sleep(waitToNoticeChange)
future.isCompleted must beTrue
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment