gmethvin / directory-watcher

A cross-platform Java recursive directory watcher, with a JNA macOS watcher and Scala better-files integration
Apache License 2.0
265 stars 34 forks source link

Support watching independent files #20

Closed jvican closed 3 years ago

jvican commented 6 years ago

As explained in the docs, JDK's watch service cannot watch independent files and instead can only watch directories. To simulate watching independent files, we watch their parent directories in a non-recursive manner. This PR implements the bits to simulate this behaviour.

The strategy here is to first register all the paths that are non-recursive, and then proceed to the recursive ones. If any of the directories that must be watched non-recursively are subsumed by any of the recursive directories, they automatically become recursive.

All events about directories contained in non-recursive directories are ignored by default.

jvican commented 6 years ago

Alright, CI has finally passed on OSX with the pertinent changes in its watch service. To avoid the default recursive file watching on OSX, I wanted to use FSEventStreamSetExclusionPaths but realized that's not feasible because the limit of excluded paths is set to 8 (!). Reference: https://github.com/facebook/watchman/blob/master/watcher/fsevents.cpp

So, we'll live with the current approach. This makes https://github.com/gmethvin/directory-watcher/issues/22 more important for the case of watching independent files efficiently.

jvican commented 6 years ago

@gmethvin There seems to be an error in the Linux CI related to the sbt-sonatype upgrade:

[warn]  module not found: org.xerial.sbt#sbt-sonatype;2.3
[warn] ==== typesafe-ivy-releases: tried
[warn]   https://repo.typesafe.com/typesafe/ivy-releases/org.xerial.sbt/sbt-sonatype/scala_2.12/sbt_1.0/2.3/ivys/ivy.xml
[warn] ==== sbt-plugin-releases: tried
[warn]   https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/org.xerial.sbt/sbt-sonatype/scala_2.12/sbt_1.0/2.3/ivys/ivy.xml
[warn] ==== local: tried
[warn]   /home/travis/.ivy2/local/org.xerial.sbt/sbt-sonatype/scala_2.12/sbt_1.0/2.3/ivys/ivy.xml
[warn] ==== public: tried
[warn]   https://repo1.maven.org/maven2/org/xerial/sbt/sbt-sonatype_2.12_1.0/2.3/sbt-sonatype-2.3.pom
[warn] ==== local-preloaded-ivy: tried
[warn]   /home/travis/.sbt/preloaded/org.xerial.sbt/sbt-sonatype/2.3/ivys/ivy.xml
[warn] ==== local-preloaded: tried
[warn]   file:////home/travis/.sbt/preloaded/org/xerial/sbt/sbt-sonatype_2.12_1.0/2.3/sbt-sonatype-2.3.pom
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: org.xerial.sbt#sbt-sonatype;2.3: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn] 
[warn]  Note: Some unresolved dependencies have extra attributes.  Check that these dependencies exist with the requested attributes.
[warn]      org.xerial.sbt:sbt-sonatype:2.3 (scalaVersion=2.12, sbtVersion=1.0)
jvican commented 6 years ago

It seems the resolution error disappeared in the last commit. CI is green again here and PR is ready for review.

jvican commented 6 years ago

@gmethvin How does this look?

jvican commented 6 years ago

What do you think about determining whether the watch path is recursive in the OSX service based on whether the FILE_TREE modifier is passed, rather than having an extra isRecursive on the WatchablePath? Seems like that would be more in line with the "standard" implementation and would make it easier to use the service on its own outside the context of directory-watcher.

Do you mean adding isRecursive to the constructor of the OSX watch service? I think it's better as it is because you usually want to control this at the path level. With regards to reusability, watchable path already appears in the public API of the watch service, so it should be reusable.

I'll act on the rest of your feedback soon.

gmethvin commented 6 years ago

Do you mean adding isRecursive to the constructor of the OSX watch service? I think it's better as it is because you usually want to control this at the path level.

No, it should be on a path level, but we can simply check for the ExtendedWatchEventModifier.FILE_TREE modifier, which we are already passing to the Watchable#register method. That requires us to do three things:

  1. Update AbstractWatchService#register to take an additional WatchEvent.Modifier... varargs at the end.
  2. Update WatchablePath#register to pass along the modifiers we give it (which are currently ignored).
  3. Update MacOSXListeningWatchService#register to check for the FILE_TREE modifier, and keep track of which watchables are registered as recursive. This could be as simple as maintaining another Set<Path> of the recursive paths (or separate sets of recursive and non-recursive paths).

I like this approach because it brings our implementation more in line with the default JDK implementation. It also makes it easier to support additional modifiers in the future if we choose to.

jvican commented 6 years ago

Makes sense, I'll have a look later this week.

gmethvin commented 3 years ago

@jvican I'm going to close this for now since it's been inactive for a while and the changes are out of date, but feel free to pick it up again later if you're still interested.

jvican commented 3 years ago

Sounds good, thanks Greg.