Skip to content
This repository has been archived by the owner on Feb 14, 2020. It is now read-only.


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Schwatcher Build Status Maven Central

Note: Requires Java8+ because the WatchService API is an essential part of this library along with Akka (Akka 2.4.x dropped support for Java7)

TL;DR A library that wraps the WatchService API. of Java7. You can choose to send callbacks to be registered on paths (both directories and files) or turn path events into a composable asynchronous data stream (RxScala Observable). For details, check out the Example usage section below.

As of Java7, the WatchService API was introduced to allow developers to monitor the file system without resorting to an external library/dependency like JNotify. Unfortunately, it requires you to use a loop that blocks in order to retrieve events from the API and does not have recursion support built-in.

Schwatcher will allow you to instantiate an Akka actor, register callbacks (functions that take a Path and return Unit) to be fired for certain Paths and event types by sending messages to it, then simply wait for the callbacks to be fired. Alternatively, use the Observable interface to register for notifications on paths.

The goal of Schwatcher is to facilitate the use of the Java7 API in Scala in a simple way that is in line with the functional programming paradigm.

As of now, Scala 2.11.x and 2.12.x are supported (Akka 2.4.x dropped support for 2.10.x)


Add the following to your build.sbt

libraryDependencies += "com.beachape.filemanagement" %% "schwatcher" % "0.3.2"

If the above does not work because it cannot be resolved, its likely because it hasn't been synced to Maven central yet. In that case, download a SNAPSHOT release of the same version by adding this to build.sbt

resolvers += "Sonatype OSS Snapshots" at ""

libraryDependencies += "com.beachape.filemanagement" %% "schwatcher" % "0.3.2-SNAPSHOT"

Those using Akka 2.1.x should stay on 0.0.2 as 0.0.3 onwards uses Akka 2.2.x, which requires some work to upgrade to.

Those using Akka 2.2.x should stay on 0.0.9 as 0.0.10 onwards uses Akka 2.3.x, which may require some work to upgrade to.

0.1.0 brings with it breaking changes on how to register callbacks (see example). Please be aware when upgrading from 0.0.x releases.

Those using Akka 2.3.x should stay on 0.1.x as 0.2.x onwards uses Akka 2.4.x, which requires Java8 and Scala 2.11.x

Example Usage


The basic workflow is:

  1. Instantiate a MonitorActor
  2. Register callbacks by sending RegisterCallback messages to the MonitorActor
  3. Carry on
  • Optionally, you can un-register all callbacks for a path and event type by sending UnRegisterCallback messages.
  • Both RegisterCallback and UnRegisterCallback messages have a recursive argument (be sure to see point 5 in the Caveats section) that triggers recursive registration and un-registration of callbacks for path.
import com.beachape.filemanagement.MonitorActor
import com.beachape.filemanagement.RegistryTypes._
import com.beachape.filemanagement.Messages._

import{FileWriter, BufferedWriter}

import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds._

implicit val system = ActorSystem("actorSystem")
val fileMonitorActor = system.actorOf(MonitorActor(concurrency = 2))

val modifyCallbackFile: Callback = { path => println(s"Something was modified in a file: $path")}
val modifyCallbackDirectory: Callback = { path => println(s"Something was modified in a directory: $path")}

val desktop = Paths get "/Users/lloyd/Desktop"
val desktopFile = Paths get "/Users/lloyd/Desktop/test"

  This will receive callbacks for just the one file
fileMonitorActor ! RegisterCallback(
  event = ENTRY_MODIFY,
  path = desktopFile,
  callback =  modifyCallbackFile

  If desktopFile is modified, this will also receive a callback
  it will receive callbacks for everything under the desktop directory
fileMonitorActor ! RegisterCallback(
  event = ENTRY_MODIFY,
  path = desktop,
  callback = modifyCallbackDirectory)

//modify a monitored file
val writer = new BufferedWriter(new FileWriter(desktopFile.toFile))
writer.write("Theres text in here wee!!")

// #=> Something was modified in a file: /Users/lloyd/Desktop/test.txt
//     Something was modified in a directory: /Users/lloyd/Desktop/test.txt


There is another way to monitor files supported by this library: via Rx Observables.

This is neat because it allows you to treat notifications for files as, well, an Observable, which conceptually is like a Stream, or a Go channel. You can filter on the stream, map on it, filter on it ... basically compose it as you would a normal observable.

Note that since RxMonitor is powered by an Akka actor under the covers, the changes to what you (do not) want to monitor are done in a thread-safe manner so feel free to pass this object around.

import com.beachape.filemanagement.RxMonitor
import{FileWriter, BufferedWriter}

import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds._

val monitor = RxMonitor()
val observable = monitor.observable

val subscription = observable.subscribe(
  onNext = { p => println(s"Something was modified in a file mufufu: $p")},
  onError = { t => println(t)},
  onCompleted = { () => println("Monitor has been shut down") }

val desktopFile = Paths get "/Users/lloyd/Desktop/test"

monitor.registerPath(ENTRY_MODIFY, desktopFile)


//modify a monitored file
val writer = new BufferedWriter(new FileWriter(desktopFile.toFile))
writer.write("Theres text in here wee!!")

// #=> Something was modified in a file mufufu: /Users/lloyd/Desktop/test

// stop monitoring

// #=> Monitor has been shut down


Above all things understand that library relies on the Java7 WatchService API and has the same constraints, limitations, and behaviours according to the implementation for the version of JVM you're using.

Additional library-specific caveats and notes are:

  1. Callbacks are registered for specific paths, and for directory paths, can be registered as recursive so that a single callback is fired when an event occurs inside the directory tree.
  2. Callbacks are not checked for uniqueness when registered to a specific path.
  3. A specific path can have multiple callbacks registered to a file event types, all will be fired.
  4. Callbacks are not guaranteed to be fired in any particular order unless if concurrency is set to 1 on the MonitorActor.
  5. Any event on a file path will bubble up to its immediate parent folder path. This means that if both a file and it's parent directory are registered for callbacks, both sets of callbacks will be fired.

As a result of note 6, you may want to think twice about registering recursive callbacks for ENTRY_DELETE because if a directory is deleted within a directory, 2 callbacks will be fired, once for the path registered for the deleted directory and once for the path registered for the directory above it (its parent directory).


IDE Sponsor

Jetbrains sponsors this project through their Open Source licence, which has been an immensely helpful tool for all of my Scala development, at home and at work.

IntelliJ IDEA


File-watching library for Scala. Built on Java 7's WatchService, RxScala and Akka actors.







No packages published