Directory watcher let you set a watcher for a directory and listen to create, delete, and modify events, then execute the appropriate command.
This project can be used both as a stand-alone application, and as a Scala or Java library embedded into another project.
Usage as a library give more control over the implementation of listeners, but the stand-alone version is pretty flexible thanks to the use of a Scala DSL for configuration.
There are two options:
-
download the compiled jar to use it as a stand-alone application. it can be downloaded from the dist folder.
-
clone the project and build the stand-alone jar with sbt
git clone https://github.com/mdread/dir-watcher.git
cd dir-watcher
sbt assembly
compiled jar will be in ./target/scala-2.11 directory
include it as a dependency from sbt / maven
sbt
libraryDependencies += "net.caoticode.dirwatcher" %% "dir-watcher" % "0.1.0"
maven
<dependency>
<groupId>net.caoticode.dirwatcher</groupId>
<artifactId>dir-watcher_2.11</artifactId>
<version>0.1.0</version>
</dependency>
First thing is to create a configuration file to instruct dir-watcher which folders to watch for, and the actions to take when an event is fired. Next an example configuration file:
watcher.conf
Watchers(
watch("/home/mdread/Documents/test") listen Events(
create { (root, file) =>
println("created - " + file.path)
},
delete { (root, file) =>
println("deleted - " + file.path)
},
modify { (root, file) =>
println("modified - " + file.path)
}
)
)
Configuration is actually done in Scala itself, with a small DSL for declaring directories to watch, and implement event listeners.
Watchers
takes a list ofwatch
watch
takes a path to the folder to watch for events (it is recursive)Events
takes a list of events, possible options are:create
,delete
andmodify
. Each of those events is a function with two input parameters, root and file, representing the parent directory where the file lives, and the actual file for which the event has been fired.- root and file are both java.nio.file.Path objects, with some shortcut methods added:
isDir
returns true if the path refers to a directory.path
returns the string version of the full path.exists
returns true if the file exists.parent
returns a nio.file.Path object referencing the parent directory./
useful to build a new path from the previous one, for exampleroot / "myfile.jpg"
returns a nio.file.Path object referencing a myfile.jpg file in the root folder.
Also the scala.sys.process
package gets imported automatically in the configuration file, this makes easy to call external processes on events, for more information on how to use it refer to the scaladoc
Run it as any executable file, with the configuration file path as parameter. Ex:
java -jar dir-watcher-assembly-0.1.0.jar watcher.conf
From Scala you can choose to implement FSListener
trait, as shown in the following example:
import net.caoticode.dirwatcher.DirWatcher
import net.caoticode.dirwatcher.FSListener
class LogListener extends FSListener {
import java.nio.file.Path
override def onCreate(ref: Path): Unit = println(s"created $ref")
override def onDelete(ref: Path): Unit = println(s"deleted $ref")
override def onModify(ref: Path): Unit = println(s"modified $ref")
}
object Main extends App {
val directoryPath = "/home/mdread/Documents/test"
val watcher = DirWatcher()
watcher.watchFor(directoryPath, new LogListener())
watcher.start()
// somewhere later ...
DirWatcher.shutdown()
}
or to use the helper methods on Listener
object:
import net.caoticode.dirwatcher.DirWatcher
import net.caoticode.dirwatcher.Listener
object Main extends App {
val directoryPath = "/home/mdread/Documents/test"
val watcher = DirWatcher()
watcher.watchFor(directoryPath,
Listener.create{ file => println(s"created $file") },
Listener.modify{ file => println(s"modified $file") },
Listener.delete{ file => println(s"deleted $file") })
watcher.start()
// somewhere later ...
DirWatcher.shutdown()
}
internally it uses file change notification implemented in java.nio.file package, and akka to asyncronusly manage listeners. If you already use akka in your project, it is possible to pass the actor system object to DirWatcher class, just change the line
val watcher = DirWatcher()
with
val watcher = new DirWatcher(system)
because the watcher system manages a separate thread-pool (with akka) you need to DirWatcher.shutdown()
it when you no longer need it.
Implement FSListener interface (alternatively extend FSListenerAdapter to override only the event you need)
import java.nio.file.Path;
import net.caoticode.dirwatcher.FSListener;
public class LogListener implements FSListener {
@Override
public void onCreate(Path ref) {
System.out.println("created " + ref);
}
@Override
public void onDelete(Path ref) {
System.out.println("deleted " + ref);
}
@Override
public void onModify(Path ref) {
System.out.println("modified " + ref);
}
}
then use it with DirWatcher
class
import net.caoticode.dirwatcher.DirWatcher;
public class JavaTest {
public static void main(String[] args) {
String directoryPath = "/home/mdread/Documents/test";
DirWatcher watcher = DirWatcher.apply();
watcher.watchFor(directoryPath, new LogListener());
watcher.start();
// somewhere later ...
DirWatcher.shutdown();
}
}